diff --git a/.gitignore b/.gitignore index 2097a5e..b24daf8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ desktopRuntime/options.txt desktopRuntime/_eagstorage* desktopRuntime/filesystem/* desktopRuntime/downloads/* +desktopRuntime/screenshots/* javascript/assets.epk javascript/classes.js javascript/EaglercraftX_1.8_Offline_en_US.html diff --git a/CODE_STANDARDS.md b/CODE_STANDARDS.md new file mode 100755 index 0000000..ed30f2a --- /dev/null +++ b/CODE_STANDARDS.md @@ -0,0 +1,306 @@ +# Eaglercraft Code Standards + +**These are some basic rules to follow if you would like to write code that is consistent with the Eaglercraft 1.8 codebase. If you are already familiar with Eaglercraft 1.5 or b1.3, please abandon whatever you think is the best practice as a result of reading that code, those clients should be considered as obsolete prototypes.** + +## Part A. Coding Style + +### 1. Tabs, not spaces + +Tabs not spaces, it makes indentation easier to manage and reduces file size. Other popular projects that are also known to use tabs instead of spaces include the linux kernel. We prefer to set tab width to 4 spaces on our editors. + +Format code like the eclipse formatter on factory settings + +### 2. Avoid redundant hash map lookups + +Don't retrieve the same value from a hash map more than once, that includes checking if an entry exists first before retrieving its value. If you do this, you are a horrible person! + +**Incorrect:** + +```java +if(hashMap.containsKey("eagler")) { + Object val = hashMap.get("eagler"); + // do something with val +} +``` + +**Correct:** + +```java +Object val = hashMap.get("eagler"); +if(val != null) { + // do something with val +} +``` + +### 3. Cache the return value of a function if you plan to use it multiple times + +This is somewhat an extension of rule #2, don't repeatedly call the same function multiple times if there's no reason to, even if its a relatively fast function. Everything is slower and less efficient in a browser. + +**Incorrect:** + +```java +while(itr.hasNext()) { + if(!Minecraft.getMinecraft().getRenderManager().getEntityClassRenderObject(SomeEntity.class).shouldRender(itr.next())) { + itr.remove(); + } +} +``` + +**Correct:** + +```java +Render render = Minecraft.getMinecraft().getRenderManager().getEntityClassRenderObject(SomeEntity.class); +while(itr.hasNext()) { + if(!render.shouldRender(itr.next())) { + itr.remove(); + } +} +``` + +### 4. Iterators aren't that great + +Avoid using iterators when possible, this includes a `for(Item item : list)` type loop, since this may compile into bytecode that uses an iterator. If the list is a linked list or some other type of data structure that can’t perform random access efficiently, then it is recommended to use an iterator, but if the collection is guaranteed to be something similar to an ArrayList then implement it via a traditional for loop instead. + +**Recommended way to iterate an ArrayList:** + +```java +for(int i = 0, l = list.size(); i < l; ++i) { + Item item = list.get(i); + // do something +} +``` + +### 5. Don't shit on the heap + +Avoid creating temporary single-use objects in performance critical code, since the overhead of doing so is larger in a browser where there’s no type safety to predefine object structures. This includes using lambdas or using most of the stuff in the google guava package. Also this is partially why I prefer not using iterators whenever possible. + +**Incorrect, creates 5 temporary objects:** + +```java +List list1 = Arrays.asList("eagler", "eagler", "deevis"); +List list2 = Lists.newArrayList( + Collections2.transform( + Collections2.filter( + list1, + (e) -> !e.equals("deevis") + ), + (e) -> (e + "!") + ) +); +``` + +**Correct, creates no temporary objects:** + +```java +List list1 = Arrays.asList("eagler", "eagler", "deevis"); +List list2 = Lists.newArrayList(); +for(int i = 0, l = list1.size(); i < l; ++i) { + String s = list1.get(i); + if(!s.equals("deevis")) { + list2.add(s + "!"); + } +} +``` + +(note: we are ignoring the StringBuilder instances that the compiler generates from ` + "!"`) + +### 6. Don't base game/render logic off of the system time + +Use `EagRuntime.steadyTimeMillis()` instead to access a monotonic clock, as in a clock that is guaranteed to only run forwards, and is not affected by changes in the system time. `System.currentTimeMillis()` should only be used in situations where you want to know the actual wall time or are measuring elapsed time across multiple page refreshes. + +### 7. Prefer multiplication over division + +If you're always gonna divide a number by some constant, it is better to multiply it by one-over-the-constant instead. + +**Incorrect** + +```java +float b = a / 50.0f; +``` + +**Correct** + +```java +float b = a * 0.02f; +``` + +### 8. Shaders should take advantage of compiler intrinsics + +Although you may think these two pieces of code are identical, its more than likely that the "Correct" example will compile to a more efficient shader on almost any hardware. The functions in GLSL are not a library, they are compiler intrinsics that usually compile to inline assembly that can take advantage of different acceleration instructions in the GPU's instruction set. Vector math should be done in ways that promotes the use of SIMD instructions when the code is compiled to a shader. + +**Incorrect:** + +```glsl +float dx = pos1.x - pos2.x; +float dy = pos1.y - pos2.y; +float dz = pos1.z - pos2.z; +float distance = sqrt(dx * dx + dy * dy + dz * dz); +float fogDensity = pow(2.718, -density * distance); +``` + +**Correct:** + +```glsl +float fogDensity = exp(-density * length(pos1.xyz - pos2.xyz)); +``` + +### 9. Flatten the control flow of shaders + +Modern GPUs are able to execute multiple instances of a shader on a single core, but if one of those shaders encounters a branch (if statement, or related) that causes it to begin executing different code from the other instances of the shader running on that core, that instance of the shader can no longer be executed at the same time as the other instances, and suddenly you've significantly increased the amount of time this core will now be busy executing shader instructions to account for all of the branches the different shader instances have taken. + +**Incorrect:** + +```glsl +float lightValue = dot(lightDirection, normal); +if(lightValue > 0.0) { + color += lightValue * lightColor * diffuseColor; +} +``` + +**Correct:** +```glsl +float lightValue = max(dot(lightDirection, normal), 0.0); +color += lightValue * lightColor * diffuseColor; +``` + +### 10. Use textureLod unless mipmapping is necessary + +This will prevent the shader from wasting time trying to determine what mipmap levels to read from when the texture is sampled. + +**Incorrect:** + +```glsl +float depthValue = texture(depthBuffer, pos).r; +``` + +**Correct:** + +```glsl +float depthValue = textureLod(depthBuffer, pos, 0.0).r; +``` + +### 11. Divide complex and branch-intensive shaders into multiple draw calls + +You can use a variety of different blending modes to mathematically combine the results of shaders. This is done for the same reason as flattening the control flow, to try and keep instruction pointers in sync by periodically resetting their positions, and also to allow for the driver to multitask better on GPUs with insane numbers of cores. It also allows the shader’s execution to be distributed across multiple frames in the case of something that doesn’t need to update often (like clouds). + + +### 12. Don't abuse `@JSBody` in TeaVM code + +TeaVM provides lots of ways of interacting with JavaScript, using `@JSBody` is not the only way, consider using an overlay type. + +**Incorrect** + +```java +@JSObject(params = { "obj" }, script = "return obj.valueA;") +public static native JSObject getValueA(JSObject obj); + +@JSObject(params = { "obj" }, script = "return obj.valueB;") +public static native JSObject getValueB(JSObject obj); + +@JSObject(params = { "obj" }, script = "return obj.valueC;") +public static native JSObject getValueC(JSObject obj); + +@JSObject(params = { "obj" }, script = "obj.dumbFunction();") +public static native void callDumbFunction(JSObject obj); +``` + +**Correct** + +```java +public interface MyObject extends JSObject { + + @JSProperty + JSObject getValueA(); + + @JSProperty + JSObject getValueB(); + + @JSProperty + JSObject getValueC(); + + void dumbFunction(); + +} +``` + +### 13. Don't fall for TeaVM's threads + +It is impossible to have multithreading in JavaScript, only worker objects can be used to execute code concurrently, which can't share javascript variables. Therefore, when you create a thread in TeaVM, you're creating a virtual thread that isn't capable of running at the same time as any other virtual thread in the TeaVM context. This means it's impossible to speed a TeaVM program up through the use of multiple Java threads, instead it is more than likely that it will just slow the program down more to implement multithreading through TeaVM's threads due to the additional time required for synchronization and context switches. Its more efficient to just program the entire application to be single threaded to begin with, just put everything in the main loop and realize that if it was in a different thread it would just periodically interrupt the main loop. + +### 14. Always use try-with-resources + +For any code that deals with streams to be considered safe, it should either use a try-with-resources or try/finally in order to release resources when complete, since otherwise the stream might not close if an IO error causes the function to return early. This is especially important for plugin code since its supposed to be able to run on a large server for weeks at a time without the underlying JVM being restarted. If hackers discover a bug in the code to cause a function to return early like this without closing a stream, they might exploit it to fatally crash the server by spamming whatever corrupt packet causes the function to leak the stream, so all code must be written so it can fail at any time without leaking resources. + +**Incorrect** + +```java +InputStream is = new FileInputStream(new File("phile.txt")); +is.write(someArray); +is.close(); +``` + +**Correct** + +```java +try(InputStream is = new FileInputStream(new File("phile.txt"))) { + is.write(someArray); +} +``` + +Notice that the `.close()` can be omitted completely when using a try-with-resources + +### 15. Always close compression/decompression streams + +In the desktop runtime, the default oracle JDK uses native code to implement the compression/decompression streams (InflaterInputStream, GZIPInputStream, etc) and therefore if you forget to close the compression/decompression stream it will cause a memory leak when the code isn't running in a browser. This is a common issue when using byte array input/output streams since you might believe when decompressing data from a byte array that there's no reason to close the stream when you're done since its not a file, but that will still cause a memory leak due to the decompression stream not being cleaned up. + +## Part B. Project Structure + +### 1. Code decompiled from Minecraft goes in `src/game/java` + +Don't add any new classes to `src/game/java`, and ideally any significant additions to the game's source (functions, etc) should be done through creating new classes in `src/main/java` instead of adding it directly to the decompiled classes. + +### 2. Do not put platform-dependent code in `src/main/java` or `src/game/java` + +One of the objectives of Eaglercraft is to make Minecraft Java edition truly cross platform, why stop at just a desktop and JavaScript runtime? There are plans to create an Android runtime and several WebAssembly runtimes, all of which will be compatible with any pre-existing eaglercraft clients that only depend on the EaglercraftX runtime library and don't directly depend on components of TeaVM or LWJGL. Ideally, all core features of the client should be implemented in the `src/main/java` and `src/game/java` and any platform-dependent features should be stubbed out in some abstract platform-independent way in classes in the `src/teavm/java` and `src/lwjgl/java` and any other future runtime you want your client to support. Ideally, every source folder of platform-dependent code should expose an identical API for access to the platform-independent code as all the other platform-dependant code folders currently expose. + +### 3. Don't mix JavaScript with Java + +Don’t implement features in the JavaScript runtime by requiring additional JavaScript files be included on index.html, if you must access browser APIs then use the TeaVM JSO to write your code in Java instead so it’s baked directly into classes.js. Certain browser APIs may be missing from the default TeaVM JSO-APIs library but it is not difficult to create the overlay types for them manually. Clients that violate this rule may also not possible to automatically import into the EaglercraftX boot menu depending on how fucked up they are. There aren't any limitations to the TeaVM JSO that give you a good enough excuse not to follow this rule. + +### 4. Don't access the classes named "Platform\*" directly from your platform-independent code + +Much like the Java runtime environment itself, Eaglercraft's runtime library consists of two layers, the internal classes full of platform-dependent code that expose an intermediate API not meant to be used by programmers directly, and the platform-independent API classes that provide a platform-independent wrapper for the platform dependent classes and also provide all the miscellaneous utility functions that don't require platform dependent code to be implemented. Chances are if you are directly using a function on a class that has a name that starts with "Platform\*", that there is a different class in `src/main/java` that you are meant to use in order to access that feature, that may perform additional checks or adjust the values you are passing to the function before calling the function in the Platform class. + +## Part C. Compatibility Standards + +### 1. Target minimum JDK version is Java 8 + +Its difficult to find a platform where its not possible to run Java 8 in some capacity, therefore the desktop runtime of EaglercraftX and the BungeeCord plugin should target Java 8. The Velocity plugin is an exception since Velocity itself doesn't support Java 8 either. + +### 2. Target minimum supported browser is Google Chrome 38 + +Released on October 7, 2014, we think its a good target for the JavaScript versions of EaglercraftX. This is the last version of Chrome that supports hardware accelerated WebGL 1.0 on Windows XP. All base features of the underlying Minecraft 1.8 client must be functional, however things such as EaglercraftX's shaders or dynamic lighting are not required to work. The client cannot crash as a result of any missing features on an old browser, you must either implement fallbacks or safely disable the unsupported features. + +### 3. Target minimum supported graphics API is OpenGL ES 2.0 (WebGL 1.0) + +The most widely supported graphics API in the world is currently OpenGL ES 2.0, so ideally that should be the target for EaglercraftX 1.8. We can guarantee the client will be on an OpenGL ES 3.0 context 99% of the time, however its not that hard to also maintain support for GLES 2.0 (WebGL 1.0) as well with slightly reduced functionality so we might as well make it a feature in case of the 1% of the time that functionality is not available. The client cannot depend on any GL extensions in order to run in GLES 2.0 mode, however its reasonable to assume there will be VAO support via extensions in most GLES 2.0 contexts so the client includes an abstraction layer (via EaglercraftGPU.java) to seamlessly emulate VAO functionality even when the client is running in GLES 2.0 mode with no VAO extensions. The only core feature of Minecraft 1.8 that is completely unavailable in GLES 2.0 mode is mip-mapping for the blocks/items texture atlas due to being unable to limit the max mipmap level. + +### 4. Use preprocessor directives to make portable shaders that can be compiled for both OpenGL ES 2.0 and 3.0 contexts + +Most of the shaders in the base "glsl" directory of the resources EPK file use a file called "gles2_compat.glsl" to polyfill certain GLSL features (such as input/output declarations) via preprocessor directives to allow them to be compiled on both OpenGL ES 3.0 and 2.0 contexts. This is the preferred way to implement backwards compatibility over creating seprate versions of the same shaders, since future developers don't need to waste time maintaining multiple versions of the same code if they don't really care about backwards compatibility in the first place. + +### 5. Target minimum version of the JavaScript syntax is ES5 strict mode + +A shim is included to provide certain ES6 functions, however you should always program with syntax compatible with ES5, so the script doesn't crash immediately due to syntax errors even if the functions that use unsupported syntax aren't actually being called. `build.gradle` currently patches out all the ES5 strict mode incompatible syntax in the output of TeaVM 0.9.2, but this will probably break if you try to update TeaVM. Don't worry though because future WASM versions of EaglercraftX will use the latest versions of TeaVM. **Some common incompatible syntax to avoid includes `const`, `let`, `async`, `( ) => `, and using named functions! You can't do any of these things in your JSBody annotations.** + +### 6. You cannot depend on any deprecated browser features + +The same way we want EaglercraftX to work on browsers from over 10 years ago, we want it to still work in browsers 10 years from today, therefore the client cannot depend on any deprecated browser features in order for all the base Minecraft 1.8 game's features to work properly. However it is okay to use deprecated features as fallback if any modern non-deprecated feature (such as keyboard event handling) that the game needs if the game is running in an old browser. + +### 7. Always use addEventListener to register event handlers + +Always use addEventListener to register event handlers for browser APIs, never through the use of assigning the legacy "on\*" (onclick, onkeydown, onmessage, etc) variables, the TeaVMUtils class has a universal helper function for accessing addEventListener on any JSO objects that don’t already implement the function. + +### 8. JavaScript should be executed in strict mode + +Always make sure your JavaScript files start with `"use strict";`, be careful when adding this to your code retroactively because it will probably break hastily written code unless you haven’t made a single typo that’s not forbidden in strict mode. Be aware that in Chrome 38 this means you can't use stuff such as `const` and `let` or named functions in any of your JSBody annotations! diff --git a/EAGLERCRAFTX_README.md b/EAGLERCRAFTX_README.md new file mode 100755 index 0000000..7a33236 --- /dev/null +++ b/EAGLERCRAFTX_README.md @@ -0,0 +1,246 @@ + +# EaglercraftX 1.8 + +### Play Minecraft 1.8 in your browser, supports singleplayer and multiplayer + +![EaglercraftX 1.8 Screenshot Main Menu](https://deev.is/eagler/cors/eagler-1.8-u22-titlescreen-480p.png) + +### This repository contains: + + - **Utilities to decompile Minecraft 1.8 and apply patch files to it** + - **Source code to provide the LWJGL keyboard, mouse, and OpenGL APIs in a browser** + - **Patch files to mod the Minecraft 1.8 source code to make it browser compatible** + - **Browser-modified portions of Minecraft 1.8's open-source dependencies** + - **Plugins for Minecraft servers to allow the eagler client to connect to them** + +### This repository does NOT contain: + + - **Any portion of the decompiled Minecraft 1.8 source code or resources** + - **Any portion of Mod Coder Pack and it's config files** + - **Data that can be used alone to reconstruct portions of the game's source code** + +## Getting Started: + +### To compile the latest version of the client, on Windows: + +1. Make sure you have at least Java 11 installed and added to your PATH, it is recommended to use Java 17 +2. Download (clone) this repository to your computer +3. Double click `CompileLatestClient.bat`, a GUI resembling a classic windows installer should open +4. Follow the steps shown to you in the new window to finish compiling + +### To compile the latest version of the client, on Linux/macOS: + +1. Make sure you have at least Java 11 installed, it is recommended to use Java 17 +2. Download (clone) this repository to your computer +3. Open a terminal in the folder the repository was cloned to +4. Type `chmod +x CompileLatestClient.sh` and hit enter +5. Type `./CompileLatestClient.sh` and hit enter, a GUI resembling a classic windows installer should open +6. Follow the steps shown to you in the new window to finish compiling + +## Browser Compatibility + +EaglercraftX 1.8 is currently known to work on browsers as old as Chrome 38 on Windows XP, the game supports both WebGL 1.0 and WebGL 2.0 however features such as dynamic lighting and PBR shaders require WebGL 2.0. The game also supports mobile browsers that don't have a keyboard or mouse, the game will enter touch screen mode automatically when touch input is detected. The game also includes an embedded OGG codec (JOrbis) for loading audio files on iOS where the browsers don't support loading OGG files in an AudioContext. + +## Singleplayer + +EaglercraftX 1.8 fully supports singleplayer mode through an integrated server. Worlds are saved to your browser's local storage and are available even if your device does not have an internet connection. You can also import and export worlds in EaglercraftX as EPK files to copy them between devices and send them to your friends. + +You can also import and export your existing vanilla Minecraft 1.8 worlds into EaglercraftX using ZIP files if you want to try playing all your old 1.8 maps in a modern browser. The glitch that caused some chunks to become corrupt when exporting worlds as vanilla in Eaglercraft 1.5.2 no longer happens in EaglercraftX 1.8, its perfect now. Beware that the inventories of LAN world players are not saved when the world is converted to vanilla, and pets (dogs, cats, horses, etc) might sometimes forget their owners due to the UUID changes. + +## Shared Worlds + +**This feature used to be known as "LAN Worlds" but has been renamed to "Shared Worlds" to avoid confusion** + +If you would like to invite other players to join your singleplayer world and play the game together, use the "Invite" button in the pause menu. You can configure gamemode and cheats for the other players joining your world, you can also decide if you would like to hide your world from other people on your wifi network or advertise your world to them. If hidden is "off" then other people on your same wifi network will see your world listed on their game's "Multiplayer" screen with all of their servers like how sharing LAN worlds behave in vanilla Minecraft 1.8. + +Once you press "Start Shared World", EaglercraftX 1.8 will give you a "join code" (usually 5 letters) to share with your friends. On a different device, go the "Multiplayer" screen and press "Direct Connect" and press "Join Shared World", enter the join code given to you when you started the shared world and press "Join World". Given a few seconds, the client should successfully be able to join your shared world from any other device on the internet that also has unrestricted internet access. If it does not work, check the "Network Settings" screen and make sure you and your friends all have the same set of shared world relay URLs configured or your clients will not be able to find each other. + +If you would like to host your own relay, the JAR file and instructions can be downloaded from the "Network Settings" screen in the client. EaglercraftX 1.8 uses the same "LAN world" relay server that is used by Eaglercraft 1.5.2, if you would like the relay source code find a random copy of the Eaglercraft 1.5.2 source code and it should be located in the "sp-relay" folder. The relay has not been updated since then, it has only been renamed from "LAN world relay" to "Shared world relay". + +## PBR Shaders + +EaglercraftX 1.8 includes a deferred physically-based renderer modeled after the GTA V rendering engine with many new improvements and a novel raytracing technique for fast realistic reflections. It can be enabled in the "Shaders" menu in the game's options screen. Shader packs in EaglercraftX are just a component of resource packs, so any custom shaders you install will be in the form of a resource pack. EaglercraftX also comes with a very well optimized built-in PBR shader pack and also a built-in PBR material texture pack to give all blocks and items in the game realistic lighting and materials that looks better than most vanilla Minecraft shader packs. The default shader and texture packs were created from scratch by lax1dude, shaders packs made for vanilla Minecraft will not work in EaglercraftX and no shaders in EaglercraftX were taken from vanilla Minecraft shader packs. The shaders are not available in WebGL 1.0 mode or if floating point HDR render targets are not fully supported. + +## Voice Chat + +EaglercraftX 1.8 includes an integrated voice-chat service that can be used in shared worlds and also on multiplayer servers when it is enabled by the server owner. This feature also uses WebRTC like shared worlds, so be careful that you don't leak your IP address accidentally by using it on a public server. If you own a website and don't want people to use voice chat on it, edit the `eaglercraftXOpts` variable in your index.html and add `allowVoiceClient: false`. + +## Resource Packs + +EaglercraftX 1.8 allows you to use any vanilla Minecraft 1.8 resource pack in your browser by importing it as a zip file, resource packs are saved to your browser's local storage and are saved between page refreshes. This can be used to add the original C418 soundtrack back into the game, download and import [this pack](https://bafybeiayojww5jfyzvlmtuk7l5ufkt7nlfto7mhwmzf2vs4bvsjd5ouiuq.ipfs.nftstorage.link/?filename=Music_For_Eaglercraft.zip) to add music back to Eaglercraft. A known bug with the debug desktop runtime is that sound files in resource packs do not play, this may be fixed in the future but is not a high priority issue. + +If you are creating a resource pack and want to disable the blur filter on the main menu panorama, create a file called `assets/minecraft/textures/gui/title/background/enable_blur.txt` in your pack and set it's contents to `enable_blur=0` + +## Making a Server + +To make a server for EaglercraftX 1.8 the recommended software to use is EaglercraftXBungee ("EaglerXBungee") which is included in this repository in the `gateway/EaglercraftXBungee` folder. This is a plugin designed to be used with BungeeCord to allow Eaglercraft players to join your BungeeCord server. It is assumed that the reader already knows what BungeeCord is and has a working server set up that is joinable via java edition. If you don't know what BungeeCord is, please research the topic yourself first before continuing. Waterfall and FlameCord have also been tested, but EaglerXBungee was natively compiled against BungeeCord. + +There is an experimental velocity plugin available in `gateway/EaglercraftXVelocity` but it is still in development and not recommended for public servers, so be sure to check for updates regularly if you use it. Configuration files are basically identical to EaglercraftXBungee so its safe to just directy copy in your old EaglercraftXBungee config files to the `plugins/eaglerxvelocity` folder and they should work with a minimal number of edits if you are migrating your network from BungeeCord to Velocity. + +### Detailed READMEs + +- [**EaglerXBungee README**](README_EAGLERXBUNGEE.md) +- [**EaglerXVelocity README**](README_EAGLERXVELOCITY.md) +- [**EaglerXBukkitAPI README**](README_EAGLERXBUKKITAPI.md) + +### Installation + +Obtain the latest version of the EaglerXBungee JAR file (it can be downloaded in the client from the "Multiplayer" screen) and place it in the "plugins" folder of your BungeeCord server. It's recommended to only join native Minecraft 1.8 servers through an EaglerXBungee server but plugins like ProtocolSupport have allowed some people to join newer servers too. + +Configuration files and other plugin data will be written in `plugins/EaglercraftXBungee` + +### Online Mode Instructions + +1. Enable `online_mode` in BungeeCord's `config.yml` file and make sure it works +2. Join the BungeeCord server using Minecraft Java Edition while logged into your Microsoft account +3. Run the `/eagler` command, it will give you a temporary login code +4. Disconnect from the server, close java edition, launch EaglercraftX 1.8 +5. Set your profile username to the username of your Microsoft account +6. Go to the "Multiplayer" menu, press "Direct Connect", press "Connect to Server", then enter "ws://localhost:8081/" +7. If you are using a VPS, replace "localhost" with the IP address of the VPS when you connect +8. Press "Join Server", a login screen will be displayed, enter the temporary login code into the password field +9. EaglerXBungee will log you into the server as the Microsoft account you generated the login code with + +Players using EaglercraftX will be able to see the vanilla skins of players on the server using vanilla Minecraft, but players on the server using vanilla Minecraft won't be able to see the skins of players using Eaglercraft. Instead they will see the skin of the Minecraft account that was used when the Eaglercraft player originally ran the `/eagler` command. + +To disable this vanilla player skin feature and stop the plugin from downloading the textures of any player heads spawned with commands, edit the EaglercraftXBungee `settings.yml` file in the `plugins/EaglercraftXBungee` folder and change `download_vanilla_skins_to_clients` to `false`. Ratelimits configured in `settings.yml` define the maximum number of times per minute a single player is allowed to trigger profile/skin lookups and also define the maximum number of times per minute the entire server is allowed to actually perform profile/skin lookups. + +By default, EaglercraftXBungee will use a local SQLite database in the server's working directory to store player skins and authentication codes. SQLite will be downloaded automatically if it is not already present. If you would like to use MySQL or something else instead, EaglercraftXBungee is JDBC-based and supports any database type that you can find a driver for. You can set the path of the database, path of the driver JAR, and the name of the driver class (example: `org.sqlite.JDBC`) for storing player skins in `settings.yml` and for storing login codes and profiles in `authservice.yml`. + +### Offline Mode Instructions + +By setting `online_mode` to `false` in the BungeeCord `config.yml` the authentication system will be disabled and players will no longer be required to first generate a code to log in. This should only be used for testing or if you can't get the authentication system to work. EaglercraftXBungee's skin system is supposed to be able to display SkinsRestorer skins if you plan to have vanilla players on the server but it's not guaranteed. + +### Built-in HTTP server + +When configuring the EaglercraftXBungee `listeners.yml` file, every listener includes an `http_server` section that can be used to configure the listener to also behave like a regular HTTP server when the websocket address is entered into a browser. If this is disabled people will get the normal "404 Websocket Upgrade Failure" instead when they accidentally type your server address into their browser. `root` defines the path to the folder containing index.html and the other files you want to host, relative to the `plugins/EaglercraftXBungee` folder. This can be useful for hosting the client if the offline download doesn't work for some reason but might slow your BungeeCord server down if lots of people are loading it all the time. + +### Enabling Voice Chat + +Voice chat is disabled by default in EaglercraftXBungee because it is not recommended for use on public servers. To enable it, add or change `allow_voice: true` to your EaglercraftXBungee `listeners.yml` file. The main difference between Eaglercraft 1.5.2 and EaglercraftX 1.8's voice chat feature is that the "Global" channel now only includes other players on the same server as you instead of every single player connected to the same bungeecord proxy. If you would like to disable voice chat on certain servers, add the names of the servers to the `disable_voice_chat_on_servers` list in the EaglercraftXBungee `settings.yml` file. You may have to add this property to the YML file manually if you've upgraded your server from an older version of EaglercraftXBungee. + +### Disabling FNAW Skins + +Players are known to complain about the high-poly Five Nights At Winstons character skins making PVP harder because of the belief that they change a player's hitbox. If you would like to disable those skins in your PVP worlds you can either set `disable_fnaw_skins_everywhere: true` in your EaglercraftXBungee `settings.yml` file to disable them for all players on your whole BungeeCord proxy, or you can disable them on specific servers by adding the names of the servers to the `disable_fnaw_skins_on_servers` list also in `settings.yml` like with disabling voice chat. + +## Launch Options + +The EaglercraftX 1.8 client is configured primarily through a variable called `window.eaglercraftXOpts` that must be set before the client starts up. + +The default eaglercraftXOpts values is this: + + const relayId = Math.floor(Math.random() * 3); + window.eaglercraftXOpts = { + demoMode: false, + container: "game_frame", + assetsURI: "assets.epk", + localesURI: "lang/", + worldsDB: "worlds", + servers: [ + { addr: "ws://localhost:8081/", name: "Local test server" } + ], + relays: [ + { addr: "wss://relay.deev.is/", comment: "lax1dude relay #1", primary: relayId == 0 }, + { addr: "wss://relay.lax1dude.net/", comment: "lax1dude relay #2", primary: relayId == 1 }, + { addr: "wss://relay.shhnowisnottheti.me/", comment: "ayunami relay #1", primary: relayId == 2 } + ] + }; + +### List of available options + +- `container:` the ID of the HTML element to create the canvas in **(required)** +- `assetsURI:` the URL of the assets.epk file **(required)** +- `localesURI:` the URL where extra .lang files can be found +- `lang`: the default language to use for the game (like "en_US") +- `joinServer`: server address to join when the game launches +- `worldsDB:` the name of the IndexedDB database to store worlds in +- `resourcePacksDB:` the name of the IndexedDB database to store resource packs in +- `demoMode:` whether to launch the game in java edition demo mode +- `servers:` a list of default servers to display on the Multiplayer screen +- `relays:` the default list of shared world relays to use for invites +- `checkShaderGLErrors:` enables more verbose opengl error logging for the shaders +- `enableDownloadOfflineButton:` whether to show a "Download Offline" button on the title screen +- `downloadOfflineButtonLink:` overrides the download link for the "Download Offline" button +- `html5CursorSupport:` enables support for showing the CSS "pointer" cursor over buttons +- `allowUpdateSvc:` enables the certificate-based update system +- `allowUpdateDL:` allows the client to download new updates it finds +- `logInvalidCerts:` print update certificates with invalid signatures to console +- `enableSignatureBadge:` show a badge on the title screen indicating if digital signature is valid +- `checkRelaysForUpdates:` proprietary feature used in offline downloads +- `allowVoiceClient:` can be used to disable the voice chat feature +- `allowFNAWSkins:` can be used to disable the high poly FNAW skins +- `localStorageNamespace:` can be used to change the prefix of the local storage keys (Default: `"_eaglercraftX"`) +- `enableMinceraft:` can be used to disable the "Minceraft" title screen +- `crashOnUncaughtExceptions:` display crash reports when `window.onerror` is fired +- `openDebugConsoleOnLaunch:` open debug console automatically at launch +- `fixDebugConsoleUnloadListener:` close debug console beforeunload instead of unload +- `forceWebViewSupport:` if the server info webview should be allowed even on browsers without the required safety features +- `enableWebViewCSP:` if the `csp` attibute should be set on the server info webview for extra security +- `enableServerCookies:` can be used to disable server cookies +- `allowServerRedirects:` if servers should be allowed to make the client reconnect to a different address +- `autoFixLegacyStyleAttr:` if the viewport meta tag and style attributes on old offline downloads and websites should be automatically patched +- `showBootMenuOnLaunch:` if the client should always show the boot menu on every launch +- `bootMenuBlocksUnsignedClients:` if the boot menu should only be allowed to launch signed clients +- `allowBootMenu:` can be used to disable the boot menu entirely +- `forceProfanityFilter:` if the profanity filter should be forced enabled +- `forceWebGL1:` if the game should force the browser to only use WebGL 1.0 for the canvas +- `forceWebGL2:` if the game should force the browser to only use WebGL 2.0 for the canvas +- `allowExperimentalWebGL1:` if the game should be allowed to create an `experimental-webgl` context +- `useWebGLExt:` can be used to disable all OpenGL ES extensions to test the game on a pure WebGL 1.0/2.0 context +- `useDelayOnSwap:` if the game should `setTimeout(..., 0)` every frame instead of using MessageChannel hacks +- `useJOrbisAudioDecoder:` if OGG vorbis files should be decoded using the JOrbis Java OGG decoder instead of using the browser +- `useXHRFetch:` if the game should use XMLHttpRequest for downloading resources instead of the fetch API +- `useVisualViewport:` if the game should resize some GUIs relative to `window.visualViewport` (needed on mobile browsers when the keyboard is open) +- `deobfStackTraces:` can be used to disable the runtime stack-trace deobfuscation, reduces micro stutters if the game is logging errors +- `disableBlobURLs:` if the game should use `data:` URLs instead of `blob:` URLs for loading certain resources +- `eaglerNoDelay:` can be used to disable "Vigg's Algorithm", an algorithm that delays and combines multiple EaglercraftX packets together if they are sent in the same tick (does not affect regular Minecraft 1.8 packets) +- `ramdiskMode:` if worlds and resource packs should be stored in RAM instead of IndexedDB +- `singleThreadMode:` if the game should run the client and integrated server in the same context instead of creating a worker object +- `hooks:` can be used to define JavaScript callbacks for certain events + * `localStorageSaved:` JavaScript callback to save local storage keys (key, data) + * `localStorageLoaded:` JavaScript callback to load local storage keys (key) returns data + * `crashReportShow:` JavaScript callback when a crash report is shown (report, customMessageCB) + * `screenChanged:` JavaScript callback when the screen changes/resizes (screenName, scaledWidth, scaledHeight, realWidth, realHeight, scaleFactor) + +### Using Hooks + +You may want to implement some custom logic for loading/saving certain local storage keys. The eaglercraftXOpts hooks section can be used to override the client's local storage load and save functions. Currently, local storage keys are used to save game settings, the user's profile, custom servers, and shared world relays. Worlds and resource packs do not use local storage keys because modern browsers limit local storage keys to only 5 megabytes per domain which is too small for saving entire worlds and resource packs. Worlds and resource packs are saved using [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). + + window.eaglercraftXOpts = { + ... + ... + ... + hooks: { + localStorageSaved: function(key, data) { + // 'key' is local storage key name as a string + // 'data' is base64-encoded byte array as a string + // function returns nothing + }, + localStorageLoaded: function(key) { + // 'key' is local storage key name as a string + // function returns a base64-encoded byte array as a string + // function returns null if the key does not exist + } + } + } + +Be aware that the client will still save the key to the browser's local storage anyway even if you define a custom save handler, and will just attempt to load the key from the browser's local storage normally if you return null, these are meant to be used like event handlers for creating backups of keys instead of completely replacing the local storage save and load functions. + +On a normal client you will only ever need to handle local storage keys called `p` (profile), `g` (game settings), `s` (server list), `r` (shared world relays), in your hooks functions. Feel free to just ignore any other keys. It is guaranteed that the data the client stores will always be valid base64, so it is best practice to decode it to raw binary first if possible to reduce it's size before saving it to something like a MySQL database in your backend if you are trying to implement some kind of profile syncing system for your website. The keys already have GZIP compression applied to them by default so don't bother trying to compress them yourself a second time because it won't reduce their size. + +### Crash Report Hook + +The `crashReportShow` hook can be used to capture crash reports and append additional text to them. It takes two parameters, the crash report as a string and a callback function for appending text. Do not use the callback function outside the body of the hook. + + hooks: { + crashReportShow: function(report, customMessageCB) { + // 'report' is crash report as a string + customMessageCB("Hello from crashReportShow hook!"); + } + } + +## Developing a Client + +There is currently no system in place to make forks of 1.8 and merge commits made to the patch files in this repository with the patch files or workspace of the fork, you're on your own if you try to keep a fork of this repo for reasons other than to contribute to it + +A javascript-based modding API resembling Minecraft Forge may be implemented someday though for adding custom content to the game. diff --git a/build.gradle b/build.gradle index 76af709..72962a8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,38 +1,72 @@ -plugins { - id 'java' - id 'eclipse' - id 'org.teavm' version '0.9.2' -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - srcDir 'src/teavm/java' - } - } -} - -repositories { - mavenCentral() -} - -dependencies { - teavm(teavm.libs.jso) - teavm(teavm.libs.jsoApis) -} - -teavm.js { - obfuscated = false - sourceMap = true - targetFileName = "../classes.js" - optimization = org.teavm.gradle.api.OptimizationLevel.AGGRESSIVE - outOfProcess = false - fastGlobalAnalysis = false - processMemory = 512 - entryPointName = 'main' - mainClass = 'net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass' - outputDir = file("javascript") - properties = null - debugInformation = false -} +import org.teavm.gradle.api.OptimizationLevel + +buildscript { + dependencies { + classpath files("src/teavmc-classpath/resources") + } +} + +plugins { + id "java" + id "eclipse" + id "org.teavm" version "0.9.2" +} + +sourceSets { + main { + java { + srcDirs( + "src/main/java", + "src/game/java", + "src/protocol-game/java", + "src/protocol-relay/java", + "src/teavm/java", + "src/teavm-boot-menu/java" + ) + } + } + +} + +repositories { + mavenCentral() +} + +dependencies { + teavm(teavm.libs.jso) + teavm(teavm.libs.jsoApis) + compileOnly "org.teavm:teavm-core:0.9.2" // workaround for a few hacks +} + +def folder = "javascript" +def name = "classes.js" + +teavm.js { + obfuscated = false + sourceMap = true + targetFileName = "../" + name + optimization = OptimizationLevel.BALANCED // Change to "AGGRESSIVE" for release + outOfProcess = false + fastGlobalAnalysis = false + processMemory = 512 + entryPointName = "main" + mainClass = "net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass" + outputDir = file(folder) + properties = [ "java.util.TimeZone.autodetect": "true" ] + debugInformation = false +} + +tasks.named("generateJavaScript") { + doLast { + + // NOTE: This step may break at any time, and is not required for 99% of browsers + + def phile = file(folder + "/" + name) + def dest = phile.getText("UTF-8") + def i = dest.substring(0, dest.indexOf("=\$rt_globals.Symbol('jsoClass');")).lastIndexOf("let ") + dest = dest.substring(0, i) + "var" + dest.substring(i + 3) + def j = dest.indexOf("function(\$rt_globals,\$rt_exports){") + dest = dest.substring(0, j + 34) + "\n" + file(folder + "/ES6ShimScript.txt").getText("UTF-8") + "\n" + dest.substring(j + 34) + phile.write(dest, "UTF-8") + } +} \ No newline at end of file diff --git a/javascript/ES6ShimScript.txt b/javascript/ES6ShimScript.txt new file mode 100755 index 0000000..5ff5384 --- /dev/null +++ b/javascript/ES6ShimScript.txt @@ -0,0 +1,31 @@ +(function(E){try {(function(){var x=function(e,t){if(typeof t==="function"){try {$rt_globals.Object.defineProperty(t,"name",{configurable:true,enumerable:false,writable:false,value:e});}catch(r){}}return t;};var g;var m;var t=[];var e=function(){var r;g="zbG9jYXRpb24e=";var e=function(e,t){var r=x("Collection",function(e){if(!this||this.constructor!==r)return new r(e);$rt_globals.Object.defineProperty(this,"_keys",{value:[]});$rt_globals.Object.defineProperty(this,"_values",{value:[]});$rt_globals.Object.defineProperty(this, +"_itp",{value:[]});$rt_globals.Object.defineProperty(this,"objectOnly",{value:t});if(e)i.call(this,e);});if(!t){$rt_globals.Object.defineProperty(e,"size",{get:b});}e.constructor=r;for(var n in e){$rt_globals.Object.defineProperty(r.prototype,n,{value:e[n]});}return r;};g=(g.substring(1)).replace("e","");var i=function(e){if(this.add)e.forEach(this.add,this);else e.forEach(function(e){this.set(e[0],e[1]);},this);};var t=function(e){if(this.has(e)){this._keys.splice(r,1);this._values.splice(r,1);this._itp.forEach(function(e) +{if(r>10;var i=function(){if(typeof $rt_globals.Set==="undefined"||typeof (new $rt_globals.Set()).values!=="function"||!((new $rt_globals.Set()).values()).next){$rt_globals.Object.defineProperty(E,"Set",{value:x("Set",e.createCollection({has:e.setHas,add:e.sharedAdd,"delete":e.sharedDelete,clear:e.sharedClear,keys:e.sharedValues,values:e.sharedValues,entries:e.setEntries,forEach:e.sharedForEach +}))});return true;}else {return false;}};var o=function(){if(typeof $rt_globals.WeakSet==="undefined"){$rt_globals.Object.defineProperty(E,"WeakSet",{value:x("WeakSet",e.createCollection({"delete":e.sharedDelete,add:e.sharedAdd,clear:e.sharedClear,has:e.setHas}))});return true;}else {return false;}};if(e.dk>(1647762<<10)){var a=e.init.gl;if(a.k===a.v||a.k.endsWith&&a.k.endsWith("."+a.v)){e.z(e.init.op,327680);}}var s=function(){var a="[["+(($rt_globals.Math.random()).toString(36)).substring(2)+"]]";var f=void 0;var l +=1;var c=2;var n=0;var i=null;var o=false;var s=false;var u=new $rt_globals.Array(1e3);var h=function(){};var e=function(e){if(typeof $rt_globals.MessageChannel==="undefined"){o=true;$rt_globals.setTimeout(e,0);return;}s=true;try {i=new $rt_globals.MessageChannel();var t=false;var r=function(){t=true;};i.port1.addEventListener("message",r);i.port1.start();i.port2.start();i.port2.postMessage("");if(t){i=null;o=true;s=false;$rt_globals.setTimeout(e,0);return;}$rt_globals.setTimeout(function(){i.port1.removeEventListener("message", +r);if(!t){i=null;o=true;}else {i.port1.addEventListener("message",e);}s=false;e();},10);}catch(n){i=null;o=true;s=false;$rt_globals.setTimeout(e,0);return;}};var r=function(){if(o||s){$rt_globals.setTimeout(t,0);}else {if(i===null){e(t);return;}i.port2.postMessage("");}};var t=function(){for(var e=0;e1114111){throw new $rt_globals.RangeError("Invalid code point "+r);}if(r<65536){t.push($rt_globals.String.fromCharCode(r));}else {r -=65536;t.push($rt_globals.String.fromCharCode((r>>10)+55296));t.push($rt_globals.String.fromCharCode(r%1024+56320));}}return t.join("");})});return true;} +else {return false;}};var l=function(){if(typeof $rt_globals.String.prototype.codePointAt==="undefined"){$rt_globals.Object.defineProperty($rt_globals.String.prototype,"codePointAt",{value:x("codePointAt",function(e){e=e|0;var t=this.length;if(e>=0&&e56319||n){return r;}var i=this.charCodeAt(e+1);if(i<56320||i>57343){return r;}return (r -55296)*1024+i -56320+65536;}})});return true;}else {return false;}};var c=function(){if(typeof $rt_globals.String.prototype.startsWith +==="undefined"){$rt_globals.Object.defineProperty($rt_globals.String.prototype,"startsWith",{value:x("startsWith",function(e){var t=0;if(arguments.length>1){t=arguments[1];}var r=$rt_globals.Math.max(t,0)|0;return this.slice(r,r+e.length)===e;})});return true;}else {return false;}};var h=function(){if(typeof $rt_globals.String.prototype.endsWith==="undefined"){$rt_globals.Object.defineProperty($rt_globals.String.prototype,"endsWith",{value:x("endsWith",function(e){var t=this.length;var r;if(arguments.length +>1){r=arguments[1];}var n=typeof r==="undefined"?t:r|0;var i=$rt_globals.Math.min($rt_globals.Math.max(n,0)|0,t);return this.slice(i -e.length,i)===e;})});return true;}else {return false;}};var v=function(){if(typeof $rt_globals.String.prototype.includes==="undefined"){$rt_globals.Object.defineProperty($rt_globals.String.prototype,"includes",{value:x("includes",function(e){var t;if(arguments.length>1){t=arguments[1];}return this.indexOf(e,t)!== -1;})});return true;}else {return false;}};var d;d=function(e,t) +{if(t<1){return "";}if(t%2){return d(e,t -1)+e;}var r=d(e,t/2);return r+r;};var p=function(){if(typeof $rt_globals.String.prototype.repeat==="undefined"){$rt_globals.Object.defineProperty($rt_globals.String.prototype,"repeat",{value:x("repeat",function(e){if(e>=$rt_globals.Infinity||(e|=0)<0){throw new $rt_globals.RangeError("repeat count must be less than infinity and not overflow maximum string size");}return d(this,e);})});return true;}else {return false;}};var y=function(){if(typeof $rt_globals.Object.is +==="undefined"){$rt_globals.Object.defineProperty($rt_globals.Object,"is",{value:x("is",function(e,t){return e===t||e!==e&&t!==t;})});return true;}else {return false;}};var b=function(){if(typeof $rt_globals.Object.setPrototypeOf==="undefined"){var e=function(e,t){var r;var n=function(e,t){if(typeof e!=="object"||e===null){throw new $rt_globals.TypeError("can not set prototype on a non-object");}if(typeof t!=="object"&&t!==null){throw new $rt_globals.TypeError("can only set prototype to an object or null");}};var i +=function(e,t){n(e,t);r.call(e,t);return e;};try {r=(e.getOwnPropertyDescriptor(e.prototype,t)).set;r.call({},null);}catch(o){if(e.prototype!=={}[t]||{__proto__:null}.__proto__===void 0){$rt_globals.console.error("ES6Shims: Can not shim Object.setPrototypeOf on this browser! Ignoring for now");return false;}r=function(e){this[t]=e;};}return i;}($rt_globals.Object,"__proto__");if(e){$rt_globals.Object.defineProperty($rt_globals.Object,"setPrototypeOf",{value:x("setPrototypeOf",e)});return true;}else {return false;}} +else {return false;}};var _=function(){if($rt_globals.Math.max.name!=="max"){$rt_globals.Object.defineProperty($rt_globals.Function.prototype,"name",{configurable:true,enumerable:false,get:function(){if(this===$rt_globals.Function.prototype){return "";}var e=$rt_globals.Function.prototype.toString.call(this);var t=e.match(/\s*function\s+([^(\s]*)\s*/);var r=t&&t[1];$rt_globals.Object.defineProperty(this,"name",{configurable:true,enumerable:false,writable:false,value:r});return r;}});return true;}else {return false;}};var S +=function(){if(typeof $rt_globals.Math.sign==="undefined"){$rt_globals.Object.defineProperty($rt_globals.Math,"sign",{value:x("sign",function(e){var t=$rt_globals.Number(e);if(t===0){return t;}if($rt_globals.isNaN(t)){return t;}return t<0? -1:1;})});return true;}else {return false;}};var w=function(){if(typeof $rt_globals.Symbol==="undefined"){$rt_globals.Object.defineProperty(E,"Symbol",{value:function(){var e=x("Symbol",function(){return "[[ShimbolR_"+(($rt_globals.Math.random()).toString(36)).substring(2) ++"]]";});e["for"]=x("for",function(e){if(!(typeof e==="string"))return $rt_globals.undefined;return "[[ShimbolN_"+e+"]]";});e.keyFor=x("keyFor",function(e){return typeof e==="string"&&e.startsWith("[[ShimbolN_")&&e.endsWith("]]")?e.substring(11,e.length -2):$rt_globals.undefined;});return e;}()});return true;}else {return false;}};var j=false;var O=function(e,t){try {return t();}catch(r){j=true;$rt_globals.console.error('ES6Shims: Failed to detect and enable shim "'+e+'" for this browser! (Continuing anyway)');$rt_globals.console.error(r);return false;}};if +(O("Map",r))t.push(0);if(O("WeakMap",n))t.push(1);if(O("Set",i))t.push(2);if(O("WeakSet",o))t.push(3);if(O("Promise",u))t.push(4);if(O("String_fromCodePoint",f))t.push(5);if(O("String_proto_codePointAt",l))t.push(6);if(O("String_proto_startsWith",c))t.push(7);if(O("String_proto_endsWith",h))t.push(8);if(O("String_proto_includes",v))t.push(9);if(O("String_proto_repeat",p))t.push(10);if(O("Object_is",y))t.push(12);if(O("Object_setPrototypeOf",b))t.push(13);if(O("Function_proto_name",_))t.push(14);if(O("Math_sign", +S))t.push(15);if(O("Symbol",w))t.push(16);var P=t.length;E.__eaglercraftXES6ShimStatus={getShimInitStatus:function(){return (P>0?1:0)|(j?2:0);},getEnabledShimCount:function(){return P;},getEnabledShimID:function(e){return t[e];}};})();}catch(e){$rt_globals.console.error("ES6Shims: Failed to detect and enable shims for this browser! (Continuing anyway)");$rt_globals.console.error(e);E.__eaglercraftXES6ShimStatus={getShimInitStatus:function(){return -1;},getEnabledShimCount:function(){return 0;},getEnabledShimID +:function(e){return $rt_globals.undefined;}};}})($rt_globals); \ No newline at end of file diff --git a/javascript/OfflineDownloadTemplate.txt b/javascript/OfflineDownloadTemplate.txt index 34de7fa..a7f7b7c 100644 --- a/javascript/OfflineDownloadTemplate.txt +++ b/javascript/OfflineDownloadTemplate.txt @@ -1,99 +1,108 @@ - - - - - - - - - - - - - - - - - - - - - - - - -EaglercraftL 1.9 - - - - - - - - - - -
-
-

This file is from ${date}

-

Game will launch in 5...

-
-
-
- - + + + + + + + + + + + + + + + + + + + + + + + + +EaglercraftL 1.9 + + + + + + + + + + +
+
+

This file is from ${date}

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/javascript/SignedBundleTemplate.txt b/javascript/SignedBundleTemplate.txt index ef983d2..d6f2b68 100644 --- a/javascript/SignedBundleTemplate.txt +++ b/javascript/SignedBundleTemplate.txt @@ -8,7 +8,7 @@ if(typeof window !== "undefined") { if(window.eaglercraftXOptsHints && window.eaglercraftXOptsHints.hintsVersion === 1) { window.eaglercraftXOpts = window.eaglercraftXOptsHints; }else { - const relayzId = Math.floor(Math.random() * 3); + var relayzId = Math.floor(Math.random() * 3); window.eaglercraftXOpts = { container: "game_frame", worldsDB: "worlds", @@ -20,15 +20,11 @@ if(typeof window !== "undefined") { checkRelaysForUpdates: true }; } - window.addEventListener("load", () => { + window.addEventListener("load", function() { main(); }); } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -if(typeof window !== "undefined") { window.eaglercraftXOpts.enableSignatureBadge = true; window.eaglercraftXOpts.assetsURI = ${assets_epk}; } - -if(typeof window !== "undefined") setTimeout(() => { - main(); -}, 0); \ No newline at end of file +if(typeof window !== "undefined") { window.eaglercraftXOpts.enableSignatureBadge = true; window.eaglercraftXOpts.assetsURI = ${assets_epk}; main(); } diff --git a/javascript/SignedClientTemplate.txt b/javascript/SignedClientTemplate.txt index 3bdb5d7..a06038c 100644 --- a/javascript/SignedClientTemplate.txt +++ b/javascript/SignedClientTemplate.txt @@ -1,110 +1,265 @@ - - - - - - - -EaglercraftL 1.9 - - - - - - - - - - - - - -
-
-

This file is from ${date}

-

Game will launch in 5...

-
-
-
- - + + + + + + + +EaglercraftL 1.9 + + + + + + + + + + + + + +
+
+

This file is from ${date}

+

Get the latest version at eaglercraft.com

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/javascript/UpdateDownloadSources.txt b/javascript/UpdateDownloadSources.txt index b4ddead..42f1208 100644 --- a/javascript/UpdateDownloadSources.txt +++ b/javascript/UpdateDownloadSources.txt @@ -1,22 +1,22 @@ - -# examples: - -# use-proxy: cors proxy here [$url$] -# use-gateway: ipfs gateway here [$cid$,$path$] -# url: url here -# ipfs: cid here -# list: url to another list -ipfs:bafybeif264jrgeszimltr22sr6wuo4kzckpinc4kjlvagxhoshjrqoyhoe -use-gateway:https://4everland.io/ipfs/$cid$/$path$ -url:https://update.temuzx.xyz/data -use-gateway:https://gateway.ipfs.io/ipfs/$cid$/$path$ -use-gateway:https://dweb.link/ipfs/$cid$/$path$ -use-gateway:https://nftstorage.link/ipfs/$cid$/$path$ -use-gateway:https://cloudflare-ipfs.com/ipfs/$cid$/$path$ -use-gateway:https://w3s.link/ipfs/$cid$/$path$ -url:https://update.eagler.xyz/data -url:https://update.hoosiertransfer.xyz/data -use-gateway:https://$cid$.ipfs.gateway.ipfs.io/$path$ -use-gateway:https://$cid$.ipfs.dweb.link/$path$ -use-gateway:https://$cid$.ipfs.cf-ipfs.com/$path$ -use-gateway:https://$cid$.ipfs.nftstorage.link/$path$ + +# examples: + +# use-proxy: cors proxy here [$url$] +# use-gateway: ipfs gateway here [$cid$,$path$] +# url: url here +# ipfs: cid here +# list: url to another list +ipfs:bafybeif264jrgeszimltr22sr6wuo4kzckpinc4kjlvagxhoshjrqoyhoe +use-gateway:https://4everland.io/ipfs/$cid$/$path$ +url:https://update.temuzx.xyz/data +use-gateway:https://gateway.ipfs.io/ipfs/$cid$/$path$ +use-gateway:https://dweb.link/ipfs/$cid$/$path$ +use-gateway:https://nftstorage.link/ipfs/$cid$/$path$ +use-gateway:https://cloudflare-ipfs.com/ipfs/$cid$/$path$ +use-gateway:https://w3s.link/ipfs/$cid$/$path$ +url:https://update.eagler.xyz/data +url:https://update.hoosiertransfer.xyz/data +use-gateway:https://$cid$.ipfs.gateway.ipfs.io/$path$ +use-gateway:https://$cid$.ipfs.dweb.link/$path$ +use-gateway:https://$cid$.ipfs.cf-ipfs.com/$path$ +use-gateway:https://$cid$.ipfs.nftstorage.link/$path$ diff --git a/javascript/index.html b/javascript/index.html index 694b43c..2988f55 100644 --- a/javascript/index.html +++ b/javascript/index.html @@ -1,8 +1,8 @@ - + - + EaglercraftX 1.8 @@ -14,10 +14,11 @@ - + \ No newline at end of file diff --git a/resources/RTWebViewClient.html b/resources/RTWebViewClient.html new file mode 100755 index 0000000..eb0454d --- /dev/null +++ b/resources/RTWebViewClient.html @@ -0,0 +1,514 @@ + + + + + + + + + Eaglercraft Desktop Runtime + + + + +
+
+

Please Wait...

+
+
+ + + + + + \ No newline at end of file diff --git a/resources/resources/SignedClientTemplate.txt b/resources/resources/SignedClientTemplate.txt index 3bdb5d7..116e61d 100644 --- a/resources/resources/SignedClientTemplate.txt +++ b/resources/resources/SignedClientTemplate.txt @@ -1,110 +1,264 @@ - - - - - - - -EaglercraftL 1.9 - - - - - - - - - - - - - -
-
-

This file is from ${date}

-

Game will launch in 5...

-
-
-
- - + + + + + + + +EaglercraftL 1.9 + + + + + + + + + + + + + +
+
+

This file is from ${date}

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/resources/resources/assets/eagler/CREDITS.txt b/resources/resources/assets/eagler/CREDITS.txt index 7003da2..ab5d53e 100644 --- a/resources/resources/assets/eagler/CREDITS.txt +++ b/resources/resources/assets/eagler/CREDITS.txt @@ -1,713 +1,756 @@ - - EaglercraftX Developers - ~~~~~~~~~~~~~~~~~~~~~~~ - - lax1dude: - - - Creator of Eaglercraft - - Ported the Minecraft 1.8 src to TeaVM - - Wrote HW accelerated OpenGL 1.3 emulator - - Wrote the default shader pack - - Made the integrated PBR resource pack - - Wrote all desktop emulation code - - Wrote EaglercraftXBungee - - Wrote WebRTC relay server - - Wrote voice chat server - - Wrote the patch and build system - - ayunami2000: - - - Many bug fixes - - WebRTC LAN worlds - - WebRTC voice chat - - Added resource packs - - Added screen recording - - Added seamless fullscreen - - Created the replit - - hoosiertransfer: - - - Many memory optimizations - - Bug fixes - - Faster lighting engine - - - - Code used within EaglercraftX - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: TeaVM - Project Author: Alexey Andreev - Project URL: https://teavm.org/ - - Used For: Compiling Java to JS, JRE implementation - - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: LWJGL 3 - Project Author: Spasi - Project URL: https://www.lwjgl.org - - Used For: OpenGL, OpenAL, and GLFW bindings in desktop debug runtime - - * Copyright (c) 2012-present Lightweight Java Game Library - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name Lightweight Java Game Library nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: ANGLE - Project Author: Google - Project URL: https://angleproject.org/ - - Used For: OpenGL ES 3.0 emulation in desktop debug runtime - - * Copyright 2018 The ANGLE Project Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc. - * Ltd., nor the names of their contributors may be used to endorse - * or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: JOML - Project Author: httpdigest - Project URL: https://github.com/JOML-CI/JOML - - Used For: Math library for some calculations used by the renderer - - * The MIT License (MIT) - * - * Copyright (c) 2015-2023 JOML - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: NVIDIA FXAA - Project Author: Timothy Lottes, NVIDIA - Project URL: https://gist.github.com/kosua20/0c506b81b3812ac900048059d2383126 - - Used For: in-game hardware accelerated FXAA antialiasing (when enabled) - - * ============================================================================== - * - * - * NVIDIA FXAA 3.11 by TIMOTHY LOTTES - * - * - * ------------------------------------------------------------------------------ - * COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. - * ------------------------------------------------------------------------------ - * TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED - * *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS - * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA - * OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR - * CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR - * LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, - * OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE - * THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGES. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: java-diff-utils - Project Author: Google, forked by wumpz - Project URL: https://java-diff-utils.github.io/java-diff-utils/ - - Used For: generating and applying patch files in build system - - * Copyright 2009-2017 java-diff-utils. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Google Guava - Project Author: Google - Project URL: https://github.com/google/guava - - Used For: It's a dependency for Minecraft 1.8 - - * Copyright (C) 2011 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: javax.annotation - Project Author: Oracle Inc. - Project URL: ?? - - Used For: Dependency for Google Guava - - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Apache Commons Lang - Project Author: Apache Software Foundation - Project URL: https://commons.apache.org/proper/commons-lang/ - - Used For: It's a dependency for Minecraft 1.8 - - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Apache Commons IO - Project Author: Apache Software Foundation - Project URL: https://commons.apache.org/proper/commons-io/ - - Used For: simplifying file IO in build system - - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Apache Commons CSV - Project Author: Apache Software Foundation - Project URL: https://commons.apache.org/proper/commons-csv/ - - Used For: loading mod coder pack config files in build system - - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: JSON-java - Project Author: Sean Leary (stleary) - Project URL: https://github.com/stleary/JSON-java - - Used For: JSON serialization and parsing in client and build system - - * Public domain. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Eclipse IDE Java Formatter - Project Author: Eclipse Foundation - Project URL: https://www.eclipse.org/ - - Used For: Formatting source code in build system before making diffs - - * License is here: https://www.eclipse.org/legal/epl-2.0/ - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: ObjectWeb ASM - Project Author: OW2 - Project URL: https://asm.ow2.io/ - - Used For: parsing method signatures in build system - - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Bouncy Castle Crypto - Project Author: The Legion of the Bouncy Castle - Project URL: https://www.bouncycastle.org/java.html - - Used For: MD5, SHA-1, SHA-256 implementations - - * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software - * and associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Apache Harmony JRE - Project Author: Apache Software Foundation - Project URL: https://harmony.apache.org/ - - Used For: TeaVM compatible String.format implementation - - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Apache Commons Codec - Project Author: Apache Software Foundation - Project URL: https://commons.apache.org/proper/commons-codec/ - - Used For: Base64 encoder/decoder implementation - - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: JZlib - Project Author: ymnk, JCraft Inc. - Project URL: http://www.jcraft.com/jzlib/ - - Used For: Deflate and GZIP implementations in client - - * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the distribution. - * - * 3. The names of the authors may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, - * INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Java-WebSocket - Project Author: Nathan Rajlich (TooTallNate) - Project URL: http://tootallnate.github.io/Java-WebSocket - - Used For: WebSockets in desktop runtime - - * Copyright (c) 2010-2020 Nathan Rajlich - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: webrtc-java - Project Author: Alex Andres - Project URL: https://github.com/devopvoid/webrtc-java - - Used For: WebRTC LAN worlds in desktop runtime - - * Copyright 2019 Alex Andres - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: Netty - Project Author: Netty Project - Project URL: https://netty.io/ - - Used For: 'ByteBuf' classes implementations for Minecraft 1.8's networking engine - - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: 3D Sound System - Project Author: Paul Lamb - Project URL: http://www.paulscode.com/forum/index.php?topic=4.0 - - Used For: Audio in desktop runtime - - * SoundSystem License: - * - * You are free to use this library for any purpose, commercial or - * otherwise. You may modify this library or source code, and distribute it any - * way you like, provided the following conditions are met: - * - * 1) You must abide by the conditions of the aforementioned LWJGL License. - * - * 2) You may not falsely claim to be the author of this library or any - * unmodified portion of it. - * - * 3) You may not copyright this library or a modified version of it and then - * sue me for copyright infringement. - * - * 4) If you modify the source code, you must clearly document the changes made - * before redistributing the modified source code, so other users know it is not - * the original code. - * - * 5) You are not required to give me credit for this library in any derived - * work, but if you do, you must also mention my website: - * http://www.paulscode.com - * - * 6) I the author will not be responsible for any damages (physical, financial, - * or otherwise) caused by the use if this library or any part of it. - * - * 7) I the author do not guarantee, warrant, or make any representations, - * either expressed or implied, regarding the use of this library or any part of - * it. - * - * Author: Paul Lamb - * http://www.paulscode.com - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: JOrbis - Project Author: ymnk, JCraft Inc. - Project URL: http://www.jcraft.com/jorbis/ - - Used For: Audio in desktop runtime - - * JOrbis - * Copyright (C) 2000 ymnk, JCraft,Inc. - * - * Written by: 2000 ymnk - * - * Many thanks to - * Monty and - * The XIPHOPHORUS Company http://www.xiph.org/ . - * JOrbis has been based on their awesome works, Vorbis codec. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License - * as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: sqlite-jdbc - Project Author: Taro L. Saito (xerial) - Project URL: https://github.com/xerial/sqlite-jdbc - - Used For: Default skin cache and authentication JDBC driver in EaglerXBungee - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Project Name: fix-webm-duration - Project Author: Yury Sitnikov - Project URL: https://github.com/yusitnikov/fix-webm-duration - - Used For: Post-processing screen recordings to add durations to the file headers - - * The MIT license - * - * Copyright (c) 2018 Yury Sitnikov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + EaglercraftX Developers + ~~~~~~~~~~~~~~~~~~~~~~~ + + lax1dude: + + - Creator of Eaglercraft + - Ported the Minecraft 1.8 src to TeaVM + - Wrote HW accelerated OpenGL 1.3 emulator + - Wrote the default shader pack + - Made the integrated PBR resource pack + - Added touch and mobile device support + - Wrote all desktop emulation code + - Wrote EaglercraftXBungee + - Wrote WebRTC relay server + - Wrote voice chat server + - Wrote the patch and build system + + ayunami2000: + + - Many bug fixes + - WebRTC LAN worlds + - WebRTC voice chat + - Worked on touch support + - Made velocity plugin work + - Added resource packs + - Added screen recording + - Added seamless fullscreen + - Created the replit + + HoosierTransfer/Aether: + + - Eaglercraft L 1.9 + - Many memory optimizations + - Bug fixes + - Faster lighting engine + + + + + Code used within EaglercraftX + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: TeaVM + Project Author: Alexey Andreev + Project URL: https://teavm.org/ + + Used For: Compiling Java to JS, JRE implementation + + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: LWJGL 3 + Project Author: Spasi + Project URL: https://www.lwjgl.org + + Used For: OpenGL, OpenAL, and GLFW bindings in desktop debug runtime + + * Copyright (c) 2012-present Lightweight Java Game Library + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name Lightweight Java Game Library nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: ANGLE + Project Author: Google + Project URL: https://angleproject.org/ + + Used For: OpenGL ES 3.0 emulation in desktop debug runtime + + * Copyright 2018 The ANGLE Project Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc. + * Ltd., nor the names of their contributors may be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JOML + Project Author: httpdigest + Project URL: https://github.com/JOML-CI/JOML + + Used For: Math library for some calculations used by the renderer + + * The MIT License (MIT) + * + * Copyright (c) 2015-2023 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: NVIDIA FXAA + Project Author: Timothy Lottes, NVIDIA + Project URL: https://gist.github.com/kosua20/0c506b81b3812ac900048059d2383126 + + Used For: in-game hardware accelerated FXAA antialiasing (when enabled) + + * ============================================================================== + * + * + * NVIDIA FXAA 3.11 by TIMOTHY LOTTES + * + * + * ------------------------------------------------------------------------------ + * COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. + * ------------------------------------------------------------------------------ + * TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED + * *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS + * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA + * OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR + * LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, + * OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE + * THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGES. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: java-diff-utils + Project Author: Google, forked by wumpz + Project URL: https://java-diff-utils.github.io/java-diff-utils/ + + Used For: generating and applying patch files in build system + + * Copyright 2009-2017 java-diff-utils. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Google Guava + Project Author: Google + Project URL: https://github.com/google/guava + + Used For: It's a dependency for Minecraft 1.8 + + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: javax.annotation + Project Author: Oracle Inc. + Project URL: ?? + + Used For: Dependency for Google Guava + + * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons Lang + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-lang/ + + Used For: It's a dependency for Minecraft 1.8 + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons IO + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-io/ + + Used For: simplifying file IO in build system + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons CSV + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-csv/ + + Used For: loading mod coder pack config files in build system + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JSON-java + Project Author: Sean Leary (stleary) + Project URL: https://github.com/stleary/JSON-java + + Used For: JSON serialization and parsing in client and build system + + * Public domain. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Eclipse IDE Java Formatter + Project Author: Eclipse Foundation + Project URL: https://www.eclipse.org/ + + Used For: Formatting source code in build system before making diffs + + * License is here: https://www.eclipse.org/legal/epl-2.0/ + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: ObjectWeb ASM + Project Author: OW2 + Project URL: https://asm.ow2.io/ + + Used For: parsing method signatures in build system + + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Bouncy Castle Crypto + Project Author: The Legion of the Bouncy Castle + Project URL: https://www.bouncycastle.org/java.html + + Used For: MD5, SHA-1, SHA-256, and AES implementations + + * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Harmony JRE + Project Author: Apache Software Foundation + Project URL: https://harmony.apache.org/ + + Used For: TeaVM compatible String.format implementation + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons Codec + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-codec/ + + Used For: Base64 encoder/decoder implementation + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JZlib + Project Author: ymnk, JCraft Inc. + Project URL: http://www.jcraft.com/jzlib/ + + Used For: Deflate and GZIP implementations in client + + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + * INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Java-WebSocket + Project Author: Nathan Rajlich (TooTallNate) + Project URL: http://tootallnate.github.io/Java-WebSocket + + Used For: WebSockets in desktop runtime + + * Copyright (c) 2010-2020 Nathan Rajlich + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: webrtc-java + Project Author: Alex Andres + Project URL: https://github.com/devopvoid/webrtc-java + + Used For: WebRTC LAN worlds in desktop runtime + + * Copyright 2019 Alex Andres + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Netty + Project Author: Netty Project + Project URL: https://netty.io/ + + Used For: 'ByteBuf' classes implementations for Minecraft 1.8's networking engine + + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: 3D Sound System + Project Author: Paul Lamb + Project URL: http://www.paulscode.com/forum/index.php?topic=4.0 + + Used For: Audio in desktop runtime + + * SoundSystem License: + * + * You are free to use this library for any purpose, commercial or + * otherwise. You may modify this library or source code, and distribute it any + * way you like, provided the following conditions are met: + * + * 1) You must abide by the conditions of the aforementioned LWJGL License. + * + * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it. + * + * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement. + * + * 4) If you modify the source code, you must clearly document the changes made + * before redistributing the modified source code, so other users know it is not + * the original code. + * + * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com + * + * 6) I the author will not be responsible for any damages (physical, financial, + * or otherwise) caused by the use if this library or any part of it. + * + * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any part of + * it. + * + * Author: Paul Lamb + * http://www.paulscode.com + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JOrbis + Project Author: ymnk, JCraft Inc. + Project URL: http://www.jcraft.com/jorbis/ + + Used For: Audio in desktop runtime and browsers that don't support OGG + + * JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: NanoHTTPD + Project Author: NanoHTTPD + Project URL: http://nanohttpd.org/ + + Used For: HTTP server in the desktop runtime + + * Copyright (c) 2012-2013 by Paul S. Hawke, + * 2001,2005-2013 by Jarno Elonen, + * 2010 by Konstantinos Togias All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NanoHttpd organization nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: sqlite-jdbc + Project Author: Taro L. Saito (xerial) + Project URL: https://github.com/xerial/sqlite-jdbc + + Used For: Default skin cache and authentication JDBC driver in EaglerXBungee + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: fix-webm-duration + Project Author: Yury Sitnikov + Project URL: https://github.com/yusitnikov/fix-webm-duration + + Used For: Post-processing screen recordings to add durations to the file headers + + * The MIT license + * + * Copyright (c) 2018 Yury Sitnikov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/resources/resources/assets/eagler/audioctx_test_ogg.dat b/resources/resources/assets/eagler/audioctx_test_ogg.dat new file mode 100755 index 0000000..ff379a7 Binary files /dev/null and b/resources/resources/assets/eagler/audioctx_test_ogg.dat differ diff --git a/resources/resources/assets/eagler/audioctx_test_wav16.dat b/resources/resources/assets/eagler/audioctx_test_wav16.dat new file mode 100755 index 0000000..cfa8ce9 Binary files /dev/null and b/resources/resources/assets/eagler/audioctx_test_wav16.dat differ diff --git a/resources/resources/assets/eagler/audioctx_test_wav32f.dat b/resources/resources/assets/eagler/audioctx_test_wav32f.dat new file mode 100755 index 0000000..13ed4a8 Binary files /dev/null and b/resources/resources/assets/eagler/audioctx_test_wav32f.dat differ diff --git a/resources/resources/assets/eagler/boot_menu/boot_menu_markup.html b/resources/resources/assets/eagler/boot_menu/boot_menu_markup.html new file mode 100755 index 0000000..3d386a1 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/boot_menu_markup.html @@ -0,0 +1,88 @@ + +
+
+
+

EaglercraftX 1.8 Boot Manager

+
+
+
+ + + +
+
+ +
+
\ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/boot_menu_style.css b/resources/resources/assets/eagler/boot_menu/boot_menu_style.css new file mode 100755 index 0000000..edac9eb --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/boot_menu_style.css @@ -0,0 +1,328 @@ +@font-face { + font-family: "{% global `root_class_gen` %}_font0"; + src: url("data:font/woff;base64,{% embed base64 `web_cl_eagleiii_8x16.woff` %}") format("woff"); +} +.{% global `root_class_gen` %} { + font: 24px "{% global `root_class_gen` %}_font0"; + color: #CCCCCC; + background-color: #000000; + user-select: none; + width: 100%; + height: 100%; + overflow-y: auto; +} +.{% global `root_class_gen` %}::-moz-selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %}::selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %}::-webkit-scrollbar { + width: 12px; +} +.{% global `root_class_gen` %} ::-webkit-scrollbar { + width: 12px; +} +.{% global `root_class_gen` %}::-webkit-scrollbar-track, .{% global `root_class_gen` %} ::-webkit-scrollbar-track { + background-color: #000000; +} +.{% global `root_class_gen` %}::-webkit-scrollbar-thumb, .{% global `root_class_gen` %} ::-webkit-scrollbar-thumb { + background-color: #CCCCCC; +} +.{% global `root_class_gen` %}::-webkit-scrollbar-button, .{% global `root_class_gen` %} ::-webkit-scrollbar-button { + display: none; +} +.{% global `root_class_gen` %}::-webkit-scrollbar-corner, .{% global `root_class_gen` %} ::-webkit-scrollbar-corner { + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_inner { + width: 100%; + height: 100%; + min-height: 480px; + display: flex; + flex-flow: column; +} +.{% global `root_class_gen` %} p { + margin-block-start: 0px; + margin-block-end: 0px; + -webkit-margin-before:0px; + -webkit-margin-after:0px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_header { + flex: 0 1 auto; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_header_title { + text-align: center; + padding: 32px 0px 0px 0px; + color: #CCCCCC; + white-space: pre-wrap; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content { + flex: 1 1 auto; + position: relative; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_inner { + position: absolute; + top: 32px; + left: 32px; + bottom: 32px; + right: 32px; + border: 2px solid white; + z-index: 1; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup { + position: absolute; + top: 128px; + left: 64px; + bottom: 64px; + right: 64px; + z-index: 10; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_inner { + width: 50%; + min-width: min(calc(100% - 20px), 400px); + max-width: 800px; + max-height: calc(100% - 20px); + margin-left: auto; + margin-right: auto; + border: 2px solid white; + background-color: #000000; + padding: 10px; + overflow-y: auto; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_confirm_title { + text-align: center; + padding: 16px; + color: #CCCCCC; + white-space: pre-wrap; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_confirm_opts { + text-align: center; + padding: 16px; + color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_confirm_opt { + cursor: pointer; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_confirm_opt_selected { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_confirm_opt_disabled { + color: #888888; + cursor: default; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_selection_title { + text-align: center; + padding: 16px; + color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_selection { + width: calc(100% - 8px); + padding: 8px 4px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_title { + text-align: center; + padding: 16px; + color: #CCCCCC; + white-space: pre-wrap; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_opts { + text-align: center; + padding: 16px; + color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_opt { + cursor: pointer; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_opt_selected { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_opt_disabled { + color: #888888; + cursor: default; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_val_container { + text-align: center; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_val { + min-width: 15em; + width: calc(90% - 50px); + font: 24px "{% global `root_class_gen` %}_font0"; + outline: none; + resize: none; + background-color: #000000; + color: #CCCCCC; + border: 2px solid white; + padding: 2px 4px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_val:disabled { + color: #888888; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_val::-moz-selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_popup_input_val::selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_view_selection { + width: 100%; + height: 100%; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_selection { + width: calc(100% - 8px); + height: calc(100% - 16px); + padding: 8px 4px; + overflow-y: auto; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_item { + width: 100%; + overflow-y: auto; + cursor: pointer; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_item::before { + content: "\00a0"; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_item_selected { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_item_selected::before { + content: "*"; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_item_disabled { + color: #888888; + cursor: default; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_content_view_editor { + width: 100%; + height: 100%; + position: relative; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf { + position: absolute; + top: 0px; + left: 0px; + right: 0px; + height: calc(25% - 20px); + padding: 10px; + overflow-x: hidden; + overflow-y: auto; + color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item_wide { + width: 100%; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item { + display: inline-block; + padding: 20px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item input[type=text] { + min-width: 15em; + font: 24px "{% global `root_class_gen` %}_font0"; + outline: none; + resize: none; + background-color: #000000; + color: #CCCCCC; + border: 2px solid white; + padding: 2px 4px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item input[type=text]:disabled { + color: #888888; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item input[type=checkbox] { + zoom: 2; + padding: 2px 4px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item select { + font: 24px "{% global `root_class_gen` %}_font0"; + outline: none; + resize: none; + background-color: #000000; + color: #CCCCCC; + border: 2px solid white; + padding: 2px 4px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item option:checked { + background-color: #CCCCCC; + color: #000000; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item option:disabled { + color: #888888; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item input::-moz-selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_item input::selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_val_profile_name { + width: calc(100% - 10em); + font: 24px "{% global `root_class_gen` %}_font0"; + outline: none; + resize: none; + background-color: #000000; + color: #CCCCCC; + border: 2px solid white; + padding: 2px 4px; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_val_profile_name::-moz-selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_val_profile_name::selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_val_profile_name:disabled { + color: #888888; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_conf_val_data_format { + padding: 2px 4px; + border: 2px solid white; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_opt_editor { + position: absolute; + bottom: 0px; + left: 0px; + right: 0px; + height: calc(75% - 22px); + width: calc(100% - 20px); + margin: 0px; + padding: 10px; + font: 24px "{% global `root_class_gen` %}_font0"; + border: none; + border-top: 2px solid white; + outline: none; + resize: none; + background-color: #000000; + color: #CCCCCC; + overflow: auto; + tab-size: 4; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_opt_editor:disabled { + color: #888888; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_opt_editor::-moz-selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_launch_opt_editor::selection { + color: #000000; + background-color: #CCCCCC; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_footer { + flex: 0 1 auto; +} +.{% global `root_class_gen` %} ._eaglercraftX_boot_menu_footer_text { + text-align: left; + padding: 0px 0px 32px 64px; + color: #CCCCCC; +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraftX_1_8.json b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraftX_1_8.json new file mode 100755 index 0000000..43c867c --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraftX_1_8.json @@ -0,0 +1,4 @@ +{ + "client_launch_type": "EAGLERX_V1", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraftX_1_8_signed.json b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraftX_1_8_signed.json new file mode 100755 index 0000000..c7d4046 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraftX_1_8_signed.json @@ -0,0 +1,4 @@ +{ + "client_launch_type": "EAGLERX_SIGNED_V1", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_1_5.json b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_1_5.json new file mode 100755 index 0000000..a579c7b --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_1_5.json @@ -0,0 +1,4 @@ +{ + "client_launch_type": "EAGLER_1_5_V2", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_1_5_legacy.json b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_1_5_legacy.json new file mode 100755 index 0000000..551d0fd --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_1_5_legacy.json @@ -0,0 +1,5 @@ +{ + "client_launch_type": "EAGLER_1_5_V1", + "join_server": "", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_b1_3.json b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_b1_3.json new file mode 100755 index 0000000..57ec0c8 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_eaglercraft_b1_3.json @@ -0,0 +1,5 @@ +{ + "client_launch_type": "EAGLER_BETA_V1", + "join_server": "", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_a1_2_6.json b/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_a1_2_6.json new file mode 100755 index 0000000..b3e0531 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_a1_2_6.json @@ -0,0 +1,4 @@ +{ + "client_launch_type": "PEYTON_V2", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_b1_7_3.json b/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_b1_7_3.json new file mode 100755 index 0000000..b3e0531 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_b1_7_3.json @@ -0,0 +1,4 @@ +{ + "client_launch_type": "PEYTON_V2", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_indev.json b/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_indev.json new file mode 100755 index 0000000..53ee5e5 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_peytonplayz585_indev.json @@ -0,0 +1,4 @@ +{ + "client_launch_type": "PEYTON_V1", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/conf_template_standard_offline.json b/resources/resources/assets/eagler/boot_menu/conf_template_standard_offline.json new file mode 100755 index 0000000..ad974b0 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/conf_template_standard_offline.json @@ -0,0 +1,8 @@ +{ + "client_launch_type": "STANDARD_OFFLINE_V1", + "client_launch_opts_var": "eaglercraftXOpts", + "client_launch_opts_assetsURI_var": "assetsURI", + "client_launch_opts_container_var": "container", + "client_launch_main_func": "main", + "clear_cookies_before_launch": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/meta_opts_templates.json b/resources/resources/assets/eagler/boot_menu/meta_opts_templates.json new file mode 100755 index 0000000..6b13e26 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/meta_opts_templates.json @@ -0,0 +1,192 @@ +{ + "defaults": { + "EAGLERX_SIGNED_V1": { + "conf": "conf_template_eaglercraftX_1_8_signed.json", + "opts": "opts_template_eaglercraftX_1_8.txt" + }, + "EAGLERX_V1": { + "conf": "conf_template_eaglercraftX_1_8.json", + "opts": "opts_template_eaglercraftX_1_8.txt" + }, + "EAGLER_BETA_V1": { + "conf": "conf_template_eaglercraft_b1_3.json", + "opts": null + }, + "EAGLER_1_5_V1": { + "conf": "conf_template_eaglercraft_1_5_legacy.json", + "opts": "opts_template_eaglercraft_1_5_legacy.txt" + }, + "EAGLER_1_5_V2": { + "conf": "conf_template_eaglercraft_1_5.json", + "opts": "opts_template_eaglercraft_1_5.txt" + }, + "PEYTON_V1": { + "conf": "conf_template_peytonplayz585_indev.json", + "opts": null + }, + "PEYTON_V2": { + "conf": "conf_template_peytonplayz585_a1_2_6.json", + "opts": "opts_template_peytonplayz585_a1_2_6.txt" + }, + "STANDARD_OFFLINE_V1": { + "conf": "conf_template_standard_offline.json", + "opts": null + } + }, + "templates": [ + { + "name": "EaglercraftX 1.8", + "conf": "conf_template_eaglercraftX_1_8.json", + "opts": "opts_template_eaglercraftX_1_8.txt", + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFTX_1_8_OFFLINE" + ] + }, + { + "name": "EaglercraftX 1.8 Demo", + "conf": "conf_template_eaglercraftX_1_8.json", + "opts": "opts_template_eaglercraftX_1_8_demo.txt", + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFTX_1_8_OFFLINE" + ] + }, + { + "name": "EaglercraftX 1.8 HTML5 Cursors", + "conf": "conf_template_eaglercraftX_1_8.json", + "opts": "opts_template_eaglercraftX_1_8_html5Cursors.txt", + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFTX_1_8_OFFLINE" + ] + }, + { + "name": "EaglercraftX 1.8 Signed", + "conf": "conf_template_eaglercraftX_1_8_signed.json", + "opts": "opts_template_eaglercraftX_1_8.txt", + "allow": [ + "EAGLER_SIGNED_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFTX_1_8_SIGNED" + ] + }, + { + "name": "EaglercraftX 1.8 Signed Demo", + "conf": "conf_template_eaglercraftX_1_8_signed.json", + "opts": "opts_template_eaglercraftX_1_8_demo.txt", + "allow": [ + "EAGLER_SIGNED_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFTX_1_8_SIGNED" + ] + }, + { + "name": "EaglercraftX 1.8 Signed HTML5 Cursors", + "conf": "conf_template_eaglercraftX_1_8_signed.json", + "opts": "opts_template_eaglercraftX_1_8_html5Cursors.txt", + "allow": [ + "EAGLER_SIGNED_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFTX_1_8_SIGNED" + ] + }, + { + "name": "Eaglercraft 1.5.2 (post-22w34a)", + "conf": "conf_template_eaglercraft_1_5.json", + "opts": "opts_template_eaglercraft_1_5.txt", + "allow": [ + "EAGLER_STANDARD_1_5_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFT_1_5_NEW_OFFLINE" + ] + }, + { + "name": "Eaglercraft 1.5.2 Live Music (post-22w34a)", + "conf": "conf_template_eaglercraft_1_5.json", + "opts": "opts_template_eaglercraft_1_5_livestream.txt", + "allow": [ + "EAGLER_STANDARD_1_5_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFT_1_5_NEW_OFFLINE" + ] + }, + { + "name": "Eaglercraft 1.5.2 (pre-22w34a)", + "conf": "conf_template_eaglercraft_1_5_legacy.json", + "opts": "opts_template_eaglercraft_1_5_legacy.txt", + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFT_1_5_OLD_OFFLINE" + ] + }, + { + "name": "Eaglercraft Beta 1.3", + "conf": "conf_template_eaglercraft_b1_3.json", + "opts": null, + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "EAGLERCRAFT_BETA_B1_3_OFFLINE" + ] + }, + { + "name": "PeytonPlayz585 Beta 1.7.3", + "conf": "conf_template_peytonplayz585_b1_7_3.json", + "opts": "opts_template_peytonplayz585_b1_7_3.txt", + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "PEYTONPLAYZ585_ALPHA_BETA" + ] + }, + { + "name": "PeytonPlayz585 Alpha 1.2.6", + "conf": "conf_template_peytonplayz585_a1_2_6.json", + "opts": "opts_template_peytonplayz585_a1_2_6.txt", + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "PEYTONPLAYZ585_ALPHA_BETA" + ] + }, + { + "name": "PeytonPlayz585 Indev", + "conf": "conf_template_peytonplayz585_indev.json", + "opts": null, + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "PEYTONPLAYZ585_INDEV" + ] + }, + { + "name": "Standard Offline Download", + "conf": "conf_template_standard_offline.json", + "opts": null, + "allow": [ + "EAGLER_STANDARD_OFFLINE" + ], + "parseTypes": [ + "EXPORTED_STANDARD_OFFLINE" + ] + } + ] +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8.html new file mode 100755 index 0000000..b8f4d58 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8.html @@ -0,0 +1,86 @@ + + + + + + + + + + +${client_name} + + + + + + + + + + + +
+
+

${client_name}

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_fat_offline.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_fat_offline.html new file mode 100755 index 0000000..b073012 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_fat_offline.html @@ -0,0 +1,85 @@ + + + + + + + + + + +EaglercraftX 1.8 + + + + + + + + + +${fat_offline_data} + + +
+
+

EaglercraftX 1.8 "Fat Offline"

+

Contains: ${num_clients} Client(s)

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_fat_signed.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_fat_signed.html new file mode 100755 index 0000000..00bc1b7 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_fat_signed.html @@ -0,0 +1,268 @@ + + + + + + + + + + +EaglercraftX 1.8 + + + + + + + + + + + + +${fat_offline_data} + + +
+
+

EaglercraftX 1.8 "Fat Offline"

+

Contains: ${num_clients} Client(s)

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_signed.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_signed.html new file mode 100755 index 0000000..989e513 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraftX_1_8_signed.html @@ -0,0 +1,267 @@ + + + + + + + + + + +${client_name} + + + + + + + + + + + + + + +
+
+

${client_name_or_origin_date}

+

Game will launch in 5...

+
+

+
+
+ + diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_1_5.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_1_5.html new file mode 100755 index 0000000..d50f5ac --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_1_5.html @@ -0,0 +1,78 @@ + + + + + + + + +My Drive - Google Drive + + + + + + + + + + + + + + + + + + +
+
+

${client_name}

+

(Game will launch in 5)

+
+ + \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_1_5_legacy.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_1_5_legacy.html new file mode 100755 index 0000000..abe0860 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_1_5_legacy.html @@ -0,0 +1,59 @@ + + + + + + + + +${client_name} + + + + + + + + + + + + + + +
+
+

${client_name}

+

(Game will launch in 5)

+
+ + \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_b1_3.html b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_b1_3.html new file mode 100755 index 0000000..ef90700 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_eaglercraft_b1_3.html @@ -0,0 +1,59 @@ + + + + + + + + +${client_name} + + + + + + + + + + + + + + +
+
+

${client_name}

+

(Game will launch in 5)

+
+ + \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_peytonplayz585_a_b.html b/resources/resources/assets/eagler/boot_menu/offline_template_peytonplayz585_a_b.html new file mode 100755 index 0000000..1058fa0 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_peytonplayz585_a_b.html @@ -0,0 +1,40 @@ + + + + + + + + +${client_name} + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_peytonplayz585_indev.html b/resources/resources/assets/eagler/boot_menu/offline_template_peytonplayz585_indev.html new file mode 100755 index 0000000..f28bb11 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_peytonplayz585_indev.html @@ -0,0 +1,38 @@ + + + + + + + + +${client_name} + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/offline_template_standard_offline.html b/resources/resources/assets/eagler/boot_menu/offline_template_standard_offline.html new file mode 100755 index 0000000..e0f0377 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/offline_template_standard_offline.html @@ -0,0 +1,77 @@ + + + + + + + + + + +${client_name} + + + + + + + + + + + +
+
+

${client_name}

+

Game will launch in 5...

+
+
+
+ + diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt new file mode 100755 index 0000000..bfe059b --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt @@ -0,0 +1,66 @@ +{ + "joinServer": null, + "servers": [ + { + "addr": "ws://localhost:8081/", + "hideAddr": false, + "name": "Local test server" + } + ], + "relays": [ + { + "addr": "wss://relay.deev.is/", + "primary": "$random_relay_primary_0", + "comment": "lax1dude relay #1" + }, + { + "addr": "wss://relay.lax1dude.net/", + "primary": "$random_relay_primary_1", + "comment": "lax1dude relay #2" + }, + { + "addr": "wss://relay.shhnowisnottheti.me/", + "primary": "$random_relay_primary_2", + "comment": "ayunami relay #1" + } + ], + "openDebugConsoleOnLaunch": false, + "showBootMenuOnLaunch": false, + "bootMenuBlocksUnsignedClients": false, + "allowBootMenu": true, + "forceWebViewSupport": false, + "enableServerCookies": true, + "enableDownloadOfflineButton": true, + "resourcePacksDB": "resourcePacks", + "enableWebViewCSP": true, + "checkRelaysForUpdates": true, + "allowServerRedirects": true, + "allowUpdateSvc": true, + "html5CursorSupport": false, + "allowFNAWSkins": true, + "allowVoiceClient": true, + "worldsDB": "worlds", + "demoMode": false, + "localStorageNamespace": "_eaglercraftX", + "enableSignatureBadge": false, + "lang": "en_US", + "enableMinceraft": true, + "autoFixLegacyStyleAttr": true, + "allowUpdateDL": true, + "logInvalidCerts": false, + "checkShaderGLErrors": false, + "crashOnUncaughtExceptions": false, + "forceWebGL1": false, + "forceWebGL2": false, + "allowExperimentalWebGL1": true, + "useWebGLExt": true, + "useDelayOnSwap": false, + "useJOrbisAudioDecoder": false, + "useXHRFetch": false, + "useVisualViewport": true, + "deobfStackTraces": true, + "disableBlobURLs": false, + "eaglerNoDelay": false, + "ramdiskMode": false, + "singleThreadMode": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt new file mode 100755 index 0000000..00c65c3 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt @@ -0,0 +1,66 @@ +{ + "joinServer": null, + "servers": [ + { + "addr": "ws://localhost:8081/", + "hideAddr": false, + "name": "Local test server" + } + ], + "relays": [ + { + "addr": "wss://relay.deev.is/", + "primary": "$random_relay_primary_0", + "comment": "lax1dude relay #1" + }, + { + "addr": "wss://relay.lax1dude.net/", + "primary": "$random_relay_primary_1", + "comment": "lax1dude relay #2" + }, + { + "addr": "wss://relay.shhnowisnottheti.me/", + "primary": "$random_relay_primary_2", + "comment": "ayunami relay #1" + } + ], + "openDebugConsoleOnLaunch": false, + "showBootMenuOnLaunch": false, + "bootMenuBlocksUnsignedClients": false, + "allowBootMenu": true, + "forceWebViewSupport": false, + "enableServerCookies": true, + "enableDownloadOfflineButton": true, + "resourcePacksDB": "resourcePacks", + "enableWebViewCSP": true, + "checkRelaysForUpdates": true, + "allowServerRedirects": true, + "allowUpdateSvc": true, + "html5CursorSupport": false, + "allowFNAWSkins": true, + "allowVoiceClient": true, + "worldsDB": "worlds", + "demoMode": true, + "localStorageNamespace": "_eaglercraftX", + "enableSignatureBadge": false, + "lang": "en_US", + "enableMinceraft": true, + "autoFixLegacyStyleAttr": true, + "allowUpdateDL": true, + "logInvalidCerts": false, + "checkShaderGLErrors": false, + "crashOnUncaughtExceptions": false, + "forceWebGL1": false, + "forceWebGL2": false, + "allowExperimentalWebGL1": true, + "useWebGLExt": true, + "useDelayOnSwap": false, + "useJOrbisAudioDecoder": false, + "useXHRFetch": false, + "useVisualViewport": true, + "deobfStackTraces": true, + "disableBlobURLs": false, + "eaglerNoDelay": false, + "ramdiskMode": false, + "singleThreadMode": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt new file mode 100755 index 0000000..9743e60 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt @@ -0,0 +1,66 @@ +{ + "joinServer": null, + "servers": [ + { + "addr": "ws://localhost:8081/", + "hideAddr": false, + "name": "Local test server" + } + ], + "relays": [ + { + "addr": "wss://relay.deev.is/", + "primary": "$random_relay_primary_0", + "comment": "lax1dude relay #1" + }, + { + "addr": "wss://relay.lax1dude.net/", + "primary": "$random_relay_primary_1", + "comment": "lax1dude relay #2" + }, + { + "addr": "wss://relay.shhnowisnottheti.me/", + "primary": "$random_relay_primary_2", + "comment": "ayunami relay #1" + } + ], + "openDebugConsoleOnLaunch": false, + "showBootMenuOnLaunch": false, + "bootMenuBlocksUnsignedClients": false, + "allowBootMenu": true, + "forceWebViewSupport": false, + "enableServerCookies": true, + "enableDownloadOfflineButton": true, + "resourcePacksDB": "resourcePacks", + "enableWebViewCSP": true, + "checkRelaysForUpdates": true, + "allowServerRedirects": true, + "allowUpdateSvc": true, + "html5CursorSupport": true, + "allowFNAWSkins": true, + "allowVoiceClient": true, + "worldsDB": "worlds", + "demoMode": false, + "localStorageNamespace": "_eaglercraftX", + "enableSignatureBadge": false, + "lang": "en_US", + "enableMinceraft": true, + "autoFixLegacyStyleAttr": true, + "allowUpdateDL": true, + "logInvalidCerts": false, + "checkShaderGLErrors": false, + "crashOnUncaughtExceptions": false, + "forceWebGL1": false, + "forceWebGL2": false, + "allowExperimentalWebGL1": true, + "useWebGLExt": true, + "useDelayOnSwap": false, + "useJOrbisAudioDecoder": false, + "useXHRFetch": false, + "useVisualViewport": true, + "deobfStackTraces": true, + "disableBlobURLs": false, + "eaglerNoDelay": false, + "ramdiskMode": false, + "singleThreadMode": false +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5.txt new file mode 100755 index 0000000..af539a9 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5.txt @@ -0,0 +1,52 @@ +{ + "joinServer": null, + "servers": [ + { + "serverName": "Local Test Server", + "serverAddress": "localhost:25565", + "hideAddress": false + } + ], + "relays": [ + { + "addr": "wss://relay.deev.is/", + "name": "lax1dude relay #1", + "primary": "$random_relay_primary_0" + }, + { + "addr": "wss://relay.lax1dude.net/", + "name": "lax1dude relay #2", + "primary": "$random_relay_primary_1" + }, + { + "addr": "wss://relay.shhnowisnottheti.me/", + "name": "ayunami relay #1", + "primary": "$random_relay_primary_2" + } + ], + "mainMenu": { + "splashes": [ + "Darviglet!", + "eaglerenophile!", + "You Eagler!", + "Yeeeeeee!", + "yeee", + "EEEEEEEEE!", + "You Darvig!", + "You Vigg!", + ":>", + "|>", + "You Yumpster!" + ], + "eaglerLogo": false, + "itemLink": null, + "itemLine0": null, + "itemLine1": null, + "itemLine2": null + }, + "worldsFolder": "MAIN", + "profanity": false, + "hideDownServers": false, + "serverListTitle": null, + "serverListLink": null +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5_legacy.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5_legacy.txt new file mode 100755 index 0000000..bbbefa5 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5_legacy.txt @@ -0,0 +1,32 @@ +[NBT]{ + servers: [ + { + name: "Local Test Server", + ip: "localhost:25565", + hideAddress: false + } + ], + mainMenu: { + itemLink: "", + itemLine0: "", + itemLine1: "", + itemLine2: "", + splashes: [ + "Darviglet!", + "eaglerenophile!", + "You Eagler!", + "Yeeeeeee!", + "yeee", + "EEEEEEEEE!", + "You Darvig!", + "You Vigg!", + ":>", + "|>", + "You Yumpster!" + ] + }, + profanity: false, + hide_down: false, + serverListTitle: "", + serverListLink: "" +}[/NBT] \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5_livestream.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5_livestream.txt new file mode 100755 index 0000000..014eb41 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraft_1_5_livestream.txt @@ -0,0 +1,63 @@ +{ + "joinServer": null, + "servers": [ + { + "serverName": "Local Test Server", + "serverAddress": "localhost:25565", + "hideAddress": false + } + ], + "relays": [ + { + "addr": "wss://relay.deev.is/", + "name": "lax1dude relay #1", + "primary": "$random_relay_primary_0" + }, + { + "addr": "wss://relay.lax1dude.net/", + "name": "lax1dude relay #2", + "primary": "$random_relay_primary_1" + }, + { + "addr": "wss://relay.shhnowisnottheti.me/", + "name": "ayunami relay #1", + "primary": "$random_relay_primary_2" + } + ], + "mainMenu": { + "splashes": [ + "Darviglet!", + "eaglerenophile!", + "You Eagler!", + "Yeeeeeee!", + "yeee", + "EEEEEEEEE!", + "You Darvig!", + "You Vigg!", + ":>", + "|>", + "You Yumpster!" + ], + "eaglerLogo": false, + "itemLink": null, + "itemLine0": null, + "itemLine1": null, + "itemLine2": null + }, + "worldsFolder": "MAIN", + "assetOverrides": { + "title/no-pano-blur.flag": "false", + "records/wait.mp3": "wait.mp3", + "records/mellohi.mp3": "https://stream.nightride.fm/chillsynth.m4a", + "records/far.mp3": "https://stream.nightride.fm/nightride.m4a", + "records/cat.mp3": "http://usa9.fastcast4u.com/proxy/jamz?mp=/1", + "records/ward.mp3": "http://fr4.1mix.co.uk:8000/192h", + "records/strad.mp3": "http://listen.011fm.com:8028/stream15", + "records/blocks.mp3": "https://www.ophanim.net:8444/s/9780", + "records/13.mp3": "https://s2.radio.co/s2b2b68744/listen" + }, + "profanity": false, + "hideDownServers": false, + "serverListTitle": null, + "serverListLink": null +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_peytonplayz585_a1_2_6.txt b/resources/resources/assets/eagler/boot_menu/opts_template_peytonplayz585_a1_2_6.txt new file mode 100755 index 0000000..418a893 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_peytonplayz585_a1_2_6.txt @@ -0,0 +1,6 @@ +{ + "dataBaseName": "_net_PeytonPlayz585_eaglercraft_Alpha_IndexedDBFilesystem_1_2_6", + "playerUsername": null, + "serverIP": null, + "joinServerOnLaunch": null +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_peytonplayz585_b1_7_3.txt b/resources/resources/assets/eagler/boot_menu/opts_template_peytonplayz585_b1_7_3.txt new file mode 100755 index 0000000..dcfb717 --- /dev/null +++ b/resources/resources/assets/eagler/boot_menu/opts_template_peytonplayz585_b1_7_3.txt @@ -0,0 +1,6 @@ +{ + "dataBaseName": "_net_PeytonPlayz585_eaglercraft_beta_IndexedDBFilesystem_1_7_3", + "playerUsername": null, + "serverIP": null, + "joinServerOnLaunch": null +} \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/web_cl_eagleiii_8x16.woff b/resources/resources/assets/eagler/boot_menu/web_cl_eagleiii_8x16.woff new file mode 100755 index 0000000..f0e9430 Binary files /dev/null and b/resources/resources/assets/eagler/boot_menu/web_cl_eagleiii_8x16.woff differ diff --git a/resources/resources/assets/eagler/glsl/accel_font.fsh b/resources/resources/assets/eagler/glsl/accel_font.fsh index 6d1c80a..6922089 100644 --- a/resources/resources/assets/eagler/glsl/accel_font.fsh +++ b/resources/resources/assets/eagler/glsl/accel_font.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,21 +16,17 @@ * */ -precision lowp int; -precision mediump float; -precision mediump sampler2D; +EAGLER_IN(vec2, v_texCoord2f) +EAGLER_IN(vec4, v_color4f) -in vec2 v_texCoord2f; -in vec4 v_color4f; - -layout(location = 0) out vec4 output4f; +EAGLER_FRAG_OUT() uniform sampler2D u_inputTexture; uniform vec4 u_colorBias4f; void main() { - output4f = texture(u_inputTexture, v_texCoord2f) * v_color4f + u_colorBias4f; - if(output4f.a < 0.004) { - discard; - } + EAGLER_FRAG_COLOR = EAGLER_TEXTURE_2D(u_inputTexture, v_texCoord2f) * v_color4f + u_colorBias4f; + if(EAGLER_FRAG_COLOR.a < 0.004) { + discard; + } } diff --git a/resources/resources/assets/eagler/glsl/accel_font.vsh b/resources/resources/assets/eagler/glsl/accel_font.vsh index 50a99cc..827a49f 100644 --- a/resources/resources/assets/eagler/glsl/accel_font.vsh +++ b/resources/resources/assets/eagler/glsl/accel_font.vsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,18 +16,15 @@ * */ -precision lowp int; -precision highp float; -precision mediump sampler2D; +EAGLER_VSH_LAYOUT_BEGIN() +EAGLER_IN(0, vec3, a_position3f) +EAGLER_IN(1, vec2, c_position2i) +EAGLER_IN(2, vec2, c_coords2i) +EAGLER_IN(3, vec4, c_color4f) +EAGLER_VSH_LAYOUT_END() -layout(location = 0) in vec3 a_position3f; - -layout(location = 1) in vec2 c_position2i; -layout(location = 2) in vec2 c_coords2i; -layout(location = 3) in vec4 c_color4f; - -out vec2 v_texCoord2f; -out vec4 v_color4f; +EAGLER_OUT(vec2, v_texCoord2f) +EAGLER_OUT(vec4, v_color4f) uniform mat4 u_matrixTransform; uniform vec2 u_charSize2f; @@ -35,19 +32,19 @@ uniform vec2 u_charCoordSize2f; uniform vec4 u_color4f; void main() { - v_color4f = c_color4f.bgra; - float shadowBit = a_position3f.z; - float boldBit = shadowBit >= 0.5 ? 1.0 : 0.0; - shadowBit -= boldBit * 0.5; - v_color4f.rgb *= (1.0 - shadowBit * 3.0); - v_texCoord2f = (c_coords2i + a_position3f.xy) * u_charCoordSize2f; - vec2 pos2d = c_position2i + vec2(shadowBit * 4.0); - pos2d += a_position3f.xy * u_charSize2f; - pos2d.x += boldBit; - float italicBit = v_color4f.a >= 0.5 ? 2.0 : 0.0; - v_color4f.a -= italicBit * 0.25; - pos2d.x -= (a_position3f.y - 0.5) * italicBit; - v_color4f.a *= 2.0; - v_color4f *= u_color4f; - gl_Position = u_matrixTransform * vec4(pos2d, 0.0, 1.0); + v_color4f = c_color4f.bgra; + float shadowBit = a_position3f.z; + float boldBit = shadowBit >= 0.5 ? 1.0 : 0.0; + shadowBit -= boldBit * 0.5; + v_color4f.rgb *= (1.0 - shadowBit * 3.0); + v_texCoord2f = (c_coords2i + a_position3f.xy) * u_charCoordSize2f; + vec2 pos2d = c_position2i + vec2(shadowBit * 4.0); + pos2d += a_position3f.xy * u_charSize2f; + pos2d.x += boldBit; + float italicBit = v_color4f.a >= 0.5 ? 2.0 : 0.0; + v_color4f.a -= italicBit * 0.25; + pos2d.x -= (a_position3f.y - 0.5) * italicBit; + v_color4f.a *= 2.0; + v_color4f *= u_color4f; + EAGLER_VERT_POSITION = u_matrixTransform * vec4(pos2d, 0.0, 1.0); } diff --git a/resources/resources/assets/eagler/glsl/accel_particle.fsh b/resources/resources/assets/eagler/glsl/accel_particle.fsh index 256290c..bbb7476 100644 --- a/resources/resources/assets/eagler/glsl/accel_particle.fsh +++ b/resources/resources/assets/eagler/glsl/accel_particle.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,20 +16,16 @@ * */ -precision lowp int; -precision mediump float; -precision mediump sampler2D; +EAGLER_IN(vec2, v_texCoord2f) +EAGLER_IN(vec4, v_color4f) -in vec2 v_texCoord2f; -in vec4 v_color4f; - -layout(location = 0) out vec4 output4f; +EAGLER_FRAG_OUT() uniform sampler2D u_inputTexture; void main() { - output4f = texture(u_inputTexture, v_texCoord2f) * v_color4f; - if(output4f.a < 0.004) { - discard; - } + EAGLER_FRAG_COLOR = EAGLER_TEXTURE_2D(u_inputTexture, v_texCoord2f) * v_color4f; + if(EAGLER_FRAG_COLOR.a < 0.004) { + discard; + } } diff --git a/resources/resources/assets/eagler/glsl/accel_particle.vsh b/resources/resources/assets/eagler/glsl/accel_particle.vsh index 59becc7..bfe2ef5 100644 --- a/resources/resources/assets/eagler/glsl/accel_particle.vsh +++ b/resources/resources/assets/eagler/glsl/accel_particle.vsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,20 +16,17 @@ * */ -precision lowp int; -precision highp float; -precision mediump sampler2D; +EAGLER_VSH_LAYOUT_BEGIN() +EAGLER_IN(0, vec2, a_position2f) +EAGLER_IN(1, vec3, p_position3f) +EAGLER_IN(2, vec2, p_texCoords2i) +EAGLER_IN(3, vec2, p_lightMap2f) +EAGLER_IN(4, vec2, p_particleSize_texCoordsSize_2i) +EAGLER_IN(5, vec4, p_color4f) +EAGLER_VSH_LAYOUT_END() -layout(location = 0) in vec2 a_position2f; - -layout(location = 1) in vec3 p_position3f; -layout(location = 2) in vec2 p_texCoords2i; -layout(location = 3) in vec2 p_lightMap2f; -layout(location = 4) in vec2 p_particleSize_texCoordsSize_2i; -layout(location = 5) in vec4 p_color4f; - -out vec2 v_texCoord2f; -out vec4 v_color4f; +EAGLER_OUT(vec2, v_texCoord2f) +EAGLER_OUT(vec4, v_color4f) uniform mat4 u_matrixTransform; uniform vec3 u_texCoordSize2f_particleSize1f; @@ -40,19 +37,19 @@ uniform vec4 u_color4f; uniform sampler2D u_lightmapTexture; void main() { - v_color4f = u_color4f * p_color4f.bgra * texture(u_lightmapTexture, p_lightMap2f); - - vec2 tex2f = a_position2f * 0.5 + 0.5; - tex2f.y = 1.0 - tex2f.y; - tex2f = p_texCoords2i + tex2f * p_particleSize_texCoordsSize_2i.y; - v_texCoord2f = tex2f * u_texCoordSize2f_particleSize1f.xy; - - float particleSize = u_texCoordSize2f_particleSize1f.z * p_particleSize_texCoordsSize_2i.x; - - vec3 pos3f = p_position3f; - vec2 spos2f = a_position2f * particleSize; - pos3f += u_transformParam_1_2_5_f * spos2f.xyy; - pos3f.zx += u_transformParam_3_4_f * spos2f; - - gl_Position = u_matrixTransform * vec4(pos3f, 1.0); + v_color4f = u_color4f * p_color4f.bgra * EAGLER_TEXTURE_2D(u_lightmapTexture, p_lightMap2f); + + vec2 tex2f = a_position2f * 0.5 + 0.5; + tex2f.y = 1.0 - tex2f.y; + tex2f = p_texCoords2i + tex2f * p_particleSize_texCoordsSize_2i.y; + v_texCoord2f = tex2f * u_texCoordSize2f_particleSize1f.xy; + + float particleSize = u_texCoordSize2f_particleSize1f.z * p_particleSize_texCoordsSize_2i.x; + + vec3 pos3f = p_position3f; + vec2 spos2f = a_position2f * particleSize; + pos3f += u_transformParam_1_2_5_f * spos2f.xyy; + pos3f.zx += u_transformParam_3_4_f * spos2f; + + EAGLER_VERT_POSITION = u_matrixTransform * vec4(pos3f, 1.0); } diff --git a/resources/resources/assets/eagler/glsl/core.fsh b/resources/resources/assets/eagler/glsl/core.fsh index 5a01a79..f9960b9 100644 --- a/resources/resources/assets/eagler/glsl/core.fsh +++ b/resources/resources/assets/eagler/glsl/core.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -17,11 +17,11 @@ */ #if defined(COMPILE_ENABLE_TEX_GEN) || defined(COMPILE_ENABLE_FOG) -in vec4 v_position4f; +EAGLER_IN(vec4, v_position4f) #endif #ifdef COMPILE_TEXTURE_ATTRIB -in vec2 v_texture2f; +EAGLER_IN(vec2, v_texture2f) #endif uniform vec4 u_color4f; @@ -32,15 +32,15 @@ uniform vec4 u_colorBlendAdd4f; #endif #ifdef COMPILE_COLOR_ATTRIB -in vec4 v_color4f; +EAGLER_IN(vec4, v_color4f) #endif #ifdef COMPILE_NORMAL_ATTRIB -in vec3 v_normal3f; +EAGLER_IN(vec3, v_normal3f) #endif #ifdef COMPILE_LIGHTMAP_ATTRIB -in vec2 v_lightmap2f; +EAGLER_IN(vec2, v_lightmap2f) #endif #ifdef COMPILE_ENABLE_TEXTURE2D @@ -76,7 +76,7 @@ uniform vec4 u_fogColor4f; #endif #ifdef COMPILE_ENABLE_TEX_GEN -in vec3 v_objectPosition3f; +EAGLER_IN(vec3, v_objectPosition3f) uniform ivec4 u_texGenPlane4i; uniform vec4 u_texGenS4f; uniform vec4 u_texGenT4f; @@ -89,95 +89,110 @@ uniform mat4 u_textureMat4f01; uniform vec2 u_textureAnisotropicFix; #endif -layout(location = 0) out vec4 output4f; +EAGLER_FRAG_OUT() void main() { - #ifdef COMPILE_COLOR_ATTRIB - vec4 color = v_color4f * u_color4f; + vec4 color = v_color4f * u_color4f; #else - vec4 color = u_color4f; + vec4 color = u_color4f; #endif - + #ifdef COMPILE_ENABLE_TEX_GEN - vec4 texGenVector; - - vec4 texGenPosSrc[2]; - texGenPosSrc[0] = vec4(v_objectPosition3f, 1.0); - texGenPosSrc[1] = v_position4f; - - texGenVector.x = dot(texGenPosSrc[u_texGenPlane4i.x], u_texGenS4f); - texGenVector.y = dot(texGenPosSrc[u_texGenPlane4i.y], u_texGenT4f); - texGenVector.z = dot(texGenPosSrc[u_texGenPlane4i.z], u_texGenR4f); - texGenVector.w = dot(texGenPosSrc[u_texGenPlane4i.w], u_texGenQ4f); - - texGenVector = u_textureMat4f01 * texGenVector; - color *= texture(u_samplerTexture, texGenVector.xy / texGenVector.w); - -#ifdef COMPILE_ENABLE_ALPHA_TEST - if(color.a < u_alphaTestRef1f) discard; -#endif - + vec4 tmpVec4 = vec4(v_objectPosition3f, 1.0); + vec4 texGenVector; + texGenVector.x = dot(u_texGenPlane4i.x == 1 ? v_position4f : tmpVec4, u_texGenS4f); + texGenVector.y = dot(u_texGenPlane4i.y == 1 ? v_position4f : tmpVec4, u_texGenT4f); + texGenVector.z = dot(u_texGenPlane4i.z == 1 ? v_position4f : tmpVec4, u_texGenR4f); + texGenVector.w = dot(u_texGenPlane4i.w == 1 ? v_position4f : tmpVec4, u_texGenQ4f); +#ifdef EAGLER_HAS_GLES_300 + texGenVector.xyz = mat4x3( + u_textureMat4f01[0].xyw, + u_textureMat4f01[1].xyw, + u_textureMat4f01[2].xyw, + u_textureMat4f01[3].xyw) * texGenVector; + texGenVector.xy /= texGenVector.z; #else - + texGenVector = u_textureMat4f01 * texGenVector; + texGenVector.xy /= texGenVector.w; +#endif + + color *= EAGLER_TEXTURE_2D(u_samplerTexture, texGenVector.xy); + +#ifdef COMPILE_ENABLE_ALPHA_TEST + if(color.a < u_alphaTestRef1f) discard; +#endif + +#else + #ifdef COMPILE_ENABLE_TEXTURE2D #ifdef COMPILE_TEXTURE_ATTRIB #ifdef COMPILE_ENABLE_ANISOTROPIC_FIX - // d3d11 doesn't support GL_NEAREST upscaling with anisotropic - // filtering enabled, so it needs this stupid fix to 'work' - vec2 uv = floor(v_texture2f * u_textureAnisotropicFix) + 0.5; - color *= texture(u_samplerTexture, uv / u_textureAnisotropicFix); + // d3d11 doesn't support GL_NEAREST upscaling with anisotropic + // filtering enabled, so it needs this stupid fix to 'work' + vec2 uv = floor(v_texture2f * u_textureAnisotropicFix) + 0.5; + color *= EAGLER_TEXTURE_2D(u_samplerTexture, uv / u_textureAnisotropicFix); #else - color *= texture(u_samplerTexture, v_texture2f); + color *= EAGLER_TEXTURE_2D(u_samplerTexture, v_texture2f); #endif #else - color *= texture(u_samplerTexture, u_textureCoords01); + color *= EAGLER_TEXTURE_2D(u_samplerTexture, u_textureCoords01); #endif #endif - + #ifdef COMPILE_ENABLE_LIGHTMAP #ifdef COMPILE_LIGHTMAP_ATTRIB - color *= texture(u_samplerLightmap, v_lightmap2f); + color *= EAGLER_TEXTURE_2D(u_samplerLightmap, v_lightmap2f); #else - color *= texture(u_samplerLightmap, u_textureCoords02); + color *= EAGLER_TEXTURE_2D(u_samplerLightmap, u_textureCoords02); #endif #endif - + #ifdef COMPILE_BLEND_ADD - color = color * u_colorBlendSrc4f + u_colorBlendAdd4f; + color = color * u_colorBlendSrc4f + u_colorBlendAdd4f; #endif - + #ifdef COMPILE_ENABLE_ALPHA_TEST - if(color.a < u_alphaTestRef1f) discard; + if(color.a < u_alphaTestRef1f) discard; #endif - + #endif - + #ifdef COMPILE_ENABLE_MC_LIGHTING #ifdef COMPILE_NORMAL_ATTRIB - vec3 normal = normalize(v_normal3f); + vec3 normal = normalize(v_normal3f); #else - vec3 normal = u_uniformNormal3f; + vec3 normal = u_uniformNormal3f; #endif - float diffuse = 0.0; - vec4 light; - for(int i = 0; i < u_lightsEnabled1i; ++i) { - light = u_lightsDirections4fv[i]; - diffuse += max(dot(light.xyz, normal), 0.0) * light.w; - } - color.rgb *= min(u_lightsAmbient3f + vec3(diffuse), 1.0); + float diffuse = 0.0; + vec4 light; +#ifdef EAGLER_HAS_GLES_300 + for(int i = 0; i < u_lightsEnabled1i; ++i) { +#else + for(int i = 0; i < 4; ++i) { #endif - + light = u_lightsDirections4fv[i]; + diffuse += max(dot(light.xyz, normal), 0.0) * light.w; +#ifndef EAGLER_HAS_GLES_300 + if(i + 1 >= u_lightsEnabled1i) { + break; + } +#endif + } + color.rgb *= min(u_lightsAmbient3f + vec3(diffuse), 1.0); +#endif + #ifdef COMPILE_ENABLE_FOG - vec3 fogPos = v_position4f.xyz / v_position4f.w; - float dist = length(fogPos); - float fogDensity = u_fogParameters4f.y; - float fogStart = u_fogParameters4f.z; - float fogEnd = u_fogParameters4f.w; - float f = u_fogParameters4f.x > 0.0 ? 1.0 - exp(-fogDensity * dist) : - (dist - fogStart) / (fogEnd - fogStart); - color.rgb = mix(color.rgb, u_fogColor4f.rgb, clamp(f, 0.0, 1.0) * u_fogColor4f.a); + vec3 fogPos = v_position4f.xyz / v_position4f.w; + float dist = length(fogPos); + float fogDensity = u_fogParameters4f.y; + float fogStart = u_fogParameters4f.z; + float fogEnd = u_fogParameters4f.w; + float f = u_fogParameters4f.x > 0.0 ? 1.0 - exp(-fogDensity * dist) : + (dist - fogStart) / (fogEnd - fogStart); + color.rgb = mix(color.rgb, u_fogColor4f.rgb, clamp(f, 0.0, 1.0) * u_fogColor4f.a); #endif - - output4f = color; -} + + EAGLER_FRAG_COLOR = color; + } + \ No newline at end of file diff --git a/resources/resources/assets/eagler/glsl/core.vsh b/resources/resources/assets/eagler/glsl/core.vsh index 77ce504..017c45d 100644 --- a/resources/resources/assets/eagler/glsl/core.vsh +++ b/resources/resources/assets/eagler/glsl/core.vsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,39 +16,39 @@ * */ -in vec3 a_position3f; +EAGLER_IN_AUTO(vec3, a_position3f) #if defined(COMPILE_ENABLE_TEX_GEN) || defined(COMPILE_ENABLE_FOG) #define _COMPILE_VARYING_POSITION #endif #ifdef _COMPILE_VARYING_POSITION -out vec4 v_position4f; +EAGLER_OUT(vec4, v_position4f) #endif #ifdef COMPILE_ENABLE_TEX_GEN -out vec3 v_objectPosition3f; +EAGLER_OUT(vec3, v_objectPosition3f) #endif #ifdef COMPILE_TEXTURE_ATTRIB -in vec2 a_texture2f; -out vec2 v_texture2f; +EAGLER_IN_AUTO(vec2, a_texture2f) +EAGLER_OUT(vec2, v_texture2f) uniform mat4 u_textureMat4f01; #endif #ifdef COMPILE_COLOR_ATTRIB -in vec4 a_color4f; -out vec4 v_color4f; +EAGLER_IN_AUTO(vec4, a_color4f) +EAGLER_OUT(vec4, v_color4f) #endif #ifdef COMPILE_NORMAL_ATTRIB -in vec4 a_normal4f; -out vec3 v_normal3f; +EAGLER_IN_AUTO(vec4, a_normal4f) +EAGLER_OUT(vec3, v_normal3f) #endif #ifdef COMPILE_LIGHTMAP_ATTRIB -in vec2 a_lightmap2f; -out vec2 v_lightmap2f; +EAGLER_IN_AUTO(vec2, a_lightmap2f) +EAGLER_OUT(vec2, v_lightmap2f) uniform mat4 u_textureMat4f02; #endif @@ -66,34 +66,34 @@ uniform mat4 u_modelviewMat4f; void main() { #ifdef COMPILE_ENABLE_TEX_GEN - v_objectPosition3f = a_position3f; + v_objectPosition3f = a_position3f; #endif - + #ifdef _COMPILE_VARYING_POSITION - v_position4f = u_modelviewMat4f * vec4(a_position3f, 1.0); + v_position4f = u_modelviewMat4f * vec4(a_position3f, 1.0); #endif - + #ifdef COMPILE_TEXTURE_ATTRIB - vec3 v_textureTmp3f = TEX_MAT3(u_textureMat4f01) * vec3(a_texture2f, 1.0); - v_texture2f = v_textureTmp3f.xy / v_textureTmp3f.z; + vec3 v_textureTmp3f = TEX_MAT3(u_textureMat4f01) * vec3(a_texture2f, 1.0); + v_texture2f = v_textureTmp3f.xy / v_textureTmp3f.z; #endif - + #ifdef COMPILE_COLOR_ATTRIB - v_color4f = a_color4f; + v_color4f = a_color4f; #endif - + #ifdef COMPILE_NORMAL_ATTRIB - v_normal3f = normalize(mat3(u_modelviewMat4f) * a_normal4f.xyz); + v_normal3f = normalize(mat3(u_modelviewMat4f) * a_normal4f.xyz); #endif - + #ifdef COMPILE_LIGHTMAP_ATTRIB - vec3 v_lightmapTmp3f = TEX_MAT3(u_textureMat4f02) * vec3(a_lightmap2f, 1.0); - v_lightmap2f = v_lightmapTmp3f.xy / v_lightmapTmp3f.z; + vec3 v_lightmapTmp3f = TEX_MAT3(u_textureMat4f02) * vec3(a_lightmap2f, 1.0); + v_lightmap2f = v_lightmapTmp3f.xy / v_lightmapTmp3f.z; #endif - + #ifdef _COMPILE_VARYING_POSITION - gl_Position = u_projectionMat4f * v_position4f; + EAGLER_VERT_POSITION = u_projectionMat4f * v_position4f; #else - gl_Position = u_modelviewProjMat4f * vec4(a_position3f, 1.0); + EAGLER_VERT_POSITION = u_modelviewProjMat4f * vec4(a_position3f, 1.0); #endif } diff --git a/resources/resources/assets/eagler/glsl/deferred/forward_core.fsh b/resources/resources/assets/eagler/glsl/deferred/forward_core.fsh index 4e6a375..87b7a62 100644 --- a/resources/resources/assets/eagler/glsl/deferred/forward_core.fsh +++ b/resources/resources/assets/eagler/glsl/deferred/forward_core.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 lax1dude. All Rights Reserved. +* Copyright (c) 2023 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -86,41 +86,41 @@ layout(location = 0) out vec4 output4f; #ifdef COMPILE_DYNAMIC_LIGHTS struct DynamicLight { - mediump vec4 u_lightPosition4f; - mediump vec4 u_lightColor4f; + mediump vec4 u_lightPosition4f; + mediump vec4 u_lightColor4f; }; layout(std140) uniform u_chunkLightingData { - mediump int u_dynamicLightCount1i; - mediump int _paddingA_; - mediump int _paddingB_; - mediump int _paddingC_; - DynamicLight u_dynamicLightArray[12]; + mediump int u_dynamicLightCount1i; + mediump int _paddingA_; + mediump int _paddingB_; + mediump int _paddingC_; + DynamicLight u_dynamicLightArray[12]; }; #endif layout(std140) uniform u_worldLightingData { - mediump vec4 u_sunDirection4f; - mediump vec4 u_sunColor3f_sky1f; - mediump vec4 u_fogParameters4f; - mediump vec4 u_fogColorLight4f; - mediump vec4 u_fogColorDark4f; - mediump vec4 u_fogColorAddSun4f; - mediump vec4 u_blockSkySunDynamicLightFac4f; + mediump vec4 u_sunDirection4f; + mediump vec4 u_sunColor3f_sky1f; + mediump vec4 u_fogParameters4f; + mediump vec4 u_fogColorLight4f; + mediump vec4 u_fogColorDark4f; + mediump vec4 u_fogColorAddSun4f; + mediump vec4 u_blockSkySunDynamicLightFac4f; #ifdef COMPILE_SUN_SHADOW_LOD0 - mediump mat4 u_sunShadowMatrixLOD04f; + mediump mat4 u_sunShadowMatrixLOD04f; #define DO_COMPILE_SUN_SHADOWS #define SUN_SHADOW_MAP_FRAC 1.0 #endif #ifdef COMPILE_SUN_SHADOW_LOD1 - mediump mat4 u_sunShadowMatrixLOD04f; - mediump mat4 u_sunShadowMatrixLOD14f; + mediump mat4 u_sunShadowMatrixLOD04f; + mediump mat4 u_sunShadowMatrixLOD14f; #define DO_COMPILE_SUN_SHADOWS #define SUN_SHADOW_MAP_FRAC 0.5 #endif #ifdef COMPILE_SUN_SHADOW_LOD2 - mediump mat4 u_sunShadowMatrixLOD04f; - mediump mat4 u_sunShadowMatrixLOD14f; - mediump mat4 u_sunShadowMatrixLOD24f; + mediump mat4 u_sunShadowMatrixLOD04f; + mediump mat4 u_sunShadowMatrixLOD14f; + mediump mat4 u_sunShadowMatrixLOD24f; #define DO_COMPILE_SUN_SHADOWS #define SUN_SHADOW_MAP_FRAC 0.3333333 #endif @@ -163,311 +163,308 @@ uniform sampler2D u_lightShaftsTexture; uniform sampler2DShadow u_sunShadowDepthTexture; #ifdef COMPILE_SUN_SHADOW_SMOOTH const vec2 POISSON_DISK[7] = vec2[]( -vec2(-0.077, 0.995), vec2(0.998, 0.015), -vec2(-0.116, -0.987), vec2(-0.916, 0.359), -vec2(-0.697, -0.511), vec2(0.740, -0.612), -vec2(0.675, 0.682)); + vec2(-0.077, 0.995), vec2(0.998, 0.015), + vec2(-0.116, -0.987), vec2(-0.916, 0.359), + vec2(-0.697, -0.511), vec2(0.740, -0.612), + vec2(0.675, 0.682)); #define SMOOTH_SHADOW_SAMPLES 1.0 / 8.0 #define SMOOTH_SHADOW_RADIUS 0.00075 #define SMOOTH_SHADOW_POISSON_SAMPLE(idx, tex, lod, vec3Pos, accum, tmpVec2)\ - tmpVec2 = vec3Pos.xy + POISSON_DISK[idx] * SMOOTH_SHADOW_RADIUS;\ - tmpVec2 = clamp(tmpVec2, vec2(0.001), vec2(0.999));\ - tmpVec2.y += lod;\ - tmpVec2.y *= SUN_SHADOW_MAP_FRAC;\ - accum += textureLod(tex, vec3(tmpVec2, vec3Pos.z), 0.0) * SMOOTH_SHADOW_SAMPLES; +tmpVec2 = vec3Pos.xy + POISSON_DISK[idx] * SMOOTH_SHADOW_RADIUS;\ +tmpVec2 = clamp(tmpVec2, vec2(0.001), vec2(0.999));\ +tmpVec2.y += lod;\ +tmpVec2.y *= SUN_SHADOW_MAP_FRAC;\ +accum += textureLod(tex, vec3(tmpVec2, vec3Pos.z), 0.0) * SMOOTH_SHADOW_SAMPLES; #endif #endif void main() { - vec4 worldPosition4f; - vec4 worldDirection4f; - vec4 diffuseColor4f; - vec3 normalVector3f; - vec2 lightmapCoords2f; - vec3 materialData3f; - float block1f; - - // =========== RESOLVE CONSTANTS ============ // - - worldPosition4f = u_inverseViewMatrix4f * v_position4f; - worldPosition4f.xyz /= worldPosition4f.w; - worldPosition4f.w = 1.0; - worldDirection4f = u_inverseViewMatrix4f * vec4(v_position4f.xyz / v_position4f.w, 0.0); - worldDirection4f.xyz = normalize(worldDirection4f.xyz); - + vec4 worldPosition4f; + vec4 worldDirection4f; + vec4 diffuseColor4f; + vec3 normalVector3f; + vec2 lightmapCoords2f; + vec3 materialData3f; + float block1f; + + // =========== RESOLVE CONSTANTS ============ // + + worldPosition4f = u_inverseViewMatrix4f * v_position4f; + worldPosition4f.xyz /= worldPosition4f.w; + worldPosition4f.w = 1.0; + worldDirection4f = u_inverseViewMatrix4f * vec4(v_position4f.xyz / v_position4f.w, 0.0); + worldDirection4f.xyz = normalize(worldDirection4f.xyz); + #ifdef COMPILE_ENABLE_LIGHTMAP #ifdef COMPILE_LIGHTMAP_ATTRIB - lightmapCoords2f = v_lightmap2f; + lightmapCoords2f = v_lightmap2f; #else - lightmapCoords2f = u_textureCoords02; + lightmapCoords2f = u_textureCoords02; #endif #else - lightmapCoords2f = vec2(0.0, 1.0); + lightmapCoords2f = vec2(0.0, 1.0); #endif - + #ifdef COMPILE_NORMAL_ATTRIB - normalVector3f = normalize(v_normal3f); - block1f = v_block1f; + normalVector3f = normalize(v_normal3f); + block1f = v_block1f; #else - normalVector3f = u_uniformNormal3f; - block1f = u_blockConstant1f; + normalVector3f = u_uniformNormal3f; + block1f = u_blockConstant1f; #endif - - normalVector3f = normalize(mat3(u_inverseViewMatrix4f) * normalVector3f); - - // ========= CALCULATE DIFFUSE COLOR ========== // - + + normalVector3f = normalize(mat3(u_inverseViewMatrix4f) * normalVector3f); + + // ========= CALCULATE DIFFUSE COLOR ========== // + #ifdef COMPILE_COLOR_ATTRIB - diffuseColor4f = v_color4f * u_color4f; + diffuseColor4f = v_color4f * u_color4f; #else - diffuseColor4f = u_color4f; + diffuseColor4f = u_color4f; #endif - + #ifdef COMPILE_ENABLE_TEXTURE2D - vec2 texCoords2f; + vec2 texCoords2f; #ifdef COMPILE_ENABLE_TEX_GEN - vec4 texGenVector; - vec4 texGenPosSrc[2]; - texGenPosSrc[0] = vec4(v_objectPosition3f, 1.0); - texGenPosSrc[1] = v_position4f; - texGenVector.x = dot(texGenPosSrc[u_texGenPlane4i.x], u_texGenS4f); - texGenVector.y = dot(texGenPosSrc[u_texGenPlane4i.y], u_texGenT4f); - texGenVector.z = dot(texGenPosSrc[u_texGenPlane4i.z], u_texGenR4f); - texGenVector.w = dot(texGenPosSrc[u_texGenPlane4i.w], u_texGenQ4f); - texGenVector = vec4(mat4x3( - u_textureMat4f01[0].xyw, - u_textureMat4f01[1].xyw, - u_textureMat4f01[2].xyw, - u_textureMat4f01[3].xyw - ) * texGenVector, 0.0); - texCoords2f = texGenVector.xy / texGenVector.z; + vec4 tmpVec4 = vec4(v_objectPosition3f, 1.0); + vec4 texGenVector; + texGenVector.x = dot(u_texGenPlane4i.x == 1 ? v_position4f : tmpVec4, u_texGenS4f); + texGenVector.y = dot(u_texGenPlane4i.y == 1 ? v_position4f : tmpVec4, u_texGenT4f); + texGenVector.z = dot(u_texGenPlane4i.z == 1 ? v_position4f : tmpVec4, u_texGenR4f); + texGenVector.w = dot(u_texGenPlane4i.w == 1 ? v_position4f : tmpVec4, u_texGenQ4f); + texGenVector.xyz = mat4x3( + u_textureMat4f01[0].xyw, + u_textureMat4f01[1].xyw, + u_textureMat4f01[2].xyw, + u_textureMat4f01[3].xyw) * texGenVector; + texCoords2f = texGenVector.xy / texGenVector.z; #else - + #ifdef COMPILE_TEXTURE_ATTRIB #ifdef COMPILE_ENABLE_ANISOTROPIC_FIX - texCoords2f = floor(v_texture2f * u_textureAnisotropicFix) + 0.5; - texCoords2f /= u_textureAnisotropicFix; + texCoords2f = floor(v_texture2f * u_textureAnisotropicFix) + 0.5; + texCoords2f /= u_textureAnisotropicFix; #else - texCoords2f = v_texture2f; + texCoords2f = v_texture2f; #endif #else - texCoords2f = u_textureCoords01; + texCoords2f = u_textureCoords01; #endif #endif - diffuseColor4f *= texture(u_samplerTexture, texCoords2f); + diffuseColor4f *= texture(u_samplerTexture, texCoords2f); #endif - + #ifdef COMPILE_BLEND_ADD - diffuseColor4f = diffuseColor4f * u_colorBlendSrc4f + u_colorBlendAdd4f; + diffuseColor4f = diffuseColor4f * u_colorBlendSrc4f + u_colorBlendAdd4f; #endif - - // ============= ALPHA TEST ============== // - + + // ============= ALPHA TEST ============== // + #ifdef COMPILE_ENABLE_ALPHA_TEST - if(diffuseColor4f.a < u_alphaTestRef1f) discard; + if(diffuseColor4f.a < u_alphaTestRef1f) discard; #endif - - // ========== RESOLVE MATERIALS =========== // - + + // ========== RESOLVE MATERIALS =========== // + #ifdef COMPILE_NORMAL_MATERIAL_TEXTURE - vec2 uv2 = vec2(1.0, 0.5) * texCoords2f; - uv2.y += 0.5; - materialData3f = texture(u_samplerNormalMaterial, uv2).rgb; + vec2 uv2 = vec2(1.0, 0.5) * texCoords2f; + uv2.y += 0.5; + materialData3f = texture(u_samplerNormalMaterial, uv2).rgb; #else - materialData3f = u_materialConstants3f; + materialData3f = u_materialConstants3f; #endif - - vec3 metalN, metalK; - PREFETCH_METALS(diffuseColor4f.rgb, materialData3f.g, metalN, metalK) - - // ============ SUN LIGHTING ============== // - - diffuseColor4f.rgb *= diffuseColor4f.rgb; - - vec3 lightColor3f = vec3(0.0); - if(dot(u_sunDirection4f.xyz, normalVector3f) > 0.0 && lightmapCoords2f.g > 0.5 && - (u_sunColor3f_sky1f.r + u_sunColor3f_sky1f.g + u_sunColor3f_sky1f.b) > 0.001) { + + vec3 metalN, metalK; + PREFETCH_METALS(diffuseColor4f.rgb, materialData3f.g, metalN, metalK) + + // ============ SUN LIGHTING ============== // + + diffuseColor4f.rgb *= diffuseColor4f.rgb; + + vec3 lightColor3f = vec3(0.0); + if(dot(u_sunDirection4f.xyz, normalVector3f) > 0.0 && lightmapCoords2f.g > 0.5 && + (u_sunColor3f_sky1f.r + u_sunColor3f_sky1f.g + u_sunColor3f_sky1f.b) > 0.001) { #ifdef DO_COMPILE_SUN_SHADOWS - - // ========== SUN SHADOW: LOD0 ============ // - - float skyLight = max(lightmapCoords2f.g * 2.0 - 1.0, 0.0); - float shadowSample = 1.0; - vec4 shadowWorldPos4f = worldPosition4f; - shadowWorldPos4f.xyz += normalVector3f * 0.05; - - vec4 shadowTexPos4f; - vec2 tmpVec2; - for(;;) { - shadowTexPos4f = u_sunShadowMatrixLOD04f * shadowWorldPos4f; - if(shadowTexPos4f.xyz == clamp(shadowTexPos4f.xyz, vec3(0.005), vec3(0.995))) { - shadowSample = textureLod(u_sunShadowDepthTexture, vec3(shadowTexPos4f.xy * vec2(1.0, SUN_SHADOW_MAP_FRAC), shadowTexPos4f.z), 0.0); + + // ========== SUN SHADOW: LOD0 ============ // + + float skyLight = max(lightmapCoords2f.g * 2.0 - 1.0, 0.0); + float shadowSample = 1.0; + vec4 shadowWorldPos4f = worldPosition4f; + shadowWorldPos4f.xyz += normalVector3f * 0.05; + + vec4 shadowTexPos4f; + vec2 tmpVec2; + for(;;) { + shadowTexPos4f = u_sunShadowMatrixLOD04f * shadowWorldPos4f; + if(shadowTexPos4f.xyz == clamp(shadowTexPos4f.xyz, vec3(0.005), vec3(0.995))) { + shadowSample = textureLod(u_sunShadowDepthTexture, vec3(shadowTexPos4f.xy * vec2(1.0, SUN_SHADOW_MAP_FRAC), shadowTexPos4f.z), 0.0); #ifdef COMPILE_SUN_SHADOW_SMOOTH - shadowSample *= SMOOTH_SHADOW_SAMPLES; - SMOOTH_SHADOW_POISSON_SAMPLE(0, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - SMOOTH_SHADOW_POISSON_SAMPLE(1, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - SMOOTH_SHADOW_POISSON_SAMPLE(2, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - SMOOTH_SHADOW_POISSON_SAMPLE(3, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - SMOOTH_SHADOW_POISSON_SAMPLE(4, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - SMOOTH_SHADOW_POISSON_SAMPLE(5, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - SMOOTH_SHADOW_POISSON_SAMPLE(6, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) - shadowSample = max(shadowSample * 2.0 - 1.0, 0.0); + shadowSample *= SMOOTH_SHADOW_SAMPLES; + SMOOTH_SHADOW_POISSON_SAMPLE(0, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + SMOOTH_SHADOW_POISSON_SAMPLE(1, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + SMOOTH_SHADOW_POISSON_SAMPLE(2, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + SMOOTH_SHADOW_POISSON_SAMPLE(3, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + SMOOTH_SHADOW_POISSON_SAMPLE(4, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + SMOOTH_SHADOW_POISSON_SAMPLE(5, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + SMOOTH_SHADOW_POISSON_SAMPLE(6, u_sunShadowDepthTexture, 0.0, shadowTexPos4f.xyz, shadowSample, tmpVec2) + shadowSample = max(shadowSample * 2.0 - 1.0, 0.0); #endif - break; - } - + break; + } + #if defined(COMPILE_SUN_SHADOW_LOD1) || defined(COMPILE_SUN_SHADOW_LOD2) - shadowTexPos4f = u_sunShadowMatrixLOD14f * shadowWorldPos4f; - if(shadowTexPos4f.xyz == clamp(shadowTexPos4f.xyz, vec3(0.005), vec3(0.995))) { - shadowTexPos4f.y += 1.0; - shadowTexPos4f.y *= SUN_SHADOW_MAP_FRAC; - shadowSample = textureLod(u_sunShadowDepthTexture, vec3(shadowTexPos4f.xy, shadowTexPos4f.z + 0.00015), 0.0); - break; - } + shadowTexPos4f = u_sunShadowMatrixLOD14f * shadowWorldPos4f; + if(shadowTexPos4f.xyz == clamp(shadowTexPos4f.xyz, vec3(0.005), vec3(0.995))) { + shadowTexPos4f.y += 1.0; + shadowTexPos4f.y *= SUN_SHADOW_MAP_FRAC; + shadowSample = textureLod(u_sunShadowDepthTexture, vec3(shadowTexPos4f.xy, shadowTexPos4f.z + 0.00015), 0.0); + break; + } #endif - + #ifdef COMPILE_SUN_SHADOW_LOD2 - shadowTexPos4f = u_sunShadowMatrixLOD24f * shadowWorldPos4f; - if(shadowTexPos4f.xyz == clamp(shadowTexPos4f.xyz, vec3(0.005), vec3(0.995))) { - shadowTexPos4f.y += 2.0; - shadowTexPos4f.y *= SUN_SHADOW_MAP_FRAC; - shadowSample = textureLod(u_sunShadowDepthTexture, vec3(shadowTexPos4f.xy, shadowTexPos4f.z + 0.00015), 0.0); - } + shadowTexPos4f = u_sunShadowMatrixLOD24f * shadowWorldPos4f; + if(shadowTexPos4f.xyz == clamp(shadowTexPos4f.xyz, vec3(0.005), vec3(0.995))) { + shadowTexPos4f.y += 2.0; + shadowTexPos4f.y *= SUN_SHADOW_MAP_FRAC; + shadowSample = textureLod(u_sunShadowDepthTexture, vec3(shadowTexPos4f.xy, shadowTexPos4f.z + 0.00015), 0.0); + } #endif - break; - } + break; + } #endif - lightColor3f = u_sunColor3f_sky1f.rgb * max(lightmapCoords2f.g * 2.0 - 1.0, 0.0); + lightColor3f = u_sunColor3f_sky1f.rgb * max(lightmapCoords2f.g * 2.0 - 1.0, 0.0); #ifdef DO_COMPILE_SUN_SHADOWS - lightColor3f *= shadowSample * skyLight; + lightColor3f *= shadowSample * skyLight; #endif - lightColor3f = eaglercraftLighting(diffuseColor4f.rgb, lightColor3f, -worldDirection4f.xyz, u_sunDirection4f.xyz, normalVector3f, materialData3f, metalN, metalK) * u_blockSkySunDynamicLightFac4f.z; - } - - float f; + lightColor3f = eaglercraftLighting(diffuseColor4f.rgb, lightColor3f, -worldDirection4f.xyz, u_sunDirection4f.xyz, normalVector3f, materialData3f, metalN, metalK) * u_blockSkySunDynamicLightFac4f.z; + } + + float f; #ifdef COMPILE_PARABOLOID_ENV_MAP - - // =========== ENVIRONMENT MAP =========== // - - f = materialData3f.g < 0.06 ? 1.0 : 0.0; - f += materialData3f.r < 0.5 ? 1.0 : 0.0; - while(f == 0.0) { - float dst2 = dot(worldPosition4f.xyz, worldPosition4f.xyz); - if(dst2 > 16.0) { - break; - } - vec3 reflectDir = reflect(worldDirection4f.xyz, normalVector3f); - reflectDir.xz /= abs(reflectDir.y) + 1.0; - float dst = 1.0 - dot(reflectDir.xz, reflectDir.xz); - dst *= dst; - reflectDir.xz = reflectDir.xz * 0.975; - vec4 envMapSample4f; - if(dst < 0.005) { - vec4 sample1 = textureLod(u_environmentMap, reflectDir.xz * vec2(0.5, 0.25) + vec2(0.5, 0.25), 0.0); - vec4 sample2 = textureLod(u_environmentMap, reflectDir.xz * vec2(0.5, -0.25) + vec2(0.5, 0.75), 0.0); - envMapSample4f = vec4(mix(sample1.rgb, sample2.rgb, smoothstep(0.0, 1.0, reflectDir.y * -12.5 + 0.5)).rgb, min(sample1.a, sample2.a)); - }else { - reflectDir.xz = reflectDir.xz * vec2(0.5, reflectDir.y > 0.0 ? 0.25 : -0.25); - reflectDir.xz += vec2(0.5, reflectDir.y > 0.0 ? 0.25 : 0.75); - envMapSample4f = textureLod(u_environmentMap, reflectDir.xz, 0.0); - } - if(envMapSample4f.a > 0.0) { - lightColor3f += eaglercraftIBL_Specular(diffuseColor4f.rgb, envMapSample4f.rgb, worldDirection4f.xyz, normalVector3f, materialData3f, metalN, metalK) * (1.0 - sqrt(dst2) * 0.25); - } - break; - } - + + // =========== ENVIRONMENT MAP =========== // + + f = materialData3f.g < 0.06 ? 1.0 : 0.0; + f += materialData3f.r < 0.5 ? 1.0 : 0.0; + while(f == 0.0) { + float dst2 = dot(worldPosition4f.xyz, worldPosition4f.xyz); + if(dst2 > 16.0) { + break; + } + vec3 reflectDir = reflect(worldDirection4f.xyz, normalVector3f); + reflectDir.xz /= abs(reflectDir.y) + 1.0; + float dst = 1.0 - dot(reflectDir.xz, reflectDir.xz); + dst *= dst; + reflectDir.xz = reflectDir.xz * 0.975; + vec4 envMapSample4f; + if(dst < 0.005) { + vec4 sample1 = textureLod(u_environmentMap, reflectDir.xz * vec2(0.5, 0.25) + vec2(0.5, 0.25), 0.0); + vec4 sample2 = textureLod(u_environmentMap, reflectDir.xz * vec2(0.5, -0.25) + vec2(0.5, 0.75), 0.0); + envMapSample4f = vec4(mix(sample1.rgb, sample2.rgb, smoothstep(0.0, 1.0, reflectDir.y * -12.5 + 0.5)).rgb, min(sample1.a, sample2.a)); + }else { + reflectDir.xz = reflectDir.xz * vec2(0.5, reflectDir.y > 0.0 ? 0.25 : -0.25); + reflectDir.xz += vec2(0.5, reflectDir.y > 0.0 ? 0.25 : 0.75); + envMapSample4f = textureLod(u_environmentMap, reflectDir.xz, 0.0); + } + if(envMapSample4f.a > 0.0) { + lightColor3f += eaglercraftIBL_Specular(diffuseColor4f.rgb, envMapSample4f.rgb, worldDirection4f.xyz, normalVector3f, materialData3f, metalN, metalK) * (1.0 - sqrt(dst2) * 0.25); + } + break; + } + #endif - - // =========== IRRADIANCE MAP =========== // - - lightmapCoords2f *= lightmapCoords2f; - - vec3 irradianceMapSamplePos2f = normalVector3f; - irradianceMapSamplePos2f.xz /= abs(irradianceMapSamplePos2f.y) + 1.0; - float dst = 1.0 - dot(irradianceMapSamplePos2f.xz, irradianceMapSamplePos2f.xz); - dst *= dst; - irradianceMapSamplePos2f.xz *= 0.975; - vec3 skyLight = vec3(sqrt(0.01 + max(u_sunDirection4f.w, 0.0))); - if(dst < 0.005) { - vec4 sample1 = textureLod(u_irradianceMap, irradianceMapSamplePos2f.xz * vec2(0.5, 0.25) + vec2(0.5, 0.25), 0.0); - vec4 sample2 = textureLod(u_irradianceMap, irradianceMapSamplePos2f.xz * vec2(0.5, -0.25) + vec2(0.5, 0.75), 0.0); - skyLight += mix(sample1.rgb, sample2.rgb, smoothstep(0.0, 1.0, irradianceMapSamplePos2f.y * -12.5 + 0.5)).rgb; - }else { - irradianceMapSamplePos2f.xz *= vec2(0.5, irradianceMapSamplePos2f.y > 0.0 ? 0.25 : -0.25); - irradianceMapSamplePos2f.xz += vec2(0.5, irradianceMapSamplePos2f.y > 0.0 ? 0.25 : 0.75); - skyLight += textureLod(u_irradianceMap, irradianceMapSamplePos2f.xz, 0.0).rgb; - } - skyLight *= lightmapCoords2f.g * u_sunColor3f_sky1f.w; - + + // =========== IRRADIANCE MAP =========== // + + lightmapCoords2f *= lightmapCoords2f; + + vec3 irradianceMapSamplePos2f = normalVector3f; + irradianceMapSamplePos2f.xz /= abs(irradianceMapSamplePos2f.y) + 1.0; + float dst = 1.0 - dot(irradianceMapSamplePos2f.xz, irradianceMapSamplePos2f.xz); + dst *= dst; + irradianceMapSamplePos2f.xz *= 0.975; + vec3 skyLight = vec3(sqrt(0.01 + max(u_sunDirection4f.w, 0.0))); + if(dst < 0.005) { + vec4 sample1 = textureLod(u_irradianceMap, irradianceMapSamplePos2f.xz * vec2(0.5, 0.25) + vec2(0.5, 0.25), 0.0); + vec4 sample2 = textureLod(u_irradianceMap, irradianceMapSamplePos2f.xz * vec2(0.5, -0.25) + vec2(0.5, 0.75), 0.0); + skyLight += mix(sample1.rgb, sample2.rgb, smoothstep(0.0, 1.0, irradianceMapSamplePos2f.y * -12.5 + 0.5)).rgb; + }else { + irradianceMapSamplePos2f.xz *= vec2(0.5, irradianceMapSamplePos2f.y > 0.0 ? 0.25 : -0.25); + irradianceMapSamplePos2f.xz += vec2(0.5, irradianceMapSamplePos2f.y > 0.0 ? 0.25 : 0.75); + skyLight += textureLod(u_irradianceMap, irradianceMapSamplePos2f.xz, 0.0).rgb; + } + skyLight *= lightmapCoords2f.g * u_sunColor3f_sky1f.w; + #ifdef COMPILE_DYNAMIC_LIGHTS - - // =========== DYNAMIC LIGHTING =========== // - - vec3 dlightDist3f, dlightDir3f, dlightColor3f; - int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; // hate this - for(int i = 0; i < safeLightCount; ++i) { - dlightDist3f = worldPosition4f.xyz - u_dynamicLightArray[i].u_lightPosition4f.xyz; - dlightDir3f = normalize(dlightDist3f); - dlightDir3f = materialData3f.b == 1.0 ? normalVector3f : -dlightDir3f; - if(dot(dlightDir3f, normalVector3f) <= 0.0) { - continue; - } - dlightColor3f = u_dynamicLightArray[i].u_lightColor4f.rgb / dot(dlightDist3f, dlightDist3f); - if(dlightColor3f.r + dlightColor3f.g + dlightColor3f.b < 0.025) { - continue; - } - lightColor3f += eaglercraftLighting(diffuseColor4f.rgb, dlightColor3f, -worldDirection4f.xyz, dlightDir3f, normalVector3f, materialData3f, metalN, metalK) * u_blockSkySunDynamicLightFac4f.w; - } - + + // =========== DYNAMIC LIGHTING =========== // + + vec3 dlightDist3f, dlightDir3f, dlightColor3f; + int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; // hate this + for(int i = 0; i < safeLightCount; ++i) { + dlightDist3f = worldPosition4f.xyz - u_dynamicLightArray[i].u_lightPosition4f.xyz; + dlightDir3f = normalize(dlightDist3f); + dlightDir3f = materialData3f.b == 1.0 ? normalVector3f : -dlightDir3f; + if(dot(dlightDir3f, normalVector3f) <= 0.0) { + continue; + } + dlightColor3f = u_dynamicLightArray[i].u_lightColor4f.rgb / dot(dlightDist3f, dlightDist3f); + if(dlightColor3f.r + dlightColor3f.g + dlightColor3f.b < 0.025) { + continue; + } + lightColor3f += eaglercraftLighting(diffuseColor4f.rgb, dlightColor3f, -worldDirection4f.xyz, dlightDir3f, normalVector3f, materialData3f, metalN, metalK) * u_blockSkySunDynamicLightFac4f.w; + } + #endif - - // ============ CACLULATE FOG ============= // - - vec4 fogBlend4f = vec4(0.0); + + // ============ CACLULATE FOG ============= // + + vec4 fogBlend4f = vec4(0.0); #ifndef COMPILE_ENABLE_TEX_GEN - while(u_fogParameters4f.x > 0.0) { - float atmos = u_fogParameters4f.x >= 4.0 ? 4.0 : 0.0; - float type = u_fogParameters4f.x - atmos; - fogBlend4f = mix(u_fogColorLight4f, u_fogColorDark4f, lightmapCoords2f.g); - - float l = length(v_position4f.xyz); - if(type == 1.0) { - f = (l - u_fogParameters4f.z) / (u_fogParameters4f.w - u_fogParameters4f.z); - }else { - f = 1.0 - exp(-u_fogParameters4f.y * l); - } - - fogBlend4f.a *= clamp(f, 0.0, 1.0); - - if(atmos == 0.0) { - break; - } - - vec3 atmosSamplePos = v_position4f.xyz / -l; - atmosSamplePos.xz /= abs(atmosSamplePos.y) + 1.0; - atmosSamplePos.xz *= vec2(-0.5, -0.25) * 0.75; - atmosSamplePos.xz += vec2(0.5, 0.25); - - fogBlend4f.rgb *= textureLod(u_irradianceMap, atmosSamplePos.xz, 0.0).rgb; - + while(u_fogParameters4f.x > 0.0) { + float atmos = u_fogParameters4f.x >= 4.0 ? 4.0 : 0.0; + float type = u_fogParameters4f.x - atmos; + fogBlend4f = mix(u_fogColorLight4f, u_fogColorDark4f, lightmapCoords2f.g); + + float l = length(v_position4f.xyz); + if(type == 1.0) { + f = (l - u_fogParameters4f.z) / (u_fogParameters4f.w - u_fogParameters4f.z); + }else { + f = 1.0 - exp(-u_fogParameters4f.y * l); + } + + fogBlend4f.a *= clamp(f, 0.0, 1.0); + + if(atmos == 0.0) { + break; + } + + vec3 atmosSamplePos = v_position4f.xyz / -l; + atmosSamplePos.xz /= abs(atmosSamplePos.y) + 1.0; + atmosSamplePos.xz *= vec2(-0.5, -0.25) * 0.75; + atmosSamplePos.xz += vec2(0.5, 0.25); + + fogBlend4f.rgb *= textureLod(u_irradianceMap, atmosSamplePos.xz, 0.0).rgb; + #ifdef COMPILE_FOG_LIGHT_SHAFTS - fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, v_positionClip2f * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25); - fogBlend4f.a = fogBlend4f.a * 0.9 + 0.1; + fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, v_positionClip2f * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25); + fogBlend4f.a = fogBlend4f.a * 0.9 + 0.1; #endif - break; - } + break; + } #endif - - // ============ OUTPUT COLOR ============== // - - vec3 blockLight = lightmapCoords2f.r * vec3(1.0, 0.5809, 0.2433) * 2.0 * u_blockSkySunDynamicLightFac4f.x; - skyLight *= u_blockSkySunDynamicLightFac4f.y; - float emissive = materialData3f.b == 1.0 ? 0.0 : materialData3f.b; - diffuseColor4f.rgb *= max(skyLight + blockLight, vec3(emissive * emissive * 20.0 + 0.075)) * 0.075; - diffuseColor4f.rgb += lightColor3f; - - diffuseColor4f.rgb = mix(diffuseColor4f.rgb, fogBlend4f.rgb, fogBlend4f.a); - - output4f = vec4(diffuseColor4f.rgb * diffuseColor4f.a, diffuseColor4f.a); + + // ============ OUTPUT COLOR ============== // + + vec3 blockLight = lightmapCoords2f.r * vec3(1.0, 0.5809, 0.2433) * 2.0 * u_blockSkySunDynamicLightFac4f.x; + skyLight *= u_blockSkySunDynamicLightFac4f.y; + float emissive = materialData3f.b == 1.0 ? 0.0 : materialData3f.b; + diffuseColor4f.rgb *= max(skyLight + blockLight, vec3(emissive * emissive * 20.0 + 0.075)) * 0.075; + diffuseColor4f.rgb += lightColor3f; + + diffuseColor4f.rgb = mix(diffuseColor4f.rgb, fogBlend4f.rgb, fogBlend4f.a); + + output4f = vec4(diffuseColor4f.rgb * diffuseColor4f.a, diffuseColor4f.a); } diff --git a/resources/resources/assets/eagler/glsl/deferred/reproject_control.fsh b/resources/resources/assets/eagler/glsl/deferred/reproject_control.fsh index a7a2c92..ff0a9f4 100644 --- a/resources/resources/assets/eagler/glsl/deferred/reproject_control.fsh +++ b/resources/resources/assets/eagler/glsl/deferred/reproject_control.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 lax1dude. All Rights Reserved. +* Copyright (c) 2023 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -91,148 +91,148 @@ uniform vec4 u_pixelAlignment4f; #define CREATE_DEPTH_MATRIX(matrix4fInput) mat4x2(matrix4fInput[0].zw,matrix4fInput[1].zw,matrix4fInput[2].zw,matrix4fInput[3].zw) void main() { - vec2 v_position2f2 = (floor(v_position2f * u_pixelAlignment4f.xy) + 0.25) * (2.0 / u_pixelAlignment4f.zw); + vec2 v_position2f2 = (floor(v_position2f * u_pixelAlignment4f.xy) + 0.25) * (2.0 / u_pixelAlignment4f.zw); #ifdef COMPILE_REPROJECT_SSAO - reprojectionSSAOOutput4f = vec4(0.0, 1.0, 0.0, 0.0); + reprojectionSSAOOutput4f = vec4(0.0, 1.0, 0.0, 0.0); #endif #ifdef COMPILE_REPROJECT_SSR - reprojectionReflectionOutput4f = vec4(0.0, 0.0, 0.0, 0.0); - reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, 0.0); + reprojectionReflectionOutput4f = vec4(0.0, 0.0, 0.0, 0.0); + reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, 0.0); #endif - float fragDepth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; - - if(fragDepth < 0.000001) { - return; - } - - vec4 fragClipSpacePos4f = vec4(v_position2f, fragDepth, 1.0) * 2.0 - 1.0; - vec4 fragPos4f = u_inverseViewProjMatrix4f * fragClipSpacePos4f; - fragPos4f.xyz /= fragPos4f.w; - fragPos4f.w = 1.0; - vec4 reprojPos4f = u_reprojectionMatrix4f * fragPos4f; - vec4 reprojClipPos4f = vec4(reprojPos4f.xyz / reprojPos4f.w, 1.0); - reprojPos4f = reprojClipPos4f; - reprojPos4f.xyz *= 0.5; - reprojPos4f.xyz += 0.5; - reprojPos4f.xy = (floor(reprojPos4f.xy * u_pixelAlignment4f.zw) + 0.5) * (0.5 / u_pixelAlignment4f.xy); - if(reprojPos4f.xy != clamp(reprojPos4f.xy, vec2(0.001), vec2(0.999)) || abs(GET_LINEAR_DEPTH_FROM_VALUE(textureLod(u_reprojectionDepthTexture, reprojPos4f.xy, 0.0).r) - GET_LINEAR_DEPTH_FROM_VALUE(reprojPos4f.z)) > reprojDepthLimit) { + float fragDepth = textureLod(u_gbufferDepthTexture, v_position2f2, 0.0).r; + + if(fragDepth < 0.000001) { + return; + } + + vec4 fragClipSpacePos4f = vec4(v_position2f2, fragDepth, 1.0) * 2.0 - 1.0; + vec4 fragPos4f = u_inverseViewProjMatrix4f * fragClipSpacePos4f; + fragPos4f.xyz /= fragPos4f.w; + fragPos4f.w = 1.0; + vec4 reprojPos4f = u_reprojectionMatrix4f * fragPos4f; + vec4 reprojClipPos4f = vec4(reprojPos4f.xyz / reprojPos4f.w, 1.0); + reprojPos4f = reprojClipPos4f; + reprojPos4f.xyz *= 0.5; + reprojPos4f.xyz += 0.5; + reprojPos4f.xy = (floor(reprojPos4f.xy * u_pixelAlignment4f.zw) + 0.5) * (0.5 / u_pixelAlignment4f.xy); + if(reprojPos4f.xy != clamp(reprojPos4f.xy, vec2(0.001), vec2(0.999)) || abs(GET_LINEAR_DEPTH_FROM_VALUE(textureLod(u_reprojectionDepthTexture, reprojPos4f.xy, 0.0).r) - GET_LINEAR_DEPTH_FROM_VALUE(reprojPos4f.z)) > reprojDepthLimit) { #ifdef COMPILE_REPROJECT_SSAO - reprojectionSSAOOutput4f = vec4(0.01, textureLod(u_ssaoSampleTexture, v_position2f, 0.0).r, 1.0, 0.0); + reprojectionSSAOOutput4f = vec4(0.01, textureLod(u_ssaoSampleTexture, v_position2f, 0.0).r, 1.0, 0.0); #endif #ifdef COMPILE_REPROJECT_SSR - reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, 50.0); + reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, 50.0); #endif - return; - } - + return; + } + #ifdef COMPILE_REPROJECT_SSAO - vec4 reprojectionSSAOInput4f = textureLod(u_reprojectionSSAOInput4f, reprojPos4f.xy, 0.0); - reprojectionSSAOInput4f.g = mix(textureLod(u_ssaoSampleTexture, v_position2f, 0.0).r, reprojectionSSAOInput4f.g, min(reprojectionSSAOInput4f.r * 10.0, 0.85)); - reprojectionSSAOInput4f.r = min(reprojectionSSAOInput4f.r + 0.01, 1.0); - reprojectionSSAOInput4f.b = 1.0; - reprojectionSSAOOutput4f = reprojectionSSAOInput4f; + vec4 reprojectionSSAOInput4f = textureLod(u_reprojectionSSAOInput4f, reprojPos4f.xy, 0.0); + reprojectionSSAOInput4f.g = mix(textureLod(u_ssaoSampleTexture, v_position2f, 0.0).r, reprojectionSSAOInput4f.g, min(reprojectionSSAOInput4f.r * 10.0, 0.85)); + reprojectionSSAOInput4f.r = min(reprojectionSSAOInput4f.r + 0.01, 1.0); + reprojectionSSAOInput4f.b = 1.0; + reprojectionSSAOOutput4f = reprojectionSSAOInput4f; #endif - + #ifdef COMPILE_REPROJECT_SSR - vec4 materials = textureLod(u_gbufferMaterialTexture, v_position2f2, 0.0); - float f = materials.g < 0.06 ? 1.0 : 0.0; - f += materials.r < 0.5 ? 1.0 : 0.0; - f += materials.a > 0.5 ? 1.0 : 0.0; - if(f > 0.0) { - return; - } - - vec4 lastFrameHitVector4f = textureLod(u_reprojectionHitVectorInput4f, reprojPos4f.xy, 0.0); - if(lastFrameHitVector4f.g <= 0.0) { - reprojectionReflectionOutput4f = textureLod(u_reprojectionReflectionInput4f, reprojPos4f.xy, 0.0); - reprojectionReflectionOutput4f.a = max(reprojectionReflectionOutput4f.a - 1.0, 1.0); - reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, lastFrameHitVector4f.a); - return; - } - - reprojectionReflectionOutput4f = vec4(0.0, 0.0, 0.0, 1.0); - - lastFrameHitVector4f.g -= 0.0004; - - vec4 lastFrameFragPosView4f = u_lastInverseProjMatrix4f * vec4(reprojClipPos4f.xyz, 1.0); - lastFrameFragPosView4f.xyz /= lastFrameFragPosView4f.w; - lastFrameFragPosView4f.w = 1.0; - vec4 lastFrameHitPos4f = vec4(lastFrameFragPosView4f.xyz + lastFrameHitVector4f.xyz, 1.0); - - vec4 thisFrameHitPos4f = u_reprojectionInverseViewMatrix4f * lastFrameHitPos4f; - thisFrameHitPos4f.xyz /= thisFrameHitPos4f.w; - thisFrameHitPos4f.w = 1.0; - - vec4 thisFrameHitPosProj4f = u_projectionMatrix4f * thisFrameHitPos4f; - thisFrameHitPosProj4f.xyz /= thisFrameHitPosProj4f.w; - thisFrameHitPosProj4f.w = 1.0; - vec3 thisFrameHitPosProjTex3f = thisFrameHitPosProj4f.xyz * 0.5 + 0.5; - - if(thisFrameHitPosProjTex3f.xy != clamp(thisFrameHitPosProjTex3f.xy, vec2(0.001), vec2(0.999))) { - return; - } - - float fragDepthSample = textureLod(u_gbufferDepthTexture, thisFrameHitPosProjTex3f.xy, 0.0).r * 2.0 - 1.0; - vec2 thisFrameHitPosProjDepthPos = CREATE_DEPTH_MATRIX(u_inverseProjectionMatrix4f) * vec4(thisFrameHitPosProj4f.xy, fragDepthSample, 1.0); - thisFrameHitPosProjDepthPos.x /= thisFrameHitPosProjDepthPos.y; - - if(thisFrameHitPosProjDepthPos.x - thisFrameHitPos4f.z - 0.125 < 0.0) { - return; - } - - vec3 lastFrameHitPosNormal3f = textureLod(u_gbufferNormalTexture, thisFrameHitPosProjTex3f.xy, 0.0).rgb; - lastFrameHitPosNormal3f *= 2.0; - lastFrameHitPosNormal3f -= 1.0; - - vec3 currentNormal3f = textureLod(u_gbufferNormalTexture, v_position2f2, 0.0).xyz; - currentNormal3f *= 2.0; - currentNormal3f -= 1.0; - - vec4 fragPosView4f = u_inverseProjectionMatrix4f * fragClipSpacePos4f; - fragPosView4f.xyz /= fragPosView4f.w; - fragPosView4f.w = 1.0; - - vec3 rayOrigin = fragPosView4f.xyz; - vec3 planePos = thisFrameHitPos4f.xyz; - vec3 planeNormal = lastFrameHitPosNormal3f; - - vec3 newRayDirection = reflect(normalize(rayOrigin), currentNormal3f.xyz); - - float dist = dot(planeNormal, newRayDirection); - if(dist > 0.9) { - return; - } - - dist = dot(planeNormal, planePos - rayOrigin) / dist; - if(dist < 0.0) { - return; - } - - reprojectionHitVectorOutput4f = vec4(newRayDirection * dist, 1.0); - reprojectionHitVectorOutput4f.y += 0.0004; - - thisFrameHitPosProj4f = u_viewToPreviousProjMatrix4f * vec4(rayOrigin + newRayDirection * dist, 1.0); - thisFrameHitPosProj4f.xyz /= thisFrameHitPosProj4f.w; - thisFrameHitPosProj4f.w = 1.0; - thisFrameHitPosProjTex3f = thisFrameHitPosProj4f.xyz * 0.5 + 0.5; - - if(thisFrameHitPosProjTex3f.xy != clamp(thisFrameHitPosProjTex3f.xy, vec2(0.001), vec2(0.999))) { - return; - } - - fragDepthSample = textureLod(u_reprojectionDepthTexture, thisFrameHitPosProjTex3f.xy, 0.0).r * 2.0 - 1.0; - - vec2 thisFrameHitPosProjPos = CREATE_DEPTH_MATRIX(u_lastInverseProjMatrix4f) * thisFrameHitPosProj4f; - thisFrameHitPosProjPos.x /= thisFrameHitPosProjPos.y; - - thisFrameHitPosProjDepthPos = CREATE_DEPTH_MATRIX(u_lastInverseProjMatrix4f) * vec4(thisFrameHitPosProj4f.xy, fragDepthSample, 1.0); - thisFrameHitPosProjDepthPos.x /= thisFrameHitPosProjDepthPos.y; - - if(thisFrameHitPosProjDepthPos.x - thisFrameHitPosProjPos.x - 0.125 < 0.0) { - reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, 0.0); - return; - } - - reprojectionReflectionOutput4f = vec4(textureLod(u_lastFrameColorInput4f, thisFrameHitPosProjTex3f.xy, 0.0).rgb, 0.0); + vec4 materials = textureLod(u_gbufferMaterialTexture, v_position2f2, 0.0); + float f = materials.g < 0.06 ? 1.0 : 0.0; + f += materials.r < 0.5 ? 1.0 : 0.0; + f += materials.a > 0.5 ? 1.0 : 0.0; + if(f > 0.0) { + return; + } + + vec4 lastFrameHitVector4f = textureLod(u_reprojectionHitVectorInput4f, reprojPos4f.xy, 0.0); + if(lastFrameHitVector4f.g <= 0.0) { + reprojectionReflectionOutput4f = textureLod(u_reprojectionReflectionInput4f, reprojPos4f.xy, 0.0); + reprojectionReflectionOutput4f.a = max(reprojectionReflectionOutput4f.a - 1.0, 1.0); + reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, lastFrameHitVector4f.a); + return; + } + + reprojectionReflectionOutput4f = vec4(0.0, 0.0, 0.0, 1.0); + + lastFrameHitVector4f.g -= 0.0004; + + vec4 lastFrameFragPosView4f = u_lastInverseProjMatrix4f * vec4(reprojClipPos4f.xyz, 1.0); + lastFrameFragPosView4f.xyz /= lastFrameFragPosView4f.w; + lastFrameFragPosView4f.w = 1.0; + vec4 lastFrameHitPos4f = vec4(lastFrameFragPosView4f.xyz + lastFrameHitVector4f.xyz, 1.0); + + vec4 thisFrameHitPos4f = u_reprojectionInverseViewMatrix4f * lastFrameHitPos4f; + thisFrameHitPos4f.xyz /= thisFrameHitPos4f.w; + thisFrameHitPos4f.w = 1.0; + + vec4 thisFrameHitPosProj4f = u_projectionMatrix4f * thisFrameHitPos4f; + thisFrameHitPosProj4f.xyz /= thisFrameHitPosProj4f.w; + thisFrameHitPosProj4f.w = 1.0; + vec3 thisFrameHitPosProjTex3f = thisFrameHitPosProj4f.xyz * 0.5 + 0.5; + + if(thisFrameHitPosProjTex3f.xy != clamp(thisFrameHitPosProjTex3f.xy, vec2(0.001), vec2(0.999))) { + return; + } + + float fragDepthSample = textureLod(u_gbufferDepthTexture, thisFrameHitPosProjTex3f.xy, 0.0).r * 2.0 - 1.0; + vec2 thisFrameHitPosProjDepthPos = CREATE_DEPTH_MATRIX(u_inverseProjectionMatrix4f) * vec4(thisFrameHitPosProj4f.xy, fragDepthSample, 1.0); + thisFrameHitPosProjDepthPos.x /= thisFrameHitPosProjDepthPos.y; + + if(thisFrameHitPosProjDepthPos.x - thisFrameHitPos4f.z - 0.125 < 0.0) { + return; + } + + vec3 lastFrameHitPosNormal3f = textureLod(u_gbufferNormalTexture, thisFrameHitPosProjTex3f.xy, 0.0).rgb; + lastFrameHitPosNormal3f *= 2.0; + lastFrameHitPosNormal3f -= 1.0; + + vec3 currentNormal3f = textureLod(u_gbufferNormalTexture, v_position2f2, 0.0).xyz; + currentNormal3f *= 2.0; + currentNormal3f -= 1.0; + + vec4 fragPosView4f = u_inverseProjectionMatrix4f * fragClipSpacePos4f; + fragPosView4f.xyz /= fragPosView4f.w; + fragPosView4f.w = 1.0; + + vec3 rayOrigin = fragPosView4f.xyz; + vec3 planePos = thisFrameHitPos4f.xyz; + vec3 planeNormal = lastFrameHitPosNormal3f; + + vec3 newRayDirection = reflect(normalize(rayOrigin), currentNormal3f.xyz); + + float dist = dot(planeNormal, newRayDirection); + if(dist > 0.9) { + return; + } + + dist = dot(planeNormal, planePos - rayOrigin) / dist; + if(dist < 0.0) { + return; + } + + reprojectionHitVectorOutput4f = vec4(newRayDirection * dist, 1.0); + reprojectionHitVectorOutput4f.y += 0.0004; + + thisFrameHitPosProj4f = u_viewToPreviousProjMatrix4f * vec4(rayOrigin + newRayDirection * dist, 1.0); + thisFrameHitPosProj4f.xyz /= thisFrameHitPosProj4f.w; + thisFrameHitPosProj4f.w = 1.0; + thisFrameHitPosProjTex3f = thisFrameHitPosProj4f.xyz * 0.5 + 0.5; + + if(thisFrameHitPosProjTex3f.xy != clamp(thisFrameHitPosProjTex3f.xy, vec2(0.001), vec2(0.999))) { + return; + } + + fragDepthSample = textureLod(u_reprojectionDepthTexture, thisFrameHitPosProjTex3f.xy, 0.0).r * 2.0 - 1.0; + + vec2 thisFrameHitPosProjPos = CREATE_DEPTH_MATRIX(u_lastInverseProjMatrix4f) * thisFrameHitPosProj4f; + thisFrameHitPosProjPos.x /= thisFrameHitPosProjPos.y; + + thisFrameHitPosProjDepthPos = CREATE_DEPTH_MATRIX(u_lastInverseProjMatrix4f) * vec4(thisFrameHitPosProj4f.xy, fragDepthSample, 1.0); + thisFrameHitPosProjDepthPos.x /= thisFrameHitPosProjDepthPos.y; + + if(thisFrameHitPosProjDepthPos.x - thisFrameHitPosProjPos.x - 0.125 < 0.0) { + reprojectionHitVectorOutput4f = vec4(0.0, 0.0, 0.0, 0.0); + return; + } + + reprojectionReflectionOutput4f = vec4(textureLod(u_lastFrameColorInput4f, thisFrameHitPosProjTex3f.xy, 0.0).rgb, 0.0); #endif } diff --git a/resources/resources/assets/eagler/glsl/deferred/shader_pack_info.json b/resources/resources/assets/eagler/glsl/deferred/shader_pack_info.json index af5f511..9cf966a 100644 --- a/resources/resources/assets/eagler/glsl/deferred/shader_pack_info.json +++ b/resources/resources/assets/eagler/glsl/deferred/shader_pack_info.json @@ -1,7 +1,7 @@ { "name": "§eHigh Performance PBR", "desc": "Pack made from scratch specifically for this client, designed to give what I call the best balance between quality and performance possible in a browser but obviously that's just my opinion", - "vers": "1.2.1", + "vers": "1.2.2", "author": "lax1dude", "api_vers": 1, "features": [ diff --git a/resources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh b/resources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh index f2b310a..5c0c215 100644 --- a/resources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh +++ b/resources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 lax1dude. All Rights Reserved. +* Copyright (c) 2023 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -34,65 +34,65 @@ uniform mat4 u_inverseProjectionMatrix4f; uniform mat2 u_randomizerDataMatrix2f; const vec3 ssaoKernel[8] = vec3[]( -vec3(0.599,0.721,0.350),vec3(0.114,0.791,0.601), -vec3(0.067,0.995,0.069),vec3(0.511,-0.510,0.692), -vec3(0.626,-0.667,0.404),vec3(0.896,-0.169,0.411), -vec3(0.716,-0.439,0.543),vec3(-0.400,0.733,0.550)); + vec3(0.599,0.721,0.350),vec3(0.114,0.791,0.601), + vec3(0.067,0.995,0.069),vec3(0.511,-0.510,0.692), + vec3(0.626,-0.667,0.404),vec3(0.896,-0.169,0.411), + vec3(0.716,-0.439,0.543),vec3(-0.400,0.733,0.550)); #define radius 1.5 #define SAMPLE_SSAO(idx, pos, matTBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2)\ - tmpVec4_1.xyz = pos + (matTBN * ssaoKernel[idx]) * radius;\ - tmpVec4_1.w = 1.0;\ - tmpVec4_2 = u_projectionMatrix4f * tmpVec4_1;\ - tmpVec4_2.xyz /= tmpVec4_2.w;\ - tmpVec4_2.xyz = clamp(tmpVec4_2.xyz, -0.99, 0.99);\ - tmpVec4_2.zw = matProjInv2f * vec4(tmpVec4_2.xy, textureLod(u_gbufferDepthTexture, tmpVec4_2.xy * 0.5 + 0.5, 0.0).r * 2.0 - 1.0, 1.0);\ - tmpVec4_2.z /= tmpVec4_2.w;\ - tmpVec4_2.x = smoothstep(0.0, 1.0, radius * 0.5 / abs(pos.z - tmpVec4_2.z));\ - divisor += tmpVec4_2.x > 0.0 ? 1.0 : 0.0;\ - occlusion += (tmpVec4_2.z >= tmpVec4_1.z ? 1.0 : 0.0) * tmpVec4_2.x; +tmpVec4_1.xyz = pos + (matTBN * ssaoKernel[idx]) * radius;\ +tmpVec4_1.w = 1.0;\ +tmpVec4_2 = u_projectionMatrix4f * tmpVec4_1;\ +tmpVec4_2.xyz /= tmpVec4_2.w;\ +tmpVec4_2.xyz = clamp(tmpVec4_2.xyz, -0.99, 0.99);\ +tmpVec4_2.zw = matProjInv2f * vec4(tmpVec4_2.xy, textureLod(u_gbufferDepthTexture, tmpVec4_2.xy * 0.5 + 0.5, 0.0).r * 2.0 - 1.0, 1.0);\ +tmpVec4_2.z /= tmpVec4_2.w;\ +tmpVec4_2.x = smoothstep(0.0, 1.0, radius * 0.5 / abs(pos.z - tmpVec4_2.z));\ +divisor += tmpVec4_2.x > 0.0 ? 1.0 : 0.0;\ +occlusion += (tmpVec4_2.z >= tmpVec4_1.z ? 1.0 : 0.0) * tmpVec4_2.x; void main() { - vec3 originalClipSpacePos = vec3(v_position2f, textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r); - - if(originalClipSpacePos.z <= 0.0000001) { - output1f = 1.0; - return; - } - - originalClipSpacePos *= 2.0; - originalClipSpacePos -= 1.0; - - vec3 normal3f = textureLod(u_gbufferNormalTexture, v_position2f, 0.0).rgb; - normal3f *= 2.0; - normal3f -= 1.0; - - vec4 originalViewSpacePos = u_inverseProjectionMatrix4f * vec4(originalClipSpacePos, 1.0); - originalViewSpacePos.xyz /= originalViewSpacePos.w; - originalViewSpacePos.w = 1.0; - - vec4 noiseVec = textureLod(u_noiseConstantTexture, u_randomizerDataMatrix2f * (v_position2f + originalViewSpacePos.xy + normal3f.xz), 0.0); - noiseVec.xyz *= 2.0; - noiseVec.xyz -= 1.0; - - vec3 tangent = normalize(noiseVec.xyz - normal3f * dot(noiseVec.xyz, normal3f)); - vec3 bitangent = cross(normal3f, tangent); - mat3 TBN = mat3(tangent, bitangent, normal3f) * noiseVec.w; - - float divisor = 0.0; - float occlusion = 0.0; - vec4 tmpVec4_1; - vec4 tmpVec4_2; - - mat4x2 matProjInv2f = mat4x2(u_inverseProjectionMatrix4f[0].zw, u_inverseProjectionMatrix4f[1].zw, u_inverseProjectionMatrix4f[2].zw, u_inverseProjectionMatrix4f[3].zw); - - SAMPLE_SSAO(0, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(1, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(2, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(3, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(4, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(5, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(6, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - SAMPLE_SSAO(7, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) - - output1f = max(1.0 - (occlusion / max(divisor, 0.001)), 0.0); + vec3 originalClipSpacePos = vec3(v_position2f, textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r); + + if(originalClipSpacePos.z <= 0.0000001) { + output1f = 1.0; + return; + } + + originalClipSpacePos *= 2.0; + originalClipSpacePos -= 1.0; + + vec3 normal3f = textureLod(u_gbufferNormalTexture, v_position2f, 0.0).rgb; + normal3f *= 2.0; + normal3f -= 1.0; + + vec4 originalViewSpacePos = u_inverseProjectionMatrix4f * vec4(originalClipSpacePos, 1.0); + originalViewSpacePos.xyz /= originalViewSpacePos.w; + originalViewSpacePos.w = 1.0; + + vec4 noiseVec = textureLod(u_noiseConstantTexture, 13.3725 / fract((u_randomizerDataMatrix2f * (v_position2f * 0.42695346 + originalViewSpacePos.xy * 1.373769945645 + normal3f.xz * 42.69456453)) * 1.123234234), 0.0); + noiseVec.xyz *= 2.0; + noiseVec.xyz -= 1.0; + + vec3 tangent = normalize(noiseVec.xyz - normal3f * dot(noiseVec.xyz, normal3f)); + vec3 bitangent = cross(normal3f, tangent); + mat3 TBN = mat3(tangent, bitangent, normal3f) * noiseVec.w; + + float divisor = 0.0; + float occlusion = 0.0; + vec4 tmpVec4_1; + vec4 tmpVec4_2; + + mat4x2 matProjInv2f = mat4x2(u_inverseProjectionMatrix4f[0].zw, u_inverseProjectionMatrix4f[1].zw, u_inverseProjectionMatrix4f[2].zw, u_inverseProjectionMatrix4f[3].zw); + + SAMPLE_SSAO(0, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(1, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(2, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(3, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(4, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(5, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(6, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + SAMPLE_SSAO(7, originalViewSpacePos.xyz, TBN, matProjInv2f, divisor, occlusion, tmpVec4_1, tmpVec4_2) + + output1f = max(1.0 - (occlusion / max(divisor, 0.001)), 0.0); } diff --git a/resources/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh b/resources/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh index aa3bc51..01dc545 100644 --- a/resources/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh +++ b/resources/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -91,128 +91,127 @@ uniform vec2 u_textureAnisotropicFix; uniform mat4 u_inverseViewMatrix4f; layout(std140) uniform u_chunkLightingData { - mediump int u_dynamicLightCount1i; - mediump int _paddingA_; - mediump int _paddingB_; - mediump int _paddingC_; - mediump vec4 u_dynamicLightArray[12]; + mediump int u_dynamicLightCount1i; + mediump int _paddingA_; + mediump int _paddingB_; + mediump int _paddingC_; + mediump vec4 u_dynamicLightArray[12]; }; layout(location = 0) out vec4 output4f; void main() { - #ifdef COMPILE_COLOR_ATTRIB - vec4 color = v_color4f * u_color4f; + vec4 color = v_color4f * u_color4f; #else - vec4 color = u_color4f; + vec4 color = u_color4f; #endif - + #ifdef COMPILE_ENABLE_TEX_GEN - vec4 texGenVector; - - vec4 texGenPosSrc[2]; - texGenPosSrc[0] = vec4(v_objectPosition3f, 1.0); - texGenPosSrc[1] = v_position4f; - - texGenVector.x = dot(texGenPosSrc[u_texGenPlane4i.x], u_texGenS4f); - texGenVector.y = dot(texGenPosSrc[u_texGenPlane4i.y], u_texGenT4f); - texGenVector.z = dot(texGenPosSrc[u_texGenPlane4i.z], u_texGenR4f); - texGenVector.w = dot(texGenPosSrc[u_texGenPlane4i.w], u_texGenQ4f); - - texGenVector = u_textureMat4f01 * texGenVector; - color *= texture(u_samplerTexture, texGenVector.xy / texGenVector.w); - + vec4 tmpVec4 = vec4(v_objectPosition3f, 1.0); + vec4 texGenVector; + texGenVector.x = dot(u_texGenPlane4i.x == 1 ? v_position4f : tmpVec4, u_texGenS4f); + texGenVector.y = dot(u_texGenPlane4i.y == 1 ? v_position4f : tmpVec4, u_texGenT4f); + texGenVector.z = dot(u_texGenPlane4i.z == 1 ? v_position4f : tmpVec4, u_texGenR4f); + texGenVector.w = dot(u_texGenPlane4i.w == 1 ? v_position4f : tmpVec4, u_texGenQ4f); + texGenVector.xyz = mat4x3( + u_textureMat4f01[0].xyw, + u_textureMat4f01[1].xyw, + u_textureMat4f01[2].xyw, + u_textureMat4f01[3].xyw) * texGenVector; + + color *= texture(u_samplerTexture, texGenVector.xy / texGenVector.z); + #ifdef COMPILE_ENABLE_ALPHA_TEST - if(color.a < u_alphaTestRef1f) discard; + if(color.a < u_alphaTestRef1f) discard; #endif - + #else - + #ifdef COMPILE_ENABLE_TEXTURE2D #ifdef COMPILE_TEXTURE_ATTRIB #ifdef COMPILE_ENABLE_ANISOTROPIC_FIX - // d3d11 doesn't support GL_NEAREST upscaling with anisotropic - // filtering enabled, so it needs this stupid fix to 'work' - vec2 uv = floor(v_texture2f * u_textureAnisotropicFix) + 0.5; - color *= texture(u_samplerTexture, uv / u_textureAnisotropicFix); + // d3d11 doesn't support GL_NEAREST upscaling with anisotropic + // filtering enabled, so it needs this stupid fix to 'work' + vec2 uv = floor(v_texture2f * u_textureAnisotropicFix) + 0.5; + color *= texture(u_samplerTexture, uv / u_textureAnisotropicFix); #else - color *= texture(u_samplerTexture, v_texture2f); + color *= texture(u_samplerTexture, v_texture2f); #endif #else - color *= texture(u_samplerTexture, u_textureCoords01); + color *= texture(u_samplerTexture, u_textureCoords01); #endif #endif - + #ifdef COMPILE_NORMAL_ATTRIB - vec3 normal = normalize(v_normal3f); + vec3 normal = normalize(v_normal3f); #else - vec3 normal = u_uniformNormal3f; + vec3 normal = u_uniformNormal3f; #endif - + #ifdef COMPILE_ENABLE_LIGHTMAP - float diffuse = 0.0; + float diffuse = 0.0; #ifdef COMPILE_LIGHTMAP_ATTRIB - float blockLight = v_lightmap2f.x; + float blockLight = v_lightmap2f.x; #else - float blockLight = u_textureCoords02.x; + float blockLight = u_textureCoords02.x; #endif - float len; - vec4 light; - if(u_dynamicLightCount1i > 0) { - vec4 worldPosition4f = u_inverseViewMatrix4f * v_position4f; - worldPosition4f.xyz /= worldPosition4f.w; - vec3 normalVector3f = normalize(mat3(u_inverseViewMatrix4f) * normal); - int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; - for(int i = 0; i < safeLightCount; ++i) { - light = u_dynamicLightArray[i]; - light.xyz = light.xyz - worldPosition4f.xyz; - len = length(light.xyz); - diffuse += max(dot(light.xyz / len, normalVector3f) * 0.8 + 0.2, 0.0) * max(light.w - len, 0.0); - } - blockLight = min(blockLight + diffuse * 0.066667, 1.0); - } + float len; + vec4 light; + if(u_dynamicLightCount1i > 0) { + vec4 worldPosition4f = u_inverseViewMatrix4f * v_position4f; + worldPosition4f.xyz /= worldPosition4f.w; + vec3 normalVector3f = normalize(mat3(u_inverseViewMatrix4f) * normal); + int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; + for(int i = 0; i < safeLightCount; ++i) { + light = u_dynamicLightArray[i]; + light.xyz = light.xyz - worldPosition4f.xyz; + len = length(light.xyz); + diffuse += max(dot(light.xyz / len, normalVector3f) * 0.8 + 0.2, 0.0) * max(light.w - len, 0.0); + } + blockLight = min(blockLight + diffuse * 0.066667, 1.0); + } #ifdef COMPILE_LIGHTMAP_ATTRIB - color *= texture(u_samplerLightmap, vec2(blockLight, v_lightmap2f.y)); + color *= texture(u_samplerLightmap, vec2(blockLight, v_lightmap2f.y)); #else - color *= texture(u_samplerLightmap, vec2(blockLight, u_textureCoords02.y)); + color *= texture(u_samplerLightmap, vec2(blockLight, u_textureCoords02.y)); #endif #endif - + #ifdef COMPILE_BLEND_ADD - color = color * u_colorBlendSrc4f + u_colorBlendAdd4f; + color = color * u_colorBlendSrc4f + u_colorBlendAdd4f; #endif - + #ifdef COMPILE_ENABLE_ALPHA_TEST - if(color.a < u_alphaTestRef1f) discard; + if(color.a < u_alphaTestRef1f) discard; #endif - + #endif - + #ifdef COMPILE_ENABLE_MC_LIGHTING #ifndef COMPILE_ENABLE_LIGHTMAP - vec4 light; - float diffuse = 0.0; + vec4 light; + float diffuse = 0.0; #else - diffuse = 0.0; + diffuse = 0.0; #endif - for(int i = 0; i < u_lightsEnabled1i; ++i) { - light = u_lightsDirections4fv[i]; - diffuse += max(dot(light.xyz, normal), 0.0) * light.w; - } - color.rgb *= min(u_lightsAmbient3f + vec3(diffuse), 1.0); + for(int i = 0; i < u_lightsEnabled1i; ++i) { + light = u_lightsDirections4fv[i]; + diffuse += max(dot(light.xyz, normal), 0.0) * light.w; + } + color.rgb *= min(u_lightsAmbient3f + vec3(diffuse), 1.0); #endif - + #ifdef COMPILE_ENABLE_FOG - vec3 fogPos = v_position4f.xyz / v_position4f.w; - float dist = sqrt(dot(fogPos, fogPos)); - float fogDensity = u_fogParameters4f.y; - float fogStart = u_fogParameters4f.z; - float fogEnd = u_fogParameters4f.w; - float f = u_fogParameters4f.x > 0.0 ? 1.0 - exp(-fogDensity * dist) : - (dist - fogStart) / (fogEnd - fogStart); - color.rgb = mix(color.rgb, u_fogColor4f.rgb, clamp(f, 0.0, 1.0) * u_fogColor4f.a); + vec3 fogPos = v_position4f.xyz / v_position4f.w; + float dist = sqrt(dot(fogPos, fogPos)); + float fogDensity = u_fogParameters4f.y; + float fogStart = u_fogParameters4f.z; + float fogEnd = u_fogParameters4f.w; + float f = u_fogParameters4f.x > 0.0 ? 1.0 - exp(-fogDensity * dist) : + (dist - fogStart) / (fogEnd - fogStart); + color.rgb = mix(color.rgb, u_fogColor4f.rgb, clamp(f, 0.0, 1.0) * u_fogColor4f.a); #endif - - output4f = color; + + output4f = color; } diff --git a/resources/resources/assets/eagler/glsl/gles2_compat.glsl b/resources/resources/assets/eagler/glsl/gles2_compat.glsl new file mode 100755 index 0000000..5529cbb --- /dev/null +++ b/resources/resources/assets/eagler/glsl/gles2_compat.glsl @@ -0,0 +1,98 @@ +#line 2 6969 + +/* + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef EAGLER_HAS_GLES_300 + +// For GLES 3.00+ (WebGL 2.0) +#ifdef EAGLER_IS_VERTEX_SHADER + +// Vertex Shaders: +#define EAGLER_VSH_LAYOUT_BEGIN() +#define EAGLER_VSH_LAYOUT_END() +#define EAGLER_IN(_loc, _type, _name) layout(location = _loc) in _type _name; +#define EAGLER_IN_AUTO(_type, _name) in _type _name; +#define EAGLER_OUT(_type, _name) out _type _name; +#define EAGLER_VERT_POSITION gl_Position + +#else +#ifdef EAGLER_IS_FRAGMENT_SHADER + +// Fragment Shaders: +#define EAGLER_IN(_type, _name) in _type _name; +#define EAGLER_FRAG_COLOR eagler_FragColor +#define EAGLER_FRAG_DEPTH gl_FragDepth + +#define EAGLER_FRAG_OUT() layout(location = 0) out vec4 EAGLER_FRAG_COLOR; + +#endif +#endif + +// All Shaders: + +#define EAGLER_TEXTURE_2D(tex, coord2f) texture(tex, coord2f) +#define EAGLER_TEXTURE_2D_LOD(_tex, _coord2f, _lod1f) textureLod(_tex, _coord2f, _lod1f) +#define EAGLER_HAS_TEXTURE_2D_LOD + + +#else +#ifdef EAGLER_HAS_GLES_200 + +// For GLES 2.00 (WebGL 1.0) +#ifdef EAGLER_IS_VERTEX_SHADER + +// Vertex Shaders: +#define EAGLER_VSH_LAYOUT_BEGIN() +#define EAGLER_VSH_LAYOUT_END() +#define EAGLER_IN(_loc, _type, _name) attribute _type _name; +#define EAGLER_IN_AUTO(_type, _name) attribute _type _name; +#define EAGLER_OUT(_type, _name) varying _type _name; +#define EAGLER_VERT_POSITION gl_Position + +#else +#ifdef EAGLER_IS_FRAGMENT_SHADER + +// Fragment Shaders: +#define EAGLER_IN(_type, _name) varying _type _name; +#define EAGLER_FRAG_COLOR gl_FragColor +// TODO: Must require EXT_frag_depth to use this on GLES 2.0 (currently not needed) +#define EAGLER_FRAG_DEPTH gl_FragDepth + +#define EAGLER_FRAG_OUT() + +#endif +#endif + +// All Shaders: + +#define EAGLER_TEXTURE_2D(_tex, _coord2f) texture2D(_tex, _coord2f) + +#ifdef EAGLER_HAS_GLES_200_SHADER_TEXTURE_LOD +#define EAGLER_TEXTURE_2D_LOD(_tex, _coord2f, _lod1f) texture2DLodEXT(_tex, _coord2f, _lod1f) +#define EAGLER_HAS_TEXTURE_2D_LOD +#else +// Beware! +#define EAGLER_TEXTURE_2D_LOD(_tex, _coord2f, _lod1f) texture2D(_tex, _coord2f) +#define EAGLER_HAS_TEXTURE_2D_LOD +#endif + +#else +#error Unable to determine API version! (Missing directive EAGLER_HAS_GLES_200 or 300) +#endif +#endif + +#line 1 0 \ No newline at end of file diff --git a/resources/resources/assets/eagler/glsl/hw_fingerprint.fsh b/resources/resources/assets/eagler/glsl/hw_fingerprint.fsh new file mode 100755 index 0000000..0c11ba8 --- /dev/null +++ b/resources/resources/assets/eagler/glsl/hw_fingerprint.fsh @@ -0,0 +1,55 @@ +#line 2 + +/* + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +EAGLER_IN(vec2, v_position2f) + +EAGLER_FRAG_OUT() + +uniform sampler2D u_inputTexture; +uniform mat4 u_textureMatrix; + +vec2 rand(in vec2 co){ + float f = dot(co, vec2(12.98984576, 78.23378678)); + return fract(vec2(sin(f + 0.32490982), cos(f - 0.69890)) * 43758.54576873); +} + +void main() { + vec4 coords4f = vec4(v_position2f.x * 0.25 - 0.125, v_position2f.y * 0.25 - 0.125, v_position2f.y * 10.0 - 9.0, 1.0); + coords4f = u_textureMatrix * coords4f; + coords4f.xy /= coords4f.w; + EAGLER_FRAG_COLOR = EAGLER_TEXTURE_2D(u_inputTexture, coords4f.xy * 0.5 + 0.5); + EAGLER_FRAG_COLOR.rg += rand(v_position2f * 1.2344574345) * 0.05; + EAGLER_FRAG_COLOR.ba -= rand(v_position2f * 1.2343525225) * 0.05; + EAGLER_FRAG_COLOR.a = fract(sin(dot(coords4f.yz, vec2(12.9898, 78.233))) * 43758.5453); + EAGLER_FRAG_COLOR.a += exp(length(rand(coords4f.xw)) * -69.420); + EAGLER_FRAG_COLOR = pow(EAGLER_FRAG_COLOR, vec4(1.0 / 2.423952)); + EAGLER_FRAG_COLOR = pow(EAGLER_FRAG_COLOR, vec4(5.4523856)); + EAGLER_FRAG_COLOR += 0.00004423 + EAGLER_FRAG_COLOR.a * 0.02; + EAGLER_FRAG_COLOR = sqrt(EAGLER_FRAG_COLOR); + EAGLER_FRAG_COLOR = pow(EAGLER_FRAG_COLOR, vec4(1.0 / 1.9023576)); +#ifdef EAGLER_HAS_GLES_300 + EAGLER_FRAG_COLOR.ra += tanh(fract(EAGLER_FRAG_COLOR.a * 32.324834)) * 0.1012426; +#endif + EAGLER_FRAG_COLOR.b *= 0.934924; + EAGLER_FRAG_COLOR.b += (1.23213 / inversesqrt(EAGLER_FRAG_COLOR.a)) * 0.156365; + EAGLER_FRAG_COLOR.ga += rand(gl_FragCoord.xy) * 0.13423567; + EAGLER_FRAG_COLOR.rb += gl_PointCoord * 0.0124264565; +#ifdef EAGLER_HAS_GLES_300 + EAGLER_FRAG_COLOR *= 0.95234 + asinh(EAGLER_FRAG_COLOR.g * 5.23423) * 0.0254325; +#endif +} diff --git a/resources/resources/assets/eagler/glsl/local.vsh b/resources/resources/assets/eagler/glsl/local.vsh index 4617298..20e4573 100644 --- a/resources/resources/assets/eagler/glsl/local.vsh +++ b/resources/resources/assets/eagler/glsl/local.vsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,15 +16,13 @@ * */ -precision lowp int; -precision highp float; -precision lowp sampler2D; +EAGLER_VSH_LAYOUT_BEGIN() +EAGLER_IN(0, vec2, a_position2f) +EAGLER_VSH_LAYOUT_END() -layout(location = 0) in vec2 a_position2f; - -out vec2 v_position2f; +EAGLER_OUT(vec2, v_position2f) void main() { - v_position2f = a_position2f * 0.5 + 0.5; - gl_Position = vec4(a_position2f, 0.0, 1.0); + v_position2f = a_position2f * 0.5 + 0.5; + EAGLER_VERT_POSITION = vec4(a_position2f, 0.0, 1.0); } diff --git a/resources/resources/assets/eagler/glsl/post_fxaa.fsh b/resources/resources/assets/eagler/glsl/post_fxaa.fsh index 9914f0a..66ec0e6 100644 --- a/resources/resources/assets/eagler/glsl/post_fxaa.fsh +++ b/resources/resources/assets/eagler/glsl/post_fxaa.fsh @@ -1,11 +1,12 @@ #line 2 // Remove this line below if you plan to modify this file +#ifndef EAGLER_IS_GLES_200 #define USE_OPTIMIZED - +#endif /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -20,17 +21,15 @@ * */ - /* - * This file was modified by lax1dude to remove dead code +* This file was modified by lax1dude to remove dead code * * Original: https://gist.github.com/kosua20/0c506b81b3812ac900048059d2383126 * */ - /* - * ============================================================================ +* ============================================================================ * * * NVIDIA FXAA 3.11 by TIMOTHY LOTTES @@ -52,78 +51,77 @@ * */ -precision lowp int; -precision mediump float; -precision mediump sampler2D; +EAGLER_IN(vec2, v_position2f) - -in vec2 v_position2f; - -layout(location = 0) out vec4 output4f; +EAGLER_FRAG_OUT() uniform sampler2D u_screenTexture; uniform vec2 u_screenSize2f; #ifndef USE_OPTIMIZED #ifndef FXAA_GREEN_AS_LUMA - // For those using non-linear color, - // and either not able to get luma in alpha, or not wanting to, - // this enables FXAA to run using green as a proxy for luma. - // So with this enabled, no need to pack luma in alpha. - // - // This will turn off AA on anything which lacks some amount of green. - // Pure red and blue or combination of only R and B, will get no AA. - // - // Might want to lower the settings for both, - // fxaaConsoleEdgeThresholdMin - // fxaaQualityEdgeThresholdMin - // In order to insure AA does not get turned off on colors - // which contain a minor amount of green. - // - // 1 = On. - // 0 = Off. - // - #define FXAA_GREEN_AS_LUMA 0 +// For those using non-linear color, +// and either not able to get luma in alpha, or not wanting to, +// this enables FXAA to run using green as a proxy for luma. +// So with this enabled, no need to pack luma in alpha. +// +// This will turn off AA on anything which lacks some amount of green. +// Pure red and blue or combination of only R and B, will get no AA. +// +// Might want to lower the settings for both, +// fxaaConsoleEdgeThresholdMin +// fxaaQualityEdgeThresholdMin +// In order to insure AA does not get turned off on colors +// which contain a minor amount of green. +// +// 1 = On. +// 0 = Off. +// +#define FXAA_GREEN_AS_LUMA 0 #endif #ifndef FXAA_DISCARD - // 1 = Use discard on pixels which don't need AA. - // 0 = Return unchanged color on pixels which don't need AA. - #define FXAA_DISCARD 0 +// 1 = Use discard on pixels which don't need AA. +// 0 = Return unchanged color on pixels which don't need AA. +#define FXAA_DISCARD 0 #endif /*============================================================================ - API PORTING -============================================================================*/ - #define FxaaBool bool - #define FxaaDiscard discard - #define FxaaFloat float - #define FxaaFloat2 vec2 - #define FxaaFloat3 vec3 - #define FxaaFloat4 vec4 - #define FxaaHalf float - #define FxaaHalf2 vec2 - #define FxaaHalf3 vec3 - #define FxaaHalf4 vec4 - #define FxaaInt2 ivec2 - #define FxaaSat(x) clamp(x, 0.0, 1.0) - #define FxaaTex sampler2D +API PORTING + ============================================================================*/ +#define FxaaBool bool +#define FxaaDiscard discard +#define FxaaFloat float +#define FxaaFloat2 vec2 +#define FxaaFloat3 vec3 +#define FxaaFloat4 vec4 +#define FxaaHalf float +#define FxaaHalf2 vec2 +#define FxaaHalf3 vec3 +#define FxaaHalf4 vec4 +#define FxaaInt2 ivec2 +#define FxaaSat(x) clamp(x, 0.0, 1.0) +#define FxaaTex sampler2D /*--------------------------------------------------------------------------*/ - #define FxaaTexTop(t, p) textureLod(t, p, 0.0) +#define FxaaTexTop(t, p) EAGLER_TEXTURE_2D_LOD(t, p, 0.0) /*============================================================================ - GREEN AS LUMA OPTION SUPPORT FUNCTION -============================================================================*/ +GREEN AS LUMA OPTION SUPPORT FUNCTION + ============================================================================*/ #if (FXAA_GREEN_AS_LUMA == 0) - FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return dot(rgba.xyz * rgba.xyz, vec3(0.299, 0.587, 0.114)); } +FxaaFloat FxaaLuma(FxaaFloat4 rgba) { + return dot(rgba.xyz * rgba.xyz, vec3(0.299, 0.587, 0.114)); +} #else - FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +FxaaFloat FxaaLuma(FxaaFloat4 rgba) { + return rgba.y; +} #endif /*============================================================================ - FXAA3 CONSOLE - PC VERSION -============================================================================*/ +FXAA3 CONSOLE - PC VERSION + ============================================================================*/ /*--------------------------------------------------------------------------*/ FxaaFloat4 FxaaPixelShader( // See FXAA Quality FxaaPixelShader() source for docs on Inputs! @@ -209,77 +207,75 @@ FxaaFloat4 FxaaPixelShader( // will appear very dark in the green channel! // Tune by looking at mostly non-green content, // then start at zero and increase until aliasing is a problem. - FxaaFloat fxaaConsoleEdgeThresholdMin -) { -/*--------------------------------------------------------------------------*/ + FxaaFloat fxaaConsoleEdgeThresholdMin) { + /*--------------------------------------------------------------------------*/ FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); - #if (FXAA_GREEN_AS_LUMA == 0) - // TODO Luma - FxaaFloat lumaM = FxaaLuma(rgbyM); - #else - FxaaFloat lumaM = rgbyM.y; - #endif -/*--------------------------------------------------------------------------*/ +#if (FXAA_GREEN_AS_LUMA == 0) + // TODO Luma + FxaaFloat lumaM = FxaaLuma(rgbyM); +#else + FxaaFloat lumaM = rgbyM.y; +#endif + /*--------------------------------------------------------------------------*/ FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); lumaNe += 1.0/384.0; FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat lumaMinM = min(lumaMin, lumaM); FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); FxaaFloat lumaMaxM = max(lumaMax, lumaM); FxaaFloat dirSwMinusNe = lumaSw - lumaNe; FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; FxaaFloat dirSeMinusNw = lumaSe - lumaNw; - if(lumaMaxSubMinM < lumaMaxScaledClamped) - { - #if (FXAA_DISCARD == 1) + if(lumaMaxSubMinM < lumaMaxScaledClamped) { +#if (FXAA_DISCARD == 1) FxaaDiscard; - #else +#else return rgbyM; - #endif +#endif } -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat2 dir; dir.x = dirSwMinusNe + dirSeMinusNw; dir.y = dirSwMinusNe - dirSeMinusNw; -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat2 dir1 = normalize(dir.xy); FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat2 dir2x = dir2 * fxaaConsoleRcpFrameOpt2.zw; FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2x); FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2x); -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); -/*--------------------------------------------------------------------------*/ - #if (FXAA_GREEN_AS_LUMA == 0) - // TODO Luma - float lumaB = FxaaLuma(rgbyB); - #else - float lumaB = rgbyB.y; - #endif + /*--------------------------------------------------------------------------*/ +#if (FXAA_GREEN_AS_LUMA == 0) + // TODO Luma + float lumaB = FxaaLuma(rgbyB); +#else + float lumaB = rgbyB.y; +#endif if((lumaB < lumaMin) || (lumaB > lumaMax)) - rgbyB.xyz = rgbyA.xyz * 0.5; + rgbyB.xyz = rgbyA.xyz * 0.5; // - return rgbyB; + return rgbyB; } /*==========================================================================*/ @@ -287,18 +283,18 @@ FxaaFloat4 FxaaPixelShader( #define edgeThreshold 0.15 #define edgeThresholdMin 0.05 -void main(){ - vec2 screenSize05 = 0.5 * u_screenSize2f; - - vec4 posPos; - posPos.xy = v_position2f; - posPos.zw = v_position2f + u_screenSize2f; - - vec4 rcpFrameOpt; - rcpFrameOpt.xy = -screenSize05; - rcpFrameOpt.zw = screenSize05; - - output4f = vec4(FxaaPixelShader(v_position2f + screenSize05, posPos, u_screenTexture, rcpFrameOpt, rcpFrameOpt * 4.0, edgeSharpness, edgeThreshold, edgeThresholdMin).rgb, 1.0); +void main() { + vec2 screenSize05 = 0.5 * u_screenSize2f; + + vec4 posPos; + posPos.xy = v_position2f; + posPos.zw = v_position2f + u_screenSize2f; + + vec4 rcpFrameOpt; + rcpFrameOpt.xy = -screenSize05; + rcpFrameOpt.zw = screenSize05; + + EAGLER_FRAG_COLOR = vec4(FxaaPixelShader(v_position2f + screenSize05, posPos, u_screenTexture, rcpFrameOpt, rcpFrameOpt * 4.0, edgeSharpness, edgeThreshold, edgeThresholdMin).rgb, 1.0); } #else @@ -306,25 +302,22 @@ void main(){ // Is it faster? Idfk, probably compiles faster at least, what matters it I tried float _616; -vec4 _617; -void main() -{ +void main() { mediump vec2 _257 = u_screenSize2f * 0.5; mediump vec4 _611 = vec4(v_position2f, v_position2f + u_screenSize2f); mediump vec4 _612 = vec4(_616, _616, _257); mediump vec2 _290 = v_position2f + _257; mediump vec4 _608; - for(;;) - { - mediump vec3 _532 = textureLod(u_screenTexture, _611.xy, 0.0).xyz; + for(;;) { + mediump vec3 _532 = EAGLER_TEXTURE_2D_LOD(u_screenTexture, _611.xy, 0.0).xyz; mediump float _536 = dot(_532 * _532, vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)); - mediump vec3 _540 = textureLod(u_screenTexture, _611.xw, 0.0).xyz; + mediump vec3 _540 = EAGLER_TEXTURE_2D_LOD(u_screenTexture, _611.xw, 0.0).xyz; mediump float _544 = dot(_540 * _540, vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)); - mediump vec3 _548 = textureLod(u_screenTexture, _611.zy, 0.0).xyz; - mediump vec3 _556 = textureLod(u_screenTexture, _611.zw, 0.0).xyz; + mediump vec3 _548 = EAGLER_TEXTURE_2D_LOD(u_screenTexture, _611.zy, 0.0).xyz; + mediump vec3 _556 = EAGLER_TEXTURE_2D_LOD(u_screenTexture, _611.zw, 0.0).xyz; mediump float _560 = dot(_556 * _556, vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)); - mediump vec4 _390 = textureLod(u_screenTexture, _290, 0.0); + mediump vec4 _390 = EAGLER_TEXTURE_2D_LOD(u_screenTexture, _290, 0.0); mediump vec3 _564 = _390.xyz; mediump float _568 = dot(_564 * _564, vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)); mediump float _397 = dot(_548 * _548, vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)) + 0.00260416674427688121795654296875; @@ -332,8 +325,7 @@ void main() mediump float _412 = min(min(_397, _560), min(_536, _544)); mediump float _427 = _544 - _397; mediump float _433 = _560 - _536; - if ((max(_409, _568) - min(_412, _568)) < max(0.0500000007450580596923828125, _409 * 0.1500000059604644775390625)) - { + if ((max(_409, _568) - min(_412, _568)) < max(0.0500000007450580596923828125, _409 * 0.1500000059604644775390625)) { _608 = _390; break; } @@ -347,12 +339,11 @@ void main() mediump vec2 _484 = (_612 * 4.0).zw; vec2 _615 = -hp_copy_481; mediump vec2 mp_copy_615 = _615; - mediump vec4 _498 = textureLod(u_screenTexture, mp_copy_614 * _454 + _290, 0.0) + textureLod(u_screenTexture, _449 * _454 + _290, 0.0); - mediump vec4 _505 = ((textureLod(u_screenTexture, mp_copy_615 * _484 + _290, 0.0) + textureLod(u_screenTexture, _481 * _484 + _290, 0.0)) * 0.25) + (_498 * 0.25); + mediump vec4 _498 = EAGLER_TEXTURE_2D_LOD(u_screenTexture, mp_copy_614 * _454 + _290, 0.0) + EAGLER_TEXTURE_2D_LOD(u_screenTexture, _449 * _454 + _290, 0.0); + mediump vec4 _505 = ((EAGLER_TEXTURE_2D_LOD(u_screenTexture, mp_copy_615 * _484 + _290, 0.0) + EAGLER_TEXTURE_2D_LOD(u_screenTexture, _481 * _484 + _290, 0.0)) * 0.25) + (_498 * 0.25); mediump float _576 = dot(_505.xyz * _505.xyz, vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)); mediump vec4 _607; - if ((_576 < _412) || (_576 > _409)) - { + if ((_576 < _412) || (_576 > _409)) { mediump vec3 _518 = _498.xyz * 0.5; mediump vec4 _600; _600.x = _518.x; @@ -360,14 +351,13 @@ void main() _600.z = _518.z; _607 = _600; } - else - { + else { _607 = _505; } _608 = _607; break; } - output4f = vec4(_608.xyz, 1.0); + EAGLER_FRAG_COLOR = vec4(_608.xyz, 1.0); } #endif \ No newline at end of file diff --git a/resources/resources/assets/eagler/glsl/texture_blit.fsh b/resources/resources/assets/eagler/glsl/texture_blit.fsh index 7b3fb59..376ac1a 100644 --- a/resources/resources/assets/eagler/glsl/texture_blit.fsh +++ b/resources/resources/assets/eagler/glsl/texture_blit.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 lax1dude. All Rights Reserved. +* Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,14 +16,10 @@ * */ -precision lowp int; -precision highp float; -precision highp sampler2D; - -in vec2 v_texCoords2f; +EAGLER_IN(vec2, v_texCoords2f) #ifndef COMPILE_BLIT_DEPTH -layout(location = 0) out vec4 output4f; +EAGLER_FRAG_OUT() #endif uniform sampler2D u_inputTexture; @@ -35,13 +31,13 @@ uniform vec2 u_pixelAlignmentOffset2f; #endif void main() { - vec2 uv2f = v_texCoords2f; + vec2 uv2f = v_texCoords2f; #ifdef COMPILE_PIXEL_ALIGNMENT - uv2f = (floor(uv2f * u_pixelAlignmentSizes4f.xy) + u_pixelAlignmentOffset2f) * u_pixelAlignmentSizes4f.zw; + uv2f = (floor(uv2f * u_pixelAlignmentSizes4f.xy) + u_pixelAlignmentOffset2f) * u_pixelAlignmentSizes4f.zw; #endif #ifndef COMPILE_BLIT_DEPTH - output4f = textureLod(u_inputTexture, uv2f, u_textureLod1f); + EAGLER_FRAG_COLOR = EAGLER_TEXTURE_2D_LOD(u_inputTexture, uv2f, u_textureLod1f); #else - gl_FragDepth = textureLod(u_inputTexture, uv2f, u_textureLod1f).r; + EAGLER_FRAG_DEPTH = EAGLER_TEXTURE_2D_LOD(u_inputTexture, uv2f, u_textureLod1f).r; #endif } diff --git a/resources/resources/assets/eagler/glsl/texture_blit.vsh b/resources/resources/assets/eagler/glsl/texture_blit.vsh index 645e1c4..dacf4d7 100644 --- a/resources/resources/assets/eagler/glsl/texture_blit.vsh +++ b/resources/resources/assets/eagler/glsl/texture_blit.vsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 lax1dude. All Rights Reserved. +* Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,19 +16,17 @@ * */ -precision lowp int; -precision lowp float; -precision lowp sampler2D; +EAGLER_VSH_LAYOUT_BEGIN() +EAGLER_IN(0, vec2, a_position2f) +EAGLER_VSH_LAYOUT_END() -layout(location = 0) in vec2 a_position2f; - -out vec2 v_texCoords2f; +EAGLER_OUT(vec2, v_texCoords2f) uniform vec4 u_srcCoords4f; uniform vec4 u_dstCoords4f; void main() { - vec2 uv = a_position2f * 0.5 + 0.5; - v_texCoords2f = u_srcCoords4f.xy + u_srcCoords4f.zw * uv; - gl_Position = vec4(u_dstCoords4f.xy + u_dstCoords4f.zw * uv, 0.0, 1.0); + vec2 uv = a_position2f * 0.5 + 0.5; + v_texCoords2f = u_srcCoords4f.xy + u_srcCoords4f.zw * uv; + EAGLER_VERT_POSITION = vec4(u_dstCoords4f.xy + u_dstCoords4f.zw * uv, 0.0, 1.0); } diff --git a/resources/resources/assets/eagler/glsl/texture_mix.fsh b/resources/resources/assets/eagler/glsl/texture_mix.fsh index 3cef92f..8cc1812 100644 --- a/resources/resources/assets/eagler/glsl/texture_mix.fsh +++ b/resources/resources/assets/eagler/glsl/texture_mix.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. +* Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -16,13 +16,9 @@ * */ -precision lowp int; -precision highp float; -precision highp sampler2D; +EAGLER_IN(vec2, v_position2f) -in vec2 v_position2f; - -layout(location = 0) out vec4 output4f; +EAGLER_FRAG_OUT() uniform sampler2D u_inputTexture; uniform float u_textureLod1f; @@ -31,7 +27,7 @@ uniform vec4 u_blendBias4f; uniform mat3 u_matrixTransform; void main() { - vec3 coords = u_matrixTransform * vec3(v_position2f, 1.0); - vec4 color4f = textureLod(u_inputTexture, coords.xy, u_textureLod1f); - output4f = color4f * u_blendFactor4f + u_blendBias4f; + vec3 coords = u_matrixTransform * vec3(v_position2f, 1.0); + vec4 color4f = EAGLER_TEXTURE_2D_LOD(u_inputTexture, coords.xy, u_textureLod1f); + EAGLER_FRAG_COLOR = color4f * u_blendFactor4f + u_blendBias4f; } diff --git a/resources/resources/assets/eagler/gui/eagler_gui.png b/resources/resources/assets/eagler/gui/eagler_gui.png index 7042858..5d9d516 100644 Binary files a/resources/resources/assets/eagler/gui/eagler_gui.png and b/resources/resources/assets/eagler/gui/eagler_gui.png differ diff --git a/resources/resources/assets/eagler/gui/notif_bk_large.png b/resources/resources/assets/eagler/gui/notif_bk_large.png new file mode 100755 index 0000000..9cab145 Binary files /dev/null and b/resources/resources/assets/eagler/gui/notif_bk_large.png differ diff --git a/resources/resources/assets/eagler/gui/touch_gui.png b/resources/resources/assets/eagler/gui/touch_gui.png new file mode 100755 index 0000000..12cbcbe Binary files /dev/null and b/resources/resources/assets/eagler/gui/touch_gui.png differ diff --git a/resources/resources/assets/minecraft/lang/en_US.lang b/resources/resources/assets/minecraft/lang/en_US.lang index fcdda89..aed7974 100644 --- a/resources/resources/assets/minecraft/lang/en_US.lang +++ b/resources/resources/assets/minecraft/lang/en_US.lang @@ -1,3201 +1,3337 @@ - -language.name=English -language.region=US -language.code=en_US - -gui.done=Done -gui.cancel=Cancel -gui.back=Back -gui.toTitle=Back to title screen -gui.toMenu=Back to server list -gui.up=Up -gui.down=Down -gui.yes=Yes -gui.no=No -gui.none=None -gui.all=All - -eaglercraft.recording.unsupported=Recording Unsupported! -eaglercraft.recording.stop=Stop Recording -eaglercraft.recording.start=Record Screen... -eaglercraft.soundCategory.voice=Recording Voice - -eaglercraft.resourcePack.prompt.title=What do you want to do with '%s'? -eaglercraft.resourcePack.prompt.text=Tip: Hold Shift to skip this screen when selecting a resource pack! -eaglercraft.resourcePack.prompt.delete=Delete this resource pack -eaglercraft.resourcePack.prompt.add=Select this resource pack -eaglercraft.resourcePack.load.refreshing=Refreshing Resources... -eaglercraft.resourcePack.load.pleaseWait=Please Wait. -eaglercraft.resourcePack.load.loading=Loading resource pack... -eaglercraft.resourcePack.load.deleting=Deleting resource pack... - -eaglercraft.gui.exitKey=Use '%s' to close this screen! -eaglercraft.gui.exitKeyRetarded=Use Backtick (`) to close this screen! -eaglercraft.gui.continue=Continue - -eaglercraft.menu.forkOnGitlab=Fork on GitLab -eaglercraft.menu.editProfile=Edit Profile -eaglercraft.menu.openToLan=Invite -eaglercraft.menu.closeLan=Stop Sharing - -eaglercraft.editProfile.title=Edit Profile -eaglercraft.editProfile.username=Username -eaglercraft.editProfile.playerSkin=Player Skin -eaglercraft.editProfile.addSkin=Add Skin -eaglercraft.editProfile.clearSkin=Clear List -eaglercraft.editProfile.capes=Capes -eaglercraft.editProfile.disableFNAW=(Note: go to 'Options...' > 'Skin Customization' to disable FNAW skins) -eaglercraft.editProfile.enableFNAW=(Note: go to 'Options...' > 'Skin Customization' to enable FNAW skins) - -eaglercraft.editCape.title=Edit Cape -eaglercraft.editCape.playerCape=Player Cape -eaglercraft.editCape.addCape=Add Cape -eaglercraft.editCape.clearCape=Clear List - -eaglercraft.editProfile.importExport=Import/Export - -eaglercraft.settingsBackup.importExport.title=What do you wanna do? -eaglercraft.settingsBackup.importExport.import=Import Profile and Settings... -eaglercraft.settingsBackup.importExport.export=Export Profile and Settings... - -eaglercraft.settingsBackup.import.title=Import Profile and Settings -eaglercraft.settingsBackup.import.option.profile=Import Profile: -eaglercraft.settingsBackup.import.option.settings=Import Settings: -eaglercraft.settingsBackup.import.option.servers=Import Servers: -eaglercraft.settingsBackup.import.option.resourcePacks=Resource Packs: -eaglercraft.settingsBackup.import.option.import=Import - -eaglercraft.settingsBackup.export.title=Export Profile and Settings -eaglercraft.settingsBackup.export.option.profile=Export Profile: -eaglercraft.settingsBackup.export.option.settings=Export Settings: -eaglercraft.settingsBackup.export.option.servers=Export Servers: -eaglercraft.settingsBackup.export.option.resourcePacks=Resource Packs: -eaglercraft.settingsBackup.export.option.export=Export - -eaglercraft.settingsBackup.exporting.1=Exporting Profile... -eaglercraft.settingsBackup.exporting.2=Please Wait. - -eaglercraft.settingsBackup.exporting.failed.1=Export Failed! -eaglercraft.settingsBackup.exporting.failed.2=Could not compile EPK - -eaglercraft.settingsBackup.importing.1=Importing Profile... -eaglercraft.settingsBackup.importing.2=Please Wait. - -eaglercraft.settingsBackup.importing.failed.1=Import Failed! -eaglercraft.settingsBackup.importing.failed.2=Could not load EPK - -eaglercraft.resourcePack.importFailed.1=Import Failed! -eaglercraft.resourcePack.importFailed.2=Could not import ZIP file - -eaglercraft.singleplayer.integratedStartup=Starting integrated server - -eaglercraft.addServer.SSLWarn1=you are on an https: page! -eaglercraft.addServer.SSLWarn2=html5 will only allow wss:// - -eaglercraft.chat.exit=Exit Chat - -eaglercraft.handshakeApprove.plaintext.title=Protocol Warning - -eaglercraft.handshakeApprove.plaintext.body.0=§eThis server's 3rd party login system is -eaglercraft.handshakeApprove.plaintext.body.1=§eusing insecure plain-text password login - -eaglercraft.handshakeApprove.plaintext.body.3=This means your password can be stolen -eaglercraft.handshakeApprove.plaintext.body.4=by who is running this server, and also -eaglercraft.handshakeApprove.plaintext.body.5=any proxy you use to connect to it with - -eaglercraft.handshakeApprove.plaintext.body.7=§7Would you like to continue? - -eaglercraft.handshakeApprove.unsupportedAuth.title=Protocol Unsupported - -eaglercraft.handshakeApprove.unsupportedAuth.body.0=§cThis server's login system is using a -eaglercraft.handshakeApprove.unsupportedAuth.body.1=§cprotocol not supported by this client - -eaglercraft.handshakeApprove.unsupportedAuth.body.3=Please make sure you are using the -eaglercraft.handshakeApprove.unsupportedAuth.body.4=latest version of EaglercraftX or -eaglercraft.handshakeApprove.unsupportedAuth.body.5=another fork made for this server - -eaglercraft.options.hud.fps=Show FPS -eaglercraft.options.hud.coords=Show XYZ -eaglercraft.options.hud.stats=Show Stats HUD -eaglercraft.options.hud.world=Show World HUD -eaglercraft.options.hud.player=Show Player -eaglercraft.options.hud.note=Check 'Video Settings' for the option to hide XYZ -eaglercraft.options.hud.24h=24h Day -eaglercraft.options.chunkFix=Chunk Lag Fix -eaglercraft.options.fog=Fog -eaglercraft.options.fxaa=FXAA Antialiasing -eaglercraft.options.fxaa.auto=Auto -eaglercraft.options.fastMath=Fast Math -eaglercraft.options.fastMath.0=OFF -eaglercraft.options.fastMath.1=Low -eaglercraft.options.fastMath.2=High -eaglercraft.options.dynamicLights=Dynamic Lights - -eaglercraft.key.function=Function -eaglercraft.key.zoomCamera=Zoom Camera -eaglercraft.key.close=Close Screen - -eaglercraft.disconnect.tooManyRequests=Too Many Requests! - -eaglercraft.auth.required=Authentication Required -eaglercraft.auth.continue=Join Server - -eaglercraft.shaders.gui.optionsButton=Shaders... -eaglercraft.shaders.gui.title=Shaders -eaglercraft.shaders.gui.enable=Enable Shaders -eaglercraft.shaders.gui.headerTier1=Simple Effects (fast) -eaglercraft.shaders.gui.headerTier2=Intermediate Effects (not as fast) - - -eaglercraft.shaders.gui.option.WAVING_BLOCKS.label=Waving Grass - -eaglercraft.shaders.gui.option.WAVING_BLOCKS.desc.0=The classic vanilla shader that make grass and leaf blocks move with the wind -eaglercraft.shaders.gui.option.WAVING_BLOCKS.desc.2=ON: slower, complex control flow -eaglercraft.shaders.gui.option.WAVING_BLOCKS.desc.3=OFF: faster, simple control flow - -eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.label=Dynamic Lights - -eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.0=Items like torches, glowstone, jack o'lanterns, etc. light up the area around them when they are held or dropped -eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.2=This feature usually does not affect FPS unless there is a large number of dynamic light sources close to the player -eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.4=ON: enable dynamic lights -eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.5=OFF: disable dynamic lights - -eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.label=Global SSAO - -eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.0=Applies realistic screenspace ambient occlusion (SSAO) to areas of the screen that vanilla Minecraft's default "Smooth Lighting" feature cannot -eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.2=This effect can greatly reduce lag spikes caused by chunk updates in exchange for a decreased maximum FPS when standing still -eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.4=ON: use SSAO -eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.5=OFF: use vanilla - -eaglercraft.shaders.gui.option.SHADOWS_SUN.label=Sun Shadow Dist - -eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.0=Makes the sun and moon cast shadows on the world, you almost definitely want to enable this -eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.2=0: off -eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.3=1: 16 blocks -eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.4=2: 32 blocks -eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.5=3: 64 blocks -eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.6=4: 128 blocks - -eaglercraft.shaders.gui.option.SHADOWS_COLORED.label=Color Shadow - -eaglercraft.shaders.gui.option.SHADOWS_COLORED.desc.0=Makes blocks like stained glass cast colored shadows -eaglercraft.shaders.gui.option.SHADOWS_COLORED.desc.2=ON: colored shadows (slower) -eaglercraft.shaders.gui.option.SHADOWS_COLORED.desc.3=OFF: grayscale shadows (faster) - -eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.label=Smooth Shadow - -eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.desc.0=Smooths out the edges of sun and dynamic light shadows to get rid of aliasing -eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.desc.2=ON: smooth shadows (slower) -eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.desc.3=OFF: aliased shadows (faster) - -eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.label=Env. Mapping - -eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.0=Renders an environment map, allows transparent blocks and moving entities to reflect their surroundings -eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.2=Uses dual paraboloid mapping, only 2 render passes are required to render all reflections, instead of a full 6 render passes like a conventional cube map -eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.4=ON: render env. map (slower) -eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.5=OFF: disable env. map (faster) - -eaglercraft.shaders.gui.option.REALISTIC_WATER.label=Realistic Water - -eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.0=Makes water realistic, adds reflection and refraction effects, uses raytracing -eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.2=ON: render realistic water (slower) -eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.3=OFF: render vanilla water (faster) - -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.label=God Rays - -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.0=Render god rays (light shafts) for sunlight and moonlight shadows -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.2=ON: render god rays (slower) -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.3=OFF: disable god rays (faster) - -eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.label=Raytracing - -eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.0=Renders raytraced reflections off of blocks -eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.2=Thanks to some advanced optimizations, this feature only has a small impact on FPS and is compatible with old hardware -eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.4=Both raymarching and raytracing are employed, raymarching is initially used to locate and track groups of pixels on the screen to reflect, then raytracing is used to reproject that data between multiple frames so the raymarching process only has to be repeated once or twice every few seconds -eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.6=ON: enable raytracing (slower) -eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.7=OFF: disable raytracing (faster) - -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.0=Render god rays (light shafts) for sunlight and moonlight shadows -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.2=ON: render god rays (slower) -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.3=OFF: disable god rays (faster) - -eaglercraft.shaders.gui.option.POST_LENS_DISTORION.label=Lens Distort - -eaglercraft.shaders.gui.option.POST_LENS_DISTORION.desc.0=Renders chromatic abberation and lens distorion -eaglercraft.shaders.gui.option.POST_LENS_DISTORION.desc.2=ON: render lens distortion (slower) -eaglercraft.shaders.gui.option.POST_LENS_DISTORION.desc.3=OFF: disable lens distortion (faster) - -eaglercraft.shaders.gui.option.POST_LENS_FLARES.label=Lens Flares - -eaglercraft.shaders.gui.option.POST_LENS_FLARES.desc.0=Renders filmic lens flares for the sun -eaglercraft.shaders.gui.option.POST_LENS_FLARES.desc.2=ON: render lens flares (slower) -eaglercraft.shaders.gui.option.POST_LENS_FLARES.desc.3=OFF: disable lens flares (faster) - -eaglercraft.shaders.gui.option.POST_BLOOM.label=Bloom - -eaglercraft.shaders.gui.option.POST_BLOOM.desc.0=Renders bloom for emissive textures and sunlight -eaglercraft.shaders.gui.option.POST_BLOOM.desc.2=ON: render bloom (slower) -eaglercraft.shaders.gui.option.POST_BLOOM.desc.3=OFF: disable bloom (faster) - -eaglercraft.shaders.gui.option.POST_FXAA.label=FXAA - -eaglercraft.shaders.gui.option.POST_FXAA.desc.0=Applies FXAA antialiasing in post processing -eaglercraft.shaders.gui.option.POST_FXAA.desc.2=This is the preferred antialiasing method for minecraft, as classical MSAA cannot antialias the pixels of upscaled textures -eaglercraft.shaders.gui.option.POST_FXAA.desc.4=ON: enable fxaa (slower) -eaglercraft.shaders.gui.option.POST_FXAA.desc.5=OFF: disable fxaa (faster) - -eaglercraft.shaders.gui.unsupported.title=Shaders are unavailable on this device! -eaglercraft.shaders.gui.unsupported.reason.hdrFramebuffer=Reason: No HDR render target support - -eaglercraft.shaders.debugMenuTip=Press %s+4 to access the shader debug menu - -eaglercraft.command.skull.tip=Use /eagskull to create custom skulls -eaglercraft.command.skull.usage=/eagskull -eaglercraft.command.skull.nopermission=Cheats are not enabled! -eaglercraft.command.skull.feedback=Created new skull: "%s" -eaglercraft.command.skull.error.invalid.png=Invalid PNG file! -eaglercraft.command.skull.error.invalid.skin=Image with size %dx%d is not a valid minecraft skin! - -eaglercraft.command.clientStub=This command is client side! - -menu.game=Game menu -menu.singleplayer=Singleplayer -menu.multiplayer=Multiplayer -menu.online=Minecraft Realms -menu.options=Options... -menu.quit=Quit Game -menu.returnToMenu=Save and Quit to Title -menu.disconnect=Disconnect -menu.returnToGame=Back to Game -menu.switchingLevel=Switching worlds -menu.generatingLevel=Generating world -menu.loadingLevel=Loading world -menu.generatingTerrain=Building terrain -menu.convertingLevel=Converting world -menu.simulating=Simulating the world for a bit -menu.respawning=Respawning -menu.shareToLan=Open to LAN - -selectWorld.title=Select World -selectWorld.empty=empty -selectWorld.world=World -selectWorld.select=Play Selected World -selectWorld.create=Create New World -selectWorld.recreate=Re-Create -selectWorld.createDemo=Play New Demo World -selectWorld.delete=Delete -selectWorld.rename=Rename -selectWorld.deleteQuestion=Are you sure you want to delete this world? -selectWorld.deleteWarning=will be lost forever! (A long time!) -selectWorld.deleteButton=Delete -selectWorld.renameButton=Rename -selectWorld.renameTitle=Rename World -selectWorld.conversion=Must be converted! -selectWorld.newWorld=New World -selectWorld.newWorld.copyOf=Copy of %s -selectWorld.enterName=World Name -selectWorld.resultFolder=Will be saved in: -selectWorld.enterSeed=Seed for the World Generator -selectWorld.seedInfo=Leave blank for a random seed -selectWorld.cheats=Cheats -selectWorld.customizeType=Customize - -createWorld.customize.presets=Presets -createWorld.customize.presets.title=Select a Preset -createWorld.customize.presets.select=Use Preset -createWorld.customize.presets.share=Want to share your preset with someone? Use the below box! -createWorld.customize.presets.list=Alternatively, here's some we made earlier! -createWorld.customize.flat.title=Superflat Customization -createWorld.customize.flat.tile=Layer Material -createWorld.customize.flat.height=Height -createWorld.customize.flat.addLayer=Add Layer -createWorld.customize.flat.editLayer=Edit Layer -createWorld.customize.flat.removeLayer=Remove Layer -createWorld.customize.flat.layer.top=Top - %d -createWorld.customize.flat.layer=%d -createWorld.customize.flat.layer.bottom=Bottom - %d - -createWorld.customize.custom.page0=Basic Settings -createWorld.customize.custom.page1=Ore Settings -createWorld.customize.custom.page2=Advanced Settings (Expert Users Only!) -createWorld.customize.custom.page3=Extra Advanced Settings (Expert Users Only!) -createWorld.customize.custom.randomize=Randomize -createWorld.customize.custom.prev=Previous Page -createWorld.customize.custom.next=Next Page -createWorld.customize.custom.defaults=Defaults -createWorld.customize.custom.confirm1=This will overwrite your current -createWorld.customize.custom.confirm2=settings and cannot be undone. -createWorld.customize.custom.confirmTitle=Warning! -createWorld.customize.custom.mainNoiseScaleX=Main Noise Scale X -createWorld.customize.custom.mainNoiseScaleY=Main Noise Scale Y -createWorld.customize.custom.mainNoiseScaleZ=Main Noise Scale Z -createWorld.customize.custom.depthNoiseScaleX=Depth Noise Scale X -createWorld.customize.custom.depthNoiseScaleZ=Depth Noise Scale Z -createWorld.customize.custom.depthNoiseScaleExponent=Depth Noise Exponent -createWorld.customize.custom.baseSize=Depth Base Size -createWorld.customize.custom.coordinateScale=Coordinate Scale -createWorld.customize.custom.heightScale=Height Scale -createWorld.customize.custom.stretchY=Height Stretch -createWorld.customize.custom.upperLimitScale=Upper Limit Scale -createWorld.customize.custom.lowerLimitScale=Lower Limit Scale -createWorld.customize.custom.biomeDepthWeight=Biome Depth Weight -createWorld.customize.custom.biomeDepthOffset=Biome Depth Offset -createWorld.customize.custom.biomeScaleWeight=Biome Scale Weight -createWorld.customize.custom.biomeScaleOffset=Biome Scale Offset -createWorld.customize.custom.seaLevel=Sea Level -createWorld.customize.custom.useCaves=Caves -createWorld.customize.custom.useStrongholds=Strongholds -createWorld.customize.custom.useVillages=Villages -createWorld.customize.custom.useMineShafts=Mineshafts -createWorld.customize.custom.useTemples=Temples -createWorld.customize.custom.useMonuments=Ocean Monuments -createWorld.customize.custom.useRavines=Ravines -createWorld.customize.custom.useDungeons=Dungeons -createWorld.customize.custom.dungeonChance=Dungeon Count -createWorld.customize.custom.useWaterLakes=Water Lakes -createWorld.customize.custom.waterLakeChance=Water Lake Rarity -createWorld.customize.custom.useLavaLakes=Lava Lakes -createWorld.customize.custom.lavaLakeChance=Lava Lake Rarity -createWorld.customize.custom.useLavaOceans=Lava Oceans -createWorld.customize.custom.fixedBiome=Biome -createWorld.customize.custom.biomeSize=Biome Size -createWorld.customize.custom.riverSize=River Size - -createWorld.customize.custom.size= Spawn Size -createWorld.customize.custom.count= Spawn Tries -createWorld.customize.custom.minHeight= Min. Height -createWorld.customize.custom.maxHeight= Max. Height -createWorld.customize.custom.center= Center Height -createWorld.customize.custom.spread= Spread Height - -createWorld.customize.custom.presets.title=Customize World Presets -createWorld.customize.custom.presets=Presets -createWorld.customize.custom.preset.waterWorld=Water World -createWorld.customize.custom.preset.isleLand=Isle Land -createWorld.customize.custom.preset.caveDelight=Caver's Delight -createWorld.customize.custom.preset.mountains=Mountain Madness -createWorld.customize.custom.preset.drought=Drought -createWorld.customize.custom.preset.caveChaos=Caves of Chaos -createWorld.customize.custom.preset.goodLuck=Good Luck - -gameMode.survival=Survival Mode -gameMode.creative=Creative Mode -gameMode.adventure=Adventure Mode -gameMode.spectator=Spectator Mode -gameMode.hardcore=Hardcore Mode! -gameMode.changed=Your game mode has been updated - -selectWorld.gameMode=Game Mode -selectWorld.gameMode.survival=Survival -selectWorld.gameMode.survival.line1=Search for resources, crafting, gain -selectWorld.gameMode.survival.line2=levels, health and hunger -selectWorld.gameMode.creative=Creative -selectWorld.gameMode.creative.line1=Unlimited resources, free flying and -selectWorld.gameMode.creative.line2=destroy blocks instantly -selectWorld.gameMode.spectator=Spectator -selectWorld.gameMode.spectator.line1=You can look but don't touch -selectWorld.gameMode.spectator.line2= -selectWorld.gameMode.hardcore=Hardcore -selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest -selectWorld.gameMode.hardcore.line2=difficulty, and one life only -selectWorld.gameMode.adventure=Adventure -selectWorld.gameMode.adventure.line1=Same as survival mode, but blocks can't -selectWorld.gameMode.adventure.line2=be added or removed -selectWorld.moreWorldOptions=More World Options... -selectWorld.mapFeatures=Generate Structures: -selectWorld.mapFeatures.info=Villages, dungeons etc -selectWorld.mapType=World Type: -selectWorld.mapType.normal=Normal -selectWorld.allowCommands=Allow Cheats: -selectWorld.allowCommands.info=Commands like /gamemode, /xp -selectWorld.hardcoreMode=Hardcore: -selectWorld.hardcoreMode.info=World is deleted upon death -selectWorld.bonusItems=Bonus Chest: - -generator.default=Default -generator.flat=Superflat -generator.largeBiomes=Large Biomes -generator.amplified=AMPLIFIED -generator.customized=Customized -generator.debug_all_block_states=Debug Mode - -generator.amplified.info=Notice: Just for fun, requires beefy computer - -eaglercraft.singleplayer.busy.killTask=Cancel Task -eaglercraft.singleplayer.busy.cancelWarning=Are you sure? -eaglercraft.singleplayer.busy.confirmCancel=Confirm Cancel - -eaglercraft.singleplayer.crashed.title=Recieved a crash report from integrated server! -eaglercraft.singleplayer.crashed.checkConsole=Check the console for more details -eaglercraft.singleplayer.crashed.continue=Continue - -eaglercraft.singleplayer.failed.title=Singleplayer Task Failed! -eaglercraft.singleplayer.failed.killed=The worker process was killed -eaglercraft.singleplayer.failed.notStarted=The worker process could not start - -eaglercraft.singleplayer.failed.singleThreadWarning.1=Worker Thread Startup Failure! -eaglercraft.singleplayer.failed.singleThreadWarning.2=The integrated server will run in single-threaded mode - -eaglercraft.singleplayer.busy.listingworlds=Loading worlds -eaglercraft.singleplayer.failed.listingworlds=Could not fetch worlds list! - -eaglercraft.singleplayer.busy.deleting=Deleting world -eaglercraft.singleplayer.failed.deleting=Failed to delete world! - -eaglercraft.singleplayer.busy.renaming=Renaming world -eaglercraft.singleplayer.failed.renaming=Failed to rename world! - -eaglercraft.singleplayer.busy.duplicating=Duplicating world -eaglercraft.singleplayer.failed.duplicating=Failed to duplicate world! - -eaglercraft.singleplayer.busy.importing.1=Importing world as EPK -eaglercraft.singleplayer.failed.importing.1=Failed to import world as EPK! - -eaglercraft.singleplayer.busy.importing.2=Importing world as vanilla -eaglercraft.singleplayer.failed.importing.2=Failed to import world as vanilla! - -eaglercraft.singleplayer.busy.exporting.1=Exporting world as EPK -eaglercraft.singleplayer.failed.exporting.1=Failed to export world as EPK! - -eaglercraft.singleplayer.busy.exporting.2=Exporting world as vanilla -eaglercraft.singleplayer.failed.exporting.2=Failed to export world as vanilla! - -eaglercraft.singleplayer.busy.clearplayers=Clearing players -eaglercraft.singleplayer.failed.clearplayers=Failed to clear players! - -eaglercraft.singleplayer.busy.startingIntegratedServer=Starting up integrated server -eaglercraft.singleplayer.failed.startingIntegratedServer=Failed to start up integrated server! - -eaglercraft.singleplayer.busy.stoppingIntegratedServer=Shutting down integrated server -eaglercraft.singleplayer.failed.stoppingIntegratedServer=Failed to shut down integrated server! - -eaglercraft.singleplayer.failed.serverCrash=Integrated server encountered an exception! - -eaglercraft.singleplayer.failed.demo.title=Could not start demo -eaglercraft.singleplayer.failed.demo.desc=Failed to start integrated server! - -eaglercraft.singleplayer.notSupported.title=Shared worlds are not supported on this browser! -eaglercraft.singleplayer.notSupported.desc=WebRTC is not supported - -eaglercraft.singleplayer.import.title=Import World -eaglercraft.singleplayer.import.enterName=Enter world name: -eaglercraft.singleplayer.import.continue=Continue -eaglercraft.singleplayer.import.reading=Reading: '%s' -eaglercraft.singleplayer.import.loadSpawnChunks=Keep spawn chunks loaded: %s -eaglercraft.singleplayer.import.enhancedGameRules=Add extra game features: %s - -eaglercraft.singleplayer.create.title=What do you wanna do? -eaglercraft.singleplayer.create.create=Create New World -eaglercraft.singleplayer.create.create.tooltip=Make a new world for eaglercraft -eaglercraft.singleplayer.create.import=Load EPK File -eaglercraft.singleplayer.create.import.tooltip=Load an Eaglercraft .epk world file -eaglercraft.singleplayer.create.vanilla=Import Vanilla World -eaglercraft.singleplayer.create.vanilla.tooltip=Load a vanilla minecraft 1.8 world - -eaglercraft.singleplayer.backup.title=World Backup Menu: '%s' -eaglercraft.singleplayer.backup.recreate=Re-Create World -eaglercraft.singleplayer.backup.recreate.tooltip=Create a new world with the same seed -eaglercraft.singleplayer.backup.seed=Seed: -eaglercraft.singleplayer.backup.duplicate=Duplicate World -eaglercraft.singleplayer.backup.duplicate.tooltip=Copy this world into a new save -eaglercraft.singleplayer.backup.export=Export EPK File -eaglercraft.singleplayer.backup.export.tooltip=Download this world as a compressed .epk file -eaglercraft.singleplayer.backup.vanilla=Convert to Vanilla -eaglercraft.singleplayer.backup.vanilla.tooltip=Download this world as a vanilla 1.8 world -eaglercraft.singleplayer.backup.clearPlayerData=Delete Player Data -eaglercraft.singleplayer.backup.clearPlayerData.tooltip=Clears the inventories of all players except the owner - -eaglercraft.singleplayer.backup.clearPlayerData.warning1=Are you sure you want to delete all player data? -eaglercraft.singleplayer.backup.clearPlayerData.warning2=All players in '%s' will lose their inventories (besides %s) - -eaglercraft.selectWorld.backup=Backup - -eaglercraft.selectWorld.duplicate=Duplicate World: -eaglercraft.selectWorld.duplicateButton=Duplicate - -eaglercraft.networkSettings.title=Shared World Relay Servers -eaglercraft.networkSettings.add=Add Relay -eaglercraft.networkSettings.delete=Delete Relay -eaglercraft.networkSettings.default=Set Primary -eaglercraft.networkSettings.refresh=Refresh -eaglercraft.networkSettings.loadDefaults=Load Defaults -eaglercraft.networkSettings.relayTimeout=Connection Timeout: -eaglercraft.networkSettings.relayTimeoutChange=change -eaglercraft.networkSettings.relayTimeoutTitle=Change Connection Timeout -eaglercraft.networkSettings.downloadRelay=Download JAR - -eaglercraft.directConnect.prompt=What would you like to do? -eaglercraft.directConnect.lanWorld=Join Shared World -eaglercraft.directConnect.lanWorldCode=Enter Join Code: -eaglercraft.directConnect.networkSettingsNote=Click 'Network Settings' to add a relay URL -eaglercraft.directConnect.ipGrabNote=Note: The world's owner can get your IP address -eaglercraft.directConnect.serverJoin=Connect to Server -eaglercraft.directConnect.lanWorldJoin=Join World -eaglercraft.directConnect.lanWorldRelay=Network Settings - -eaglercraft.lanInfo.title=Shared World Info -eaglercraft.lanInfo.desc.0=Eaglercraft shared worlds are NOT limited to your local network like vanilla LAN worlds. This means that anyone with an internet connection and connection to the invite relay can join your world provided they have the code. -eaglercraft.lanInfo.desc.1=Join a shared world from the %s screen, or create one with the %s button while in a world. - -eaglercraft.lanServer.legacyClient=Please use EaglercraftL 1.9! - -eaglercraft.lanServer.pauseMenu0=Sharing World -eaglercraft.lanServer.pauseMenu1=Relay URL: -eaglercraft.lanServer.pauseMenu2=Join Code: - -eaglercraft.lanServer.wouldYouLikeToKick=Would you like to kick all players? - -eaglercraft.lanServer.worldName=World Name: -eaglercraft.lanServer.hidden=Hidden: -eaglercraft.lanServer.hideCode=hide details -eaglercraft.lanServer.showCode=show details -eaglercraft.lanServer.opened=Shared world opened on $relay$, join code is §a$code$ -eaglercraft.lanServer.closed=Shared world closed -eaglercraft.lanServer.pleaseWait=Please Wait... -eaglercraft.lanServer.relayDisconnected=Warning: connection to invite relay was lost, you must re-share the world to invite more people -eaglercraft.lanServer.ipGrabNote=Note: Players joining your world can get your IP address - -eaglercraft.addRelay.title=Add New Relay -eaglercraft.addRelay.name=Relay Comment -eaglercraft.addRelay.address=Relay Address -eaglercraft.addRelay.add=Add Relay -eaglercraft.addRelay.primary=Set Primary -eaglercraft.addRelay.removeText1=Do you want to remove this relay? - -eaglercraft.noRelay.title=No Relays are Configured! -eaglercraft.noRelay.titleFail=No Working Relays Available! -eaglercraft.noRelay.noRelay1=Shared Worlds Unavailable: No Relays Configured! -eaglercraft.noRelay.noRelay2=Click '§nNetwork Settings§r' to fix -eaglercraft.noRelay.worldNotFound1=Could not locate '§c$code$§r'! -eaglercraft.noRelay.worldNotFound2=Make sure to add the '§f$code$§r' world's relay URL -eaglercraft.noRelay.worldNotFound3=to the relay list 'Network Settings' to connect -eaglercraft.noRelay.worldFail=Failed to connect to '$code$'! - -eaglercraft.singleplayer.demo.create.title=What do you wanna do? -eaglercraft.singleplayer.demo.create.create=Play Demo World -eaglercraft.singleplayer.demo.create.create.tooltip=Play the Minecraft 1.8 demo and invite others -eaglercraft.singleplayer.demo.create.join=Join Shared World -eaglercraft.singleplayer.demo.create.join.tooltip=Join someone else's world and play multiplayer - -eaglercraft.createWorld.seedNote=Note: Vanilla seeds now work! - -eaglercraft.singleplayer.oldseedwarning.title=Old World Detected! -eaglercraft.singleplayer.oldseedwarning.msg1=Please use EaglercraftX u32 or older to "Re-Create" this world -eaglercraft.singleplayer.oldseedwarning.msg2=The world's seed will not be the same otherwise :( -eaglercraft.singleplayer.oldseedwarning.ok=OK - -eaglercraft.singleplayer.outdatedLANServerKick=This is a 1.5.2 LAN world! - -eaglercraft.options.debugConsoleButton=Open Debug Console - -eaglercraft.tile.bed.setspawn=Respawn point set - -eaglercraft.update.button=Check for updates: -eaglercraft.update.none=No updates available! -eaglercraft.update.noneNew=No new updates found -eaglercraft.update.found=Found new client update -eaglercraft.update.update=Update: -eaglercraft.update.author=Author: -eaglercraft.update.timestamp=Timestamp: -eaglercraft.update.size=Size (kB): -eaglercraft.update.startDownload=Download -eaglercraft.update.viewAll=View All (%d) -eaglercraft.update.dismiss=Dismiss -eaglercraft.update.downloading=Downloading Updates - -eaglercraft.update.downloadOffline=Download Offline -eaglercraft.update.digitallySigned=Digitally Signed (%s) -eaglercraft.update.signatureInvalid=Signature Invalid! - -eaglercraft.updateList.title=Versions Available -eaglercraft.updateList.download=Download -eaglercraft.updateList.refresh=Refresh -eaglercraft.updateList.note.0=Note: Updates are digitally signed, EaglercraftL will block any -eaglercraft.updateList.note.1=updates that were not created by HoosierTransfer - -eaglercraft.voice.title=Voice Channel -eaglercraft.voice.titleNoVoice=Voice is disabled on this server -eaglercraft.voice.titleVoiceUnavailable=Voice is unavailable -eaglercraft.voice.titleVoiceBrowserError=(browser issue) -eaglercraft.voice.ptt=Press '%s' to speak -eaglercraft.voice.pttChangeDesc=(Press Any Key) -eaglercraft.voice.changeKey=Change -eaglercraft.voice.off=OFF -eaglercraft.voice.radius=NEARBY -eaglercraft.voice.global=GLOBAL -eaglercraft.voice.volumeTitle=Change Volume -eaglercraft.voice.volumeListen=Speakers Volume: -eaglercraft.voice.volumeSpeak=Microphone Volume: -eaglercraft.voice.radiusTitle=Change Listener Radius -eaglercraft.voice.radiusLabel=Players Within: -eaglercraft.voice.radiusChange=change -eaglercraft.voice.notConnected=Not Connected -eaglercraft.voice.connecting=Connecting... -eaglercraft.voice.unavailable=Could not connect! -eaglercraft.voice.connectedGlobal=Connected - Global -eaglercraft.voice.connectedRadius=Connected - $f$Within $radius$m -eaglercraft.voice.playersListening=Players Listening: -eaglercraft.voice.muted=Players Muted: -eaglercraft.voice.unmute=unmute -eaglercraft.voice.mute=mute -eaglercraft.voice.apply=Apply -eaglercraft.voice.volumeSpeakerLabel=Speakers: -eaglercraft.voice.volumeMicrophoneLabel=Microphone: - -eaglercraft.voice.unsupportedWarning1=Voice Warning -eaglercraft.voice.unsupportedWarning2=Your network's firewall may not support -eaglercraft.voice.unsupportedWarning3=eaglercraft's voice chat. -eaglercraft.voice.unsupportedWarning4=If your game doesn't work it's your issue -eaglercraft.voice.unsupportedWarning5=to solve, not ayunami2000's or lax1dude's. -eaglercraft.voice.unsupportedWarning6=Don't ask them to 'fix' it for you because -eaglercraft.voice.unsupportedWarning7=they won't help you fix a problem that only -eaglercraft.voice.unsupportedWarning8=you or your network's administrator has the -eaglercraft.voice.unsupportedWarning9=ability to correctly resolve. -eaglercraft.voice.unsupportedWarning10=Continue -eaglercraft.voice.unsupportedWarning11=Cancel - -eaglercraft.voice.ipGrabWarning1=IP Logger Warning -eaglercraft.voice.ipGrabWarning2=Using Eaglercraft voice chat may allow your -eaglercraft.voice.ipGrabWarning3=IP address to be logged by other players -eaglercraft.voice.ipGrabWarning4=also using voice on the server. -eaglercraft.voice.ipGrabWarning5=This issue will not be fixed, it is an -eaglercraft.voice.ipGrabWarning6=internal browser issue, not a mistake in the -eaglercraft.voice.ipGrabWarning7=game. Fortunately, this can only be done if -eaglercraft.voice.ipGrabWarning8=the other player uses a hacked web browser -eaglercraft.voice.ipGrabWarning9=or has Wireshark to capture the voice -eaglercraft.voice.ipGrabWarning10=packets, as there exists no real javascript -eaglercraft.voice.ipGrabWarning11=method to log IPs using a normal skidded -eaglercraft.voice.ipGrabWarning12=eaglercraft hacked client. - -selectServer.title=Select Server -selectServer.empty=empty -selectServer.select=Join Server -selectServer.direct=Direct Connect -selectServer.edit=Edit -selectServer.delete=Delete -selectServer.add=Add server -selectServer.defaultName=Minecraft Server -selectServer.deleteQuestion=Are you sure you want to remove this server? -selectServer.deleteWarning=will be lost forever! (A long time!) -selectServer.deleteButton=Delete -selectServer.refresh=Refresh -selectServer.hiddenAddress=(Hidden) -addServer.title=Edit Server Info -addServer.enterName=Server Name -addServer.enterIp=Server Address -addServer.add=Done -addServer.hideAddress=Hide Address -addServer.resourcePack=Server Resource Packs -addServer.resourcePack.enabled=Enabled -addServer.resourcePack.disabled=Disabled -addServer.resourcePack.prompt=Prompt -lanServer.title=Shared World -lanServer.scanning=Scanning for games on your local network -lanServer.start=Start Shared World -lanServer.otherPlayers=Settings for Other Players -mcoServer.title=Minecraft Online World - -multiplayer.title=Play Multiplayer -multiplayer.connect=Connect -multiplayer.info1=Minecraft Multiplayer is currently not finished, but there -multiplayer.info2=is some buggy early testing going on. -multiplayer.ipinfo=Enter the IP of a server to connect to it: -multiplayer.texturePrompt.line1=This server recommends the use of a custom resource pack. -multiplayer.texturePrompt.line2=Would you like to download and install it automagically? -multiplayer.downloadingTerrain=Downloading terrain -multiplayer.downloadingStats=Downloading statistics & achievements... -multiplayer.stopSleeping=Leave Bed -multiplayer.player.joined=%s joined the game -multiplayer.player.joined.renamed=%s (formerly known as %s) joined the game -multiplayer.player.left=%s left the game - -chat.cannotSend=Cannot send chat message -chat.type.text=<%s> %s -chat.type.emote=* %s %s -chat.type.announcement=[%s] %s -chat.type.admin=[%s: %s] -chat.type.achievement=%s has just earned the achievement %s -chat.type.achievement.taken=%s has lost the achievement %s -chat.link.confirm=Are you sure you want to open the following website? -chat.link.warning=Never open links from people that you don't trust! -chat.copy=Copy to Clipboard -chat.link.confirmTrusted=Do you want to open this link or copy it to your clipboard? -chat.link.open=Open in browser - -chat.stream.text=(%s) <%s> %s -chat.stream.emote=(%s) * %s %s - -menu.playdemo=Play Demo World -menu.resetdemo=Reset Demo World - -demo.day.1=This demo will last five game days, do your best! -demo.day.2=Day Two -demo.day.3=Day Three -demo.day.4=Day Four -demo.day.5=This is your last day! -demo.day.warning=Your time is almost up! -demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation -demo.reminder=The demo time has expired, buy the game to continue or start a new world! -demo.remainingTime=Remaining time: %s -demo.demoExpired=Demo time's up! -demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around -demo.help.movementShort=Move by pressing %1$s, %2$s, %3$s, %4$s -demo.help.movementMouse=Look around using the mouse -demo.help.jump=Jump by pressing %1$s -demo.help.inventory=Use %1$s to open your inventory -demo.help.title=Minecraft Demo Mode -demo.help.fullWrapped=This demo will last 5 ingame days (about 1 hour and 40 minutes of real time). Check the achievements for hints! Have fun! -demo.help.buy=Purchase Now! -demo.help.later=Continue Playing! - -connect.connecting=Connecting to the server... -connect.authorizing=Logging in... -connect.failed=Failed to connect to the server - -disconnect.genericReason=%s -disconnect.disconnected=Disconnected by Server -disconnect.lost=Connection Lost -disconnect.kicked=Was kicked from the game -disconnect.timeout=Timed out -disconnect.closed=Connection closed -disconnect.loginFailed=Failed to login -disconnect.loginFailedInfo=Failed to login: %s -disconnect.loginFailedInfo.serversUnavailable=The authentication servers are currently down for maintenance. -disconnect.loginFailedInfo.invalidSession=Invalid session (Try restarting your game) -disconnect.quitting=Quitting -disconnect.endOfStream=End of stream -disconnect.overflow=Buffer overflow -disconnect.spam=Kicked for spamming - -soundCategory.master=Master Volume -soundCategory.music=Music -soundCategory.record=Jukebox/Noteblocks -soundCategory.weather=Weather -soundCategory.hostile=Hostile Creatures -soundCategory.neutral=Friendly Creatures -soundCategory.player=Players -soundCategory.block=Blocks -soundCategory.ambient=Ambient/Environment - -record.nowPlaying=Now playing: %s - -options.off=OFF -options.on=ON -options.visible=Shown -options.hidden=Hidden -options.title=Options -options.controls=Controls... -options.video=Video Settings... -options.language=Language... -options.stream=Broadcast Settings... -options.sounds=Music & Sounds... -options.sounds.title=Music & Sound Options -options.languageWarning=Language translations may not be 100%% accurate -options.videoTitle=Video Settings -options.customizeTitle=Customize World Settings -options.music=Music -options.sound=Sound -options.invertMouse=Invert Mouse -options.fov=FOV -options.fov.min=Normal -options.fov.max=Quake Pro -options.saturation=Saturation -options.gamma=Brightness -options.gamma.min=Moody -options.gamma.max=Bright -options.sensitivity=Sensitivity -options.sensitivity.min=*yawn* -options.sensitivity.max=HYPERSPEED!!! -options.renderDistance=Render Distance -options.renderDistance.tiny=Tiny -options.renderDistance.short=Short -options.renderDistance.normal=Normal -options.renderDistance.far=Far -options.viewBobbing=View Bobbing -options.ao=Smooth Lighting -options.ao.off=OFF -options.ao.min=Minimum -options.ao.max=Maximum -options.anaglyph=3D Anaglyph -options.framerateLimit=Max Framerate -options.framerateLimit.max=Unlimited -options.difficulty=Difficulty -options.difficulty.peaceful=Peaceful -options.difficulty.easy=Easy -options.difficulty.normal=Normal -options.difficulty.hard=Hard -options.difficulty.hardcore=Hardcore -options.graphics=Graphics -options.graphics.fancy=Fancy -options.graphics.fast=Fast -options.guiScale=GUI Scale -options.guiScale.auto=Auto -options.guiScale.small=Small -options.guiScale.normal=Normal -options.guiScale.large=Large -options.advancedOpengl=Advanced OpenGL -options.fboEnable=Enable FBOs -options.postProcessEnable=Enable Post-Processing -options.renderClouds=Clouds -options.qualityButton=Video Quality Settings... -options.qualityVideoTitle=Video Quality Settings -options.performanceButton=Video Performance Settings... -options.performanceVideoTitle=Video Performance Settings -options.advancedButton=Advanced Video Settings... -options.advancedVideoTitle=Advanced Video Settings -options.postButton=Post-Processing Settings... -options.postVideoTitle=Post-Processing Settings -options.farWarning1=A 64 bit Java installation is recommended -options.farWarning2=for 'Far' render distance (you have 32 bit) -options.particles=Particles -options.particles.all=All -options.particles.decreased=Decreased -options.particles.minimal=Minimal -options.multiplayer.title=Multiplayer Settings... -options.chat.title=Chat Settings... -options.chat.visibility=Chat -options.chat.visibility.full=Shown -options.chat.visibility.system=Commands Only -options.chat.visibility.hidden=Hidden -options.chat.color=Colors -options.chat.opacity=Opacity -options.chat.links=Web Links -options.chat.links.prompt=Prompt on Links -options.chat.scale=Scale -options.chat.width=Width -options.chat.height.focused=Focused Height -options.chat.height.unfocused=Unfocused Height -options.skinCustomisation=Skin Customization... -options.skinCustomisation.title=Skin Customization -options.skinCustomisation.enableFNAWSkins=Show FNAW Skins -options.modelPart.cape=Cape -options.modelPart.hat=Hat -options.modelPart.jacket=Jacket -options.modelPart.left_sleeve=Left Sleeve -options.modelPart.right_sleeve=Right Sleeve -options.modelPart.left_pants_leg=Left Pants Leg -options.modelPart.right_pants_leg=Right Pants Leg -options.snooper=Allow Snooper -options.snooper.view=Snooper Settings... -options.snooper.title=Machine Specs Collection -options.snooper.desc=We want to collect information about your machine to help improve Minecraft by knowing what we can support and where the biggest problems are. All of this information is completely anonymous and viewable below. We promise we won't do anything bad with this data, but if you want to opt out then feel free to toggle it off! -options.resourcepack=Resource Packs... -options.fullscreen=Fullscreen -options.vsync=Use VSync -options.vbo=Use VBOs -options.touchscreen=Touchscreen Mode -options.blockAlternatives=Alternate Blocks -options.reducedDebugInfo=Reduced Debug Info -options.entityShadows=Entity Shadows - -options.mipmapLevels=Mipmap Levels -options.forceUnicodeFont=Force Unicode Font - -options.stream.title=Twitch Broadcast Settings -options.stream.bytesPerPixel=Quality -options.stream.micVolumne=Mic Volume -options.stream.micToggleBehavior=Push To -options.stream.mic_toggle.mute=Mute -options.stream.mic_toggle.talk=Talk -options.stream.systemVolume=System Volume -options.stream.kbps=Bandwidth -options.stream.fps=Framerate -options.stream.sendMetadata=Send Metadata -options.stream.compression=Compression -options.stream.compression.low=Low -options.stream.compression.medium=Medium -options.stream.compression.high=High -options.stream.estimation=Estimated resolution: %dx%d -options.stream.changes=You may need to restart your stream for these changes to take place. -options.stream.ingestSelection=Broadcast Server List -options.stream.ingest.title=Twitch Broadcast Servers -options.stream.ingest.reset=Reset Preference -options.stream.chat.title=Twitch Chat Settings -options.stream.chat.enabled=Enable -options.stream.chat.enabled.streaming=Whilst Streaming -options.stream.chat.enabled.always=Always -options.stream.chat.enabled.never=Never -options.stream.chat.userFilter=User Filter -options.stream.chat.userFilter.all=All Viewers -options.stream.chat.userFilter.subs=Subscribers -options.stream.chat.userFilter.mods=Moderators - -difficulty.lock.title=Lock World Difficulty -difficulty.lock.question=Are you sure you want to lock the difficulty of this world? This will set this world to always be %1$s, and you will never be able to change that again. - -title.oldgl1=Old graphics card detected; this may prevent you from -title.oldgl2=playing in the future as OpenGL 2.0 will be required. - -controls.title=Controls -controls.reset=Reset -controls.resetAll=Reset Keys - -key.sprint=Sprint -key.forward=Walk Forwards -key.left=Strafe Left -key.back=Walk Backwards -key.right=Strafe Right -key.jump=Jump -key.inventory=Inventory -key.drop=Drop Item -key.chat=Open Chat -key.sneak=Sneak -key.playerlist=List Players -key.attack=Attack/Destroy -key.use=Use Item/Place Block -key.pickItem=Pick Block -key.mouseButton=Button %1$s -key.command=Open Command -key.screenshot=Take Screenshot -key.togglePerspective=Toggle Perspective -key.smoothCamera=Toggle Cinematic Camera -key.fullscreen=Toggle Fullscreen -key.spectatorOutlines=Highlight Players (Spectators) -key.hotbar.1=Hotbar Slot 1 -key.hotbar.2=Hotbar Slot 2 -key.hotbar.3=Hotbar Slot 3 -key.hotbar.4=Hotbar Slot 4 -key.hotbar.5=Hotbar Slot 5 -key.hotbar.6=Hotbar Slot 6 -key.hotbar.7=Hotbar Slot 7 -key.hotbar.8=Hotbar Slot 8 -key.hotbar.9=Hotbar Slot 9 -key.streamStartStop=Start/Stop Stream -key.streamPauseUnpause=Pause/Unpause Stream -key.streamCommercial=Show Stream Commercials -key.streamToggleMic=Push To Talk/Mute - -key.categories.movement=Movement -key.categories.misc=Miscellaneous -key.categories.multiplayer=Multiplayer -key.categories.gameplay=Gameplay -key.categories.ui=Game Interface -key.categories.inventory=Inventory -key.categories.stream=Streaming - -resourcePack.openFolder=Open resource pack -resourcePack.title=Select Resource Packs -resourcePack.available.title=Available Resource Packs -resourcePack.selected.title=Selected Resource Packs -resourcePack.folderInfo=(Select resource pack files here) -resourcePack.incompatible=Incompatible -resourcePack.incompatible.old=(Made for an older version of Minecraft) -resourcePack.incompatible.new=(Made for a newer version of Minecraft) -resourcePack.incompatible.confirm.title=Are you sure you want to load this resource pack? -resourcePack.incompatible.confirm.old=This resource pack was made for an older version of Minecraft and may no longer work correctly. -resourcePack.incompatible.confirm.new=This resource pack was made for a newer version of Minecraft and may no longer work correctly. - -sign.edit=Edit sign message - -book.pageIndicator=Page %1$s of %2$s -book.byAuthor=by %1$s -book.signButton=Sign -book.editTitle=Enter Book Title: -book.finalizeButton=Sign and Close -book.finalizeWarning=Note! When you sign the book, it will no longer be editable. -book.generation.0=Original -book.generation.1=Copy of original -book.generation.2=Copy of a copy -book.generation.3=Tattered - -merchant.deprecated=Trade something else to unlock! - -tile.barrier.name=Barrier -tile.stone.stone.name=Stone -tile.stone.granite.name=Granite -tile.stone.graniteSmooth.name=Polished Granite -tile.stone.diorite.name=Diorite -tile.stone.dioriteSmooth.name=Polished Diorite -tile.stone.andesite.name=Andesite -tile.stone.andesiteSmooth.name=Polished Andesite -tile.hayBlock.name=Hay Bale -tile.grass.name=Grass Block -tile.dirt.name=Dirt -tile.dirt.default.name=Dirt -tile.dirt.coarse.name=Coarse Dirt -tile.dirt.podzol.name=Podzol -tile.stonebrick.name=Cobblestone -tile.wood.name=Wooden Planks -tile.wood.oak.name=Oak Wood Planks -tile.wood.spruce.name=Spruce Wood Planks -tile.wood.birch.name=Birch Wood Planks -tile.wood.jungle.name=Jungle Wood Planks -tile.wood.acacia.name=Acacia Wood Planks -tile.wood.big_oak.name=Dark Oak Wood Planks -tile.sapling.oak.name=Oak Sapling -tile.sapling.spruce.name=Spruce Sapling -tile.sapling.birch.name=Birch Sapling -tile.sapling.jungle.name=Jungle Sapling -tile.sapling.acacia.name=Acacia Sapling -tile.sapling.big_oak.name=Dark Oak Sapling -tile.deadbush.name=Dead Bush -tile.bedrock.name=Bedrock -tile.water.name=Water -tile.lava.name=Lava -tile.sand.name=Sand -tile.sand.default.name=Sand -tile.sand.red.name=Red Sand -tile.sandStone.name=Sandstone -tile.sandStone.default.name=Sandstone -tile.sandStone.chiseled.name=Chiseled Sandstone -tile.sandStone.smooth.name=Smooth Sandstone -tile.redSandStone.name=Red Sandstone -tile.redSandStone.default.name=Red Sandstone -tile.redSandStone.chiseled.name=Chiseled Red Sandstone -tile.redSandStone.smooth.name=Smooth Red Sandstone -tile.gravel.name=Gravel -tile.oreGold.name=Gold Ore -tile.oreIron.name=Iron Ore -tile.oreCoal.name=Coal Ore -tile.log.name=Wood -tile.log.oak.name=Oak Wood -tile.log.spruce.name=Spruce Wood -tile.log.birch.name=Birch Wood -tile.log.jungle.name=Jungle Wood -tile.log.acacia.name=Acacia Wood -tile.log.big_oak.name=Dark Oak Wood -tile.leaves.name=Leaves -tile.leaves.oak.name=Oak Leaves -tile.leaves.spruce.name=Spruce Leaves -tile.leaves.birch.name=Birch Leaves -tile.leaves.jungle.name=Jungle Leaves -tile.leaves.acacia.name=Acacia Leaves -tile.leaves.big_oak.name=Dark Oak Leaves -tile.tallgrass.name=Grass -tile.tallgrass.shrub.name=Shrub -tile.tallgrass.grass.name=Grass -tile.tallgrass.fern.name=Fern -tile.sponge.dry.name=Sponge -tile.sponge.wet.name=Wet Sponge -tile.glass.name=Glass -tile.stainedGlass.name=Stained Glass -tile.stainedGlass.black.name=Black Stained Glass -tile.stainedGlass.red.name=Red Stained Glass -tile.stainedGlass.green.name=Green Stained Glass -tile.stainedGlass.brown.name=Brown Stained Glass -tile.stainedGlass.blue.name=Blue Stained Glass -tile.stainedGlass.purple.name=Purple Stained Glass -tile.stainedGlass.cyan.name=Cyan Stained Glass -tile.stainedGlass.silver.name=Light Gray Stained Glass -tile.stainedGlass.gray.name=Gray Stained Glass -tile.stainedGlass.pink.name=Pink Stained Glass -tile.stainedGlass.lime.name=Lime Stained Glass -tile.stainedGlass.yellow.name=Yellow Stained Glass -tile.stainedGlass.lightBlue.name=Light Blue Stained Glass -tile.stainedGlass.magenta.name=Magenta Stained Glass -tile.stainedGlass.orange.name=Orange Stained Glass -tile.stainedGlass.white.name=White Stained Glass -tile.thinStainedGlass.name=Stained Glass Pane -tile.thinStainedGlass.black.name=Black Stained Glass Pane -tile.thinStainedGlass.red.name=Red Stained Glass Pane -tile.thinStainedGlass.green.name=Green Stained Glass Pane -tile.thinStainedGlass.brown.name=Brown Stained Glass Pane -tile.thinStainedGlass.blue.name=Blue Stained Glass Pane -tile.thinStainedGlass.purple.name=Purple Stained Glass Pane -tile.thinStainedGlass.cyan.name=Cyan Stained Glass Pane -tile.thinStainedGlass.silver.name=Light Gray Stained Glass Pane -tile.thinStainedGlass.gray.name=Gray Stained Glass Pane -tile.thinStainedGlass.pink.name=Pink Stained Glass Pane -tile.thinStainedGlass.lime.name=Lime Stained Glass Pane -tile.thinStainedGlass.yellow.name=Yellow Stained Glass Pane -tile.thinStainedGlass.lightBlue.name=Light Blue Stained Glass Pane -tile.thinStainedGlass.magenta.name=Magenta Stained Glass Pane -tile.thinStainedGlass.orange.name=Orange Stained Glass Pane -tile.thinStainedGlass.white.name=White Stained Glass Pane -tile.thinGlass.name=Glass Pane -tile.cloth.name=Wool -tile.flower1.name=Flower -tile.flower1.dandelion.name=Dandelion -tile.flower2.name=Flower -tile.flower2.poppy.name=Poppy -tile.flower2.blueOrchid.name=Blue Orchid -tile.flower2.allium.name=Allium -tile.flower2.houstonia.name=Azure Bluet -tile.flower2.tulipRed.name=Red Tulip -tile.flower2.tulipOrange.name=Orange Tulip -tile.flower2.tulipWhite.name=White Tulip -tile.flower2.tulipPink.name=Pink Tulip -tile.flower2.oxeyeDaisy.name=Oxeye Daisy -tile.doublePlant.name=Plant -tile.doublePlant.sunflower.name=Sunflower -tile.doublePlant.syringa.name=Lilac -tile.doublePlant.grass.name=Double Tallgrass -tile.doublePlant.fern.name=Large Fern -tile.doublePlant.rose.name=Rose Bush -tile.doublePlant.paeonia.name=Peony -tile.mushroom.name=Mushroom -tile.blockGold.name=Block of Gold -tile.blockIron.name=Block of Iron -tile.stoneSlab.name=Stone Slab -tile.stoneSlab.stone.name=Stone Slab -tile.stoneSlab.sand.name=Sandstone Slab -tile.stoneSlab.wood.name=Wooden Slab -tile.stoneSlab.cobble.name=Cobblestone Slab -tile.stoneSlab.brick.name=Bricks Slab -tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab -tile.stoneSlab.netherBrick.name=Nether Brick Slab -tile.stoneSlab.quartz.name=Quartz Slab -tile.stoneSlab2.red_sandstone.name=Red Sandstone Slab -tile.woodSlab.name=Wood Slab -tile.woodSlab.oak.name=Oak Wood Slab -tile.woodSlab.spruce.name=Spruce Wood Slab -tile.woodSlab.birch.name=Birch Wood Slab -tile.woodSlab.jungle.name=Jungle Wood Slab -tile.woodSlab.acacia.name=Acacia Wood Slab -tile.woodSlab.big_oak.name=Dark Oak Wood Slab -tile.brick.name=Bricks -tile.tnt.name=TNT -tile.bookshelf.name=Bookshelf -tile.stoneMoss.name=Moss Stone -tile.obsidian.name=Obsidian -tile.torch.name=Torch -tile.fire.name=Fire -tile.mobSpawner.name=Monster Spawner -tile.stairsWood.name=Oak Wood Stairs -tile.stairsWoodSpruce.name=Spruce Wood Stairs -tile.stairsWoodBirch.name=Birch Wood Stairs -tile.stairsWoodJungle.name=Jungle Wood Stairs -tile.stairsWoodAcacia.name=Acacia Wood Stairs -tile.stairsWoodDarkOak.name=Dark Oak Wood Stairs -tile.chest.name=Chest -tile.chestTrap.name=Trapped Chest -tile.redstoneDust.name=Redstone Dust -tile.oreDiamond.name=Diamond Ore -tile.blockCoal.name=Block of Coal -tile.blockDiamond.name=Block of Diamond -tile.workbench.name=Crafting Table -tile.crops.name=Crops -tile.farmland.name=Farmland -tile.furnace.name=Furnace -tile.sign.name=Sign -tile.doorWood.name=Wooden Door -tile.ladder.name=Ladder -tile.rail.name=Rail -tile.goldenRail.name=Powered Rail -tile.activatorRail.name=Activator Rail -tile.detectorRail.name=Detector Rail -tile.stairsStone.name=Cobblestone Stairs -tile.stairsSandStone.name=Sandstone Stairs -tile.stairsRedSandStone.name=Red Sandstone Stairs -tile.lever.name=Lever -tile.pressurePlateStone.name=Stone Pressure Plate -tile.pressurePlateWood.name=Wooden Pressure Plate -tile.weightedPlate_light.name=Weighted Pressure Plate (Light) -tile.weightedPlate_heavy.name=Weighted Pressure Plate (Heavy) -tile.doorIron.name=Iron Door -tile.oreRedstone.name=Redstone Ore -tile.notGate.name=Redstone Torch -tile.button.name=Button -tile.snow.name=Snow -tile.woolCarpet.name=Carpet -tile.woolCarpet.black.name=Black Carpet -tile.woolCarpet.red.name=Red Carpet -tile.woolCarpet.green.name=Green Carpet -tile.woolCarpet.brown.name=Brown Carpet -tile.woolCarpet.blue.name=Blue Carpet -tile.woolCarpet.purple.name=Purple Carpet -tile.woolCarpet.cyan.name=Cyan Carpet -tile.woolCarpet.silver.name=Light Gray Carpet -tile.woolCarpet.gray.name=Gray Carpet -tile.woolCarpet.pink.name=Pink Carpet -tile.woolCarpet.lime.name=Lime Carpet -tile.woolCarpet.yellow.name=Yellow Carpet -tile.woolCarpet.lightBlue.name=Light Blue Carpet -tile.woolCarpet.magenta.name=Magenta Carpet -tile.woolCarpet.orange.name=Orange Carpet -tile.woolCarpet.white.name=Carpet -tile.ice.name=Ice -tile.frostedIce.name=Frosted Ice -tile.icePacked.name=Packed Ice -tile.cactus.name=Cactus -tile.clay.name=Clay -tile.clayHardenedStained.name=Stained Clay -tile.clayHardenedStained.black.name=Black Stained Clay -tile.clayHardenedStained.red.name=Red Stained Clay -tile.clayHardenedStained.green.name=Green Stained Clay -tile.clayHardenedStained.brown.name=Brown Stained Clay -tile.clayHardenedStained.blue.name=Blue Stained Clay -tile.clayHardenedStained.purple.name=Purple Stained Clay -tile.clayHardenedStained.cyan.name=Cyan Stained Clay -tile.clayHardenedStained.silver.name=Light Gray Stained Clay -tile.clayHardenedStained.gray.name=Gray Stained Clay -tile.clayHardenedStained.pink.name=Pink Stained Clay -tile.clayHardenedStained.lime.name=Lime Stained Clay -tile.clayHardenedStained.yellow.name=Yellow Stained Clay -tile.clayHardenedStained.lightBlue.name=Light Blue Stained Clay -tile.clayHardenedStained.magenta.name=Magenta Stained Clay -tile.clayHardenedStained.orange.name=Orange Stained Clay -tile.clayHardenedStained.white.name=White Stained Clay -tile.clayHardened.name=Hardened Clay -tile.reeds.name=Sugar cane -tile.jukebox.name=Jukebox -tile.fence.name=Oak Fence -tile.spruceFence.name=Spruce Fence -tile.birchFence.name=Birch Fence -tile.jungleFence.name=Jungle Fence -tile.darkOakFence.name=Dark Oak Fence -tile.acaciaFence.name=Acacia Fence -tile.fenceGate.name=Oak Fence Gate -tile.spruceFenceGate.name=Spruce Fence Gate -tile.birchFenceGate.name=Birch Fence Gate -tile.jungleFenceGate.name=Jungle Fence Gate -tile.darkOakFenceGate.name=Dark Oak Fence Gate -tile.acaciaFenceGate.name=Acacia Fence Gate -tile.pumpkinStem.name=Pumpkin Stem -tile.pumpkin.name=Pumpkin -tile.litpumpkin.name=Jack o'Lantern -tile.hellrock.name=Netherrack -tile.hellsand.name=Soul Sand -tile.lightgem.name=Glowstone -tile.portal.name=Portal -tile.cloth.black.name=Black Wool -tile.cloth.red.name=Red Wool -tile.cloth.green.name=Green Wool -tile.cloth.brown.name=Brown Wool -tile.cloth.blue.name=Blue Wool -tile.cloth.purple.name=Purple Wool -tile.cloth.cyan.name=Cyan Wool -tile.cloth.silver.name=Light Gray Wool -tile.cloth.gray.name=Gray Wool -tile.cloth.pink.name=Pink Wool -tile.cloth.lime.name=Lime Wool -tile.cloth.yellow.name=Yellow Wool -tile.cloth.lightBlue.name=Light Blue Wool -tile.cloth.magenta.name=Magenta Wool -tile.cloth.orange.name=Orange Wool -tile.cloth.white.name=Wool -tile.oreLapis.name=Lapis Lazuli Ore -tile.blockLapis.name=Lapis Lazuli Block -tile.dispenser.name=Dispenser -tile.dropper.name=Dropper -tile.musicBlock.name=Note Block -tile.cake.name=Cake -tile.bed.name=Bed -tile.bed.occupied=This bed is occupied -tile.bed.noSleep=You can only sleep at night -tile.bed.notSafe=You may not rest now, there are monsters nearby -tile.bed.notValid=Your home bed was missing or obstructed -tile.lockedchest.name=Locked chest -tile.trapdoor.name=Wooden Trapdoor -tile.ironTrapdoor.name=Iron Trapdoor -tile.web.name=Cobweb -tile.stonebricksmooth.name=Stone Bricks -tile.stonebricksmooth.default.name=Stone Bricks -tile.stonebricksmooth.mossy.name=Mossy Stone Bricks -tile.stonebricksmooth.cracked.name=Cracked Stone Bricks -tile.stonebricksmooth.chiseled.name=Chiseled Stone Bricks -tile.monsterStoneEgg.name=Stone Monster Egg -tile.monsterStoneEgg.stone.name=Stone Monster Egg -tile.monsterStoneEgg.cobble.name=Cobblestone Monster Egg -tile.monsterStoneEgg.brick.name=Stone Brick Monster Egg -tile.monsterStoneEgg.mossybrick.name=Mossy Stone Brick Monster Egg -tile.monsterStoneEgg.crackedbrick.name=Cracked Stone Brick Monster Egg -tile.monsterStoneEgg.chiseledbrick.name=Chiseled Stone Brick Monster Egg -tile.pistonBase.name=Piston -tile.pistonStickyBase.name=Sticky Piston -tile.fenceIron.name=Iron Bars -tile.melon.name=Melon -tile.stairsBrick.name=Brick Stairs -tile.stairsStoneBrickSmooth.name=Stone Brick Stairs -tile.vine.name=Vines -tile.netherBrick.name=Nether Brick -tile.netherFence.name=Nether Brick Fence -tile.stairsNetherBrick.name=Nether Brick Stairs -tile.netherStalk.name=Nether Wart -tile.cauldron.name=Cauldron -tile.enchantmentTable.name=Enchantment Table -tile.anvil.name=Anvil -tile.anvil.intact.name=Anvil -tile.anvil.slightlyDamaged.name=Slightly Damaged Anvil -tile.anvil.veryDamaged.name=Very Damaged Anvil -tile.whiteStone.name=End Stone -tile.endPortalFrame.name=End Portal -tile.mycel.name=Mycelium -tile.waterlily.name=Lily Pad -tile.dragonEgg.name=Dragon Egg -tile.redstoneLight.name=Redstone Lamp -tile.cocoa.name=Cocoa -tile.enderChest.name=Ender Chest -tile.oreRuby.name=Ruby Ore -tile.oreEmerald.name=Emerald Ore -tile.blockEmerald.name=Block of Emerald -tile.blockRedstone.name=Block of Redstone -tile.tripWire.name=Tripwire -tile.tripWireSource.name=Tripwire Hook -tile.commandBlock.name=Command Block -tile.beacon.name=Beacon -tile.beacon.primary=Primary Power -tile.beacon.secondary=Secondary Power -tile.cobbleWall.normal.name=Cobblestone Wall -tile.cobbleWall.mossy.name=Mossy Cobblestone Wall -tile.carrots.name=Carrots -tile.potatoes.name=Potatoes -tile.daylightDetector.name=Daylight Sensor -tile.netherquartz.name=Nether Quartz Ore -tile.hopper.name=Hopper -tile.quartzBlock.name=Block of Quartz -tile.quartzBlock.default.name=Block of Quartz -tile.quartzBlock.chiseled.name=Chiseled Quartz Block -tile.quartzBlock.lines.name=Pillar Quartz Block -tile.stairsQuartz.name=Quartz Stairs -tile.slime.name=Slime Block -tile.prismarine.rough.name=Prismarine -tile.prismarine.bricks.name=Prismarine Bricks -tile.prismarine.dark.name=Dark Prismarine -tile.seaLantern.name=Sea Lantern - -item.nameTag.name=Name Tag -item.leash.name=Lead -item.shovelIron.name=Iron Shovel -item.pickaxeIron.name=Iron Pickaxe -item.hatchetIron.name=Iron Axe -item.flintAndSteel.name=Flint and Steel -item.apple.name=Apple -item.cookie.name=Cookie -item.bow.name=Bow -item.arrow.name=Arrow -item.coal.name=Coal -item.charcoal.name=Charcoal -item.diamond.name=Diamond -item.emerald.name=Emerald -item.ingotIron.name=Iron Ingot -item.ingotGold.name=Gold Ingot -item.swordIron.name=Iron Sword -item.swordWood.name=Wooden Sword -item.shovelWood.name=Wooden Shovel -item.pickaxeWood.name=Wooden Pickaxe -item.hatchetWood.name=Wooden Axe -item.swordStone.name=Stone Sword -item.shovelStone.name=Stone Shovel -item.pickaxeStone.name=Stone Pickaxe -item.hatchetStone.name=Stone Axe -item.swordDiamond.name=Diamond Sword -item.shovelDiamond.name=Diamond Shovel -item.pickaxeDiamond.name=Diamond Pickaxe -item.hatchetDiamond.name=Diamond Axe -item.stick.name=Stick -item.bowl.name=Bowl -item.mushroomStew.name=Mushroom Stew -item.swordGold.name=Golden Sword -item.shovelGold.name=Golden Shovel -item.pickaxeGold.name=Golden Pickaxe -item.hatchetGold.name=Golden Axe -item.string.name=String -item.feather.name=Feather -item.sulphur.name=Gunpowder -item.hoeWood.name=Wooden Hoe -item.hoeStone.name=Stone Hoe -item.hoeIron.name=Iron Hoe -item.hoeDiamond.name=Diamond Hoe -item.hoeGold.name=Golden Hoe -item.seeds.name=Seeds -item.seeds_pumpkin.name=Pumpkin Seeds -item.seeds_melon.name=Melon Seeds -item.melon.name=Melon -item.wheat.name=Wheat -item.bread.name=Bread -item.helmetCloth.name=Leather Cap -item.chestplateCloth.name=Leather Tunic -item.leggingsCloth.name=Leather Pants -item.bootsCloth.name=Leather Boots -item.helmetChain.name=Chain Helmet -item.chestplateChain.name=Chain Chestplate -item.leggingsChain.name=Chain Leggings -item.bootsChain.name=Chain Boots -item.helmetIron.name=Iron Helmet -item.chestplateIron.name=Iron Chestplate -item.leggingsIron.name=Iron Leggings -item.bootsIron.name=Iron Boots -item.helmetDiamond.name=Diamond Helmet -item.chestplateDiamond.name=Diamond Chestplate -item.leggingsDiamond.name=Diamond Leggings -item.bootsDiamond.name=Diamond Boots -item.helmetGold.name=Golden Helmet -item.chestplateGold.name=Golden Chestplate -item.leggingsGold.name=Golden Leggings -item.bootsGold.name=Golden Boots -item.flint.name=Flint -item.porkchopRaw.name=Raw Porkchop -item.porkchopCooked.name=Cooked Porkchop -item.chickenRaw.name=Raw Chicken -item.chickenCooked.name=Cooked Chicken -item.muttonRaw.name=Raw Mutton -item.muttonCooked.name=Cooked Mutton -item.rabbitRaw.name=Raw Rabbit -item.rabbitCooked.name=Cooked Rabbit -item.rabbitStew.name=Rabbit Stew -item.rabbitFoot.name=Rabbit's Foot -item.rabbitHide.name=Rabbit Hide -item.beefRaw.name=Raw Beef -item.beefCooked.name=Steak -item.painting.name=Painting -item.frame.name=Item Frame -item.appleGold.name=Golden Apple -item.sign.name=Sign -item.doorOak.name=Oak Door -item.doorSpruce.name=Spruce Door -item.doorBirch.name=Birch Door -item.doorJungle.name=Jungle Door -item.doorAcacia.name=Acacia Door -item.doorDarkOak.name=Dark Oak Door -item.bucket.name=Bucket -item.bucketWater.name=Water Bucket -item.bucketLava.name=Lava Bucket -item.minecart.name=Minecart -item.saddle.name=Saddle -item.doorIron.name=Iron Door -item.redstone.name=Redstone -item.snowball.name=Snowball -item.boat.name=Boat -item.leather.name=Leather -item.milk.name=Milk -item.brick.name=Brick -item.clay.name=Clay -item.reeds.name=Sugar Canes -item.paper.name=Paper -item.book.name=Book -item.slimeball.name=Slimeball -item.minecartChest.name=Minecart with Chest -item.minecartFurnace.name=Minecart with Furnace -item.minecartTnt.name=Minecart with TNT -item.minecartHopper.name=Minecart with Hopper -item.minecartCommandBlock.name=Minecart with Command Block -item.egg.name=Egg -item.compass.name=Compass -item.fishingRod.name=Fishing Rod -item.clock.name=Clock -item.yellowDust.name=Glowstone Dust -item.fish.cod.raw.name=Raw Fish -item.fish.salmon.raw.name=Raw Salmon -item.fish.pufferfish.raw.name=Pufferfish -item.fish.clownfish.raw.name=Clownfish -item.fish.cod.cooked.name=Cooked Fish -item.fish.salmon.cooked.name=Cooked Salmon -item.record.name=Music Disc -item.record.13.desc=C418 - 13 -item.record.cat.desc=C418 - cat -item.record.blocks.desc=C418 - blocks -item.record.chirp.desc=C418 - chirp -item.record.far.desc=C418 - far -item.record.mall.desc=C418 - mall -item.record.mellohi.desc=C418 - mellohi -item.record.stal.desc=C418 - stal -item.record.strad.desc=C418 - strad -item.record.ward.desc=C418 - ward -item.record.11.desc=C418 - 11 -item.record.wait.desc=C418 - wait -item.bone.name=Bone -item.dyePowder.black.name=Ink Sac -item.dyePowder.red.name=Rose Red -item.dyePowder.green.name=Cactus Green -item.dyePowder.brown.name=Cocoa Beans -item.dyePowder.blue.name=Lapis Lazuli -item.dyePowder.purple.name=Purple Dye -item.dyePowder.cyan.name=Cyan Dye -item.dyePowder.silver.name=Light Gray Dye -item.dyePowder.gray.name=Gray Dye -item.dyePowder.pink.name=Pink Dye -item.dyePowder.lime.name=Lime Dye -item.dyePowder.yellow.name=Dandelion Yellow -item.dyePowder.lightBlue.name=Light Blue Dye -item.dyePowder.magenta.name=Magenta Dye -item.dyePowder.orange.name=Orange Dye -item.dyePowder.white.name=Bone Meal -item.sugar.name=Sugar -item.cake.name=Cake -item.bed.name=Bed -item.diode.name=Redstone Repeater -item.comparator.name=Redstone Comparator -item.map.name=Map -item.leaves.name=Leaves -item.shears.name=Shears -item.rottenFlesh.name=Rotten Flesh -item.enderPearl.name=Ender Pearl -item.blazeRod.name=Blaze Rod -item.ghastTear.name=Ghast Tear -item.netherStalkSeeds.name=Nether Wart -item.potion.name=Potion -item.emptyPotion.name=Water Bottle -item.goldNugget.name=Gold Nugget -item.glassBottle.name=Glass Bottle -item.spiderEye.name=Spider Eye -item.fermentedSpiderEye.name=Fermented Spider Eye -item.blazePowder.name=Blaze Powder -item.magmaCream.name=Magma Cream -item.cauldron.name=Cauldron -item.brewingStand.name=Brewing Stand -item.eyeOfEnder.name=Eye of Ender -item.speckledMelon.name=Glistering Melon -item.monsterPlacer.name=Spawn -item.expBottle.name=Bottle o' Enchanting -item.fireball.name=Fire Charge -item.writingBook.name=Book and Quill -item.writtenBook.name=Written Book -item.ruby.name=Ruby -item.flowerPot.name=Flower Pot -item.emptyMap.name=Empty Map -item.carrots.name=Carrot -item.carrotGolden.name=Golden Carrot -item.potato.name=Potato -item.potatoBaked.name=Baked Potato -item.potatoPoisonous.name=Poisonous Potato -item.skull.skeleton.name=Skeleton Skull -item.skull.wither.name=Wither Skeleton Skull -item.skull.zombie.name=Zombie Head -item.skull.char.name=Head -item.skull.player.name=%s's Head -item.skull.creeper.name=Creeper Head -item.skull.dragon.name=Dragon Head -item.carrotOnAStick.name=Carrot on a Stick -item.netherStar.name=Nether Star -item.pumpkinPie.name=Pumpkin Pie -item.enchantedBook.name=Enchanted Book -item.fireworks.name=Firework Rocket -item.fireworks.flight=Flight Duration: -item.fireworksCharge.name=Firework Star -item.fireworksCharge.black=Black -item.fireworksCharge.red=Red -item.fireworksCharge.green=Green -item.fireworksCharge.brown=Brown -item.fireworksCharge.blue=Blue -item.fireworksCharge.purple=Purple -item.fireworksCharge.cyan=Cyan -item.fireworksCharge.silver=Light Gray -item.fireworksCharge.gray=Gray -item.fireworksCharge.pink=Pink -item.fireworksCharge.lime=Lime -item.fireworksCharge.yellow=Yellow -item.fireworksCharge.lightBlue=Light Blue -item.fireworksCharge.magenta=Magenta -item.fireworksCharge.orange=Orange -item.fireworksCharge.white=White -item.fireworksCharge.customColor=Custom -item.fireworksCharge.fadeTo=Fade to -item.fireworksCharge.flicker=Twinkle -item.fireworksCharge.trail=Trail -item.fireworksCharge.type.0=Small Ball -item.fireworksCharge.type.1=Large Ball -item.fireworksCharge.type.2=Star-shaped -item.fireworksCharge.type.3=Creeper-shaped -item.fireworksCharge.type.4=Burst -item.fireworksCharge.type=Unknown Shape -item.netherbrick.name=Nether Brick -item.netherquartz.name=Nether Quartz -item.armorStand.name=Armor Stand -item.horsearmormetal.name=Iron Horse Armor -item.horsearmorgold.name=Gold Horse Armor -item.horsearmordiamond.name=Diamond Horse Armor -item.prismarineShard.name=Prismarine Shard -item.prismarineCrystals.name=Prismarine Crystals - -container.inventory=Inventory -container.hopper=Item Hopper -container.crafting=Crafting -container.dispenser=Dispenser -container.dropper=Dropper -container.furnace=Furnace -container.enchant=Enchant -container.enchant.lapis.one=1 Lapis Lazuli -container.enchant.lapis.many=%d Lapis Lazuli -container.enchant.level.one=1 Enchantment Level -container.enchant.level.many=%d Enchantment Levels -container.enchant.clue=%s . . . ? -container.repair=Repair & Name -container.repair.cost=Enchantment Cost: %1$d -container.repair.expensive=Too Expensive! -container.creative=Item Selection -container.brewing=Brewing Stand -container.chest=Chest -container.chestDouble=Large Chest -container.minecart=Minecart -container.enderchest=Ender Chest -container.beacon=Beacon - -container.isLocked=%s is locked! - -item.dyed=Dyed -item.unbreakable=Unbreakable -item.canBreak=Can break: -item.canPlace=Can be placed on: - -entity.Item.name=Item -entity.XPOrb.name=Experience Orb -entity.SmallFireball.name=Small Fireball -entity.Fireball.name=Fireball -entity.ThrownPotion.name=Potion - -entity.Arrow.name=Arrow -entity.Snowball.name=Snowball -entity.Painting.name=Painting -entity.ArmorStand.name=Armor Stand - -entity.Mob.name=Mob -entity.Monster.name=Monster - -entity.Creeper.name=Creeper -entity.Skeleton.name=Skeleton -entity.Spider.name=Spider -entity.Giant.name=Giant -entity.Zombie.name=Zombie -entity.Slime.name=Slime -entity.Ghast.name=Ghast -entity.PigZombie.name=Zombie Pigman -entity.Enderman.name=Enderman -entity.Endermite.name=Endermite -entity.Silverfish.name=Silverfish -entity.CaveSpider.name=Cave Spider -entity.Blaze.name=Blaze -entity.LavaSlime.name=Magma Cube -entity.MushroomCow.name=Mooshroom -entity.Villager.name=Villager -entity.VillagerGolem.name=Iron Golem -entity.SnowMan.name=Snow Golem -entity.EnderDragon.name=Ender Dragon -entity.WitherBoss.name=Wither -entity.Witch.name=Witch -entity.Guardian.name=Guardian - -entity.Villager.farmer=Farmer -entity.Villager.fisherman=Fisherman -entity.Villager.shepherd=Shepherd -entity.Villager.fletcher=Fletcher -entity.Villager.librarian=Librarian -entity.Villager.cleric=Cleric -entity.Villager.armor=Armorer -entity.Villager.weapon=Weapon Smith -entity.Villager.tool=Tool Smith -entity.Villager.butcher=Butcher -entity.Villager.leather=Leatherworker - -entity.Pig.name=Pig -entity.Sheep.name=Sheep -entity.Cow.name=Cow -entity.Chicken.name=Chicken -entity.Squid.name=Squid -entity.Wolf.name=Wolf -entity.Ozelot.name=Ocelot -entity.Cat.name=Cat -entity.Bat.name=Bat -entity.EntityHorse.name=Horse -entity.horse.name=Horse -entity.donkey.name=Donkey -entity.mule.name=Mule -entity.skeletonhorse.name=Skeleton Horse -entity.zombiehorse.name=Zombie Horse -entity.Rabbit.name=Rabbit -entity.KillerBunny.name=The Killer Bunny - -entity.PrimedTnt.name=Block of TNT -entity.FallingSand.name=Falling Block - -entity.Minecart.name=Minecart -entity.Boat.name=Boat - -entity.generic.name=unknown - -death.fell.accident.ladder=%1$s fell off a ladder -death.fell.accident.vines=%1$s fell off some vines -death.fell.accident.water=%1$s fell out of the water -death.fell.accident.generic=%1$s fell from a high place -death.fell.killer=%1$s was doomed to fall -death.fell.assist=%1$s was doomed to fall by %2$s -death.fell.assist.item=%1$s was doomed to fall by %2$s using %3$s -death.fell.finish=%1$s fell too far and was finished by %2$s -death.fell.finish.item=%1$s fell too far and was finished by %2$s using %3$s - -death.attack.lightningBolt=%1$s was struck by lightning -death.attack.inFire=%1$s went up in flames -death.attack.inFire.player=%1$s walked into fire whilst fighting %2$s -death.attack.onFire=%1$s burned to death -death.attack.onFire.player=%1$s was burnt to a crisp whilst fighting %2$s -death.attack.lava=%1$s tried to swim in lava -death.attack.lava.player=%1$s tried to swim in lava to escape %2$s -death.attack.inWall=%1$s suffocated in a wall -death.attack.drown=%1$s drowned -death.attack.drown.player=%1$s drowned whilst trying to escape %2$s -death.attack.starve=%1$s starved to death -death.attack.cactus=%1$s was pricked to death -death.attack.cactus.player=%1$s walked into a cactus whilst trying to escape %2$s -death.attack.generic=%1$s died -death.attack.explosion=%1$s blew up -death.attack.explosion.player=%1$s was blown up by %2$s -death.attack.magic=%1$s was killed by magic -death.attack.wither=%1$s withered away -death.attack.anvil=%1$s was squashed by a falling anvil -death.attack.fallingBlock=%1$s was squashed by a falling block -death.attack.mob=%1$s was slain by %2$s -death.attack.player=%1$s was slain by %2$s -death.attack.player.item=%1$s was slain by %2$s using %3$s -death.attack.arrow=%1$s was shot by %2$s -death.attack.arrow.item=%1$s was shot by %2$s using %3$s -death.attack.fireball=%1$s was fireballed by %2$s -death.attack.fireball.item=%1$s was fireballed by %2$s using %3$s -death.attack.thrown=%1$s was pummeled by %2$s -death.attack.thrown.item=%1$s was pummeled by %2$s using %3$s -death.attack.indirectMagic=%1$s was killed by %2$s using magic -death.attack.indirectMagic.item=%1$s was killed by %2$s using %3$s -death.attack.thorns=%1$s was killed trying to hurt %2$s -death.attack.fall=%1$s hit the ground too hard -death.attack.outOfWorld=%1$s fell out of the world - -deathScreen.respawn=Respawn -deathScreen.deleteWorld=Delete world -deathScreen.titleScreen=Title screen -deathScreen.score=Score -deathScreen.title.hardcore=Game over! -deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! -deathScreen.title=You died! -deathScreen.leaveServer=Leave server -deathScreen.quit.confirm=Are you sure you want to quit? - -potion.effects.whenDrank=When Applied: -potion.empty=No Effects -potion.moveSpeed=Speed -potion.moveSlowdown=Slowness -potion.digSpeed=Haste -potion.digSlowDown=Mining Fatigue -potion.damageBoost=Strength -potion.heal=Instant Health -potion.harm=Instant Damage -potion.jump=Jump Boost -potion.confusion=Nausea -potion.regeneration=Regeneration -potion.resistance=Resistance -potion.fireResistance=Fire Resistance -potion.waterBreathing=Water Breathing -potion.invisibility=Invisibility -potion.blindness=Blindness -potion.nightVision=Night Vision -potion.hunger=Hunger -potion.weakness=Weakness -potion.poison=Poison -potion.wither=Wither -potion.healthBoost=Health Boost -potion.absorption=Absorption -potion.saturation=Saturation -potion.glowing=Glowing -potion.luck=Luck -potion.unluck=Bad Luck -potion.levitation=Levitation - -potion.moveSpeed.postfix=Potion of Swiftness -potion.moveSlowdown.postfix=Potion of Slowness -potion.digSpeed.postfix=Potion of Haste -potion.digSlowDown.postfix=Potion of Dullness -potion.damageBoost.postfix=Potion of Strength -potion.weakness.postfix=Potion of Weakness -potion.heal.postfix=Potion of Healing -potion.harm.postfix=Potion of Harming -potion.jump.postfix=Potion of Leaping -potion.confusion.postfix=Potion of Nausea -potion.regeneration.postfix=Potion of Regeneration -potion.resistance.postfix=Potion of Resistance -potion.fireResistance.postfix=Potion of Fire Resistance -potion.waterBreathing.postfix=Potion of Water Breathing -potion.invisibility.postfix=Potion of Invisibility -potion.blindness.postfix=Potion of Blindness -potion.nightVision.postfix=Potion of Night Vision -potion.hunger.postfix=Potion of Hunger -potion.poison.postfix=Potion of Poison -potion.wither.postfix=Potion of Decay -potion.healthBoost.postfix=Potion of Health Boost -potion.absorption.postfix=Potion of Absorption -potion.saturation.postfix=Potion of Saturation - -potion.potency.0= -potion.potency.1=II -potion.potency.2=III -potion.potency.3=IV - -potion.prefix.grenade=Splash -potion.prefix.mundane=Mundane -potion.prefix.uninteresting=Uninteresting -potion.prefix.bland=Bland -potion.prefix.clear=Clear -potion.prefix.milky=Milky -potion.prefix.diffuse=Diffuse -potion.prefix.artless=Artless -potion.prefix.thin=Thin -potion.prefix.awkward=Awkward -potion.prefix.flat=Flat -potion.prefix.bulky=Bulky -potion.prefix.bungling=Bungling -potion.prefix.buttered=Buttered -potion.prefix.smooth=Smooth -potion.prefix.suave=Suave -potion.prefix.debonair=Debonair -potion.prefix.thick=Thick -potion.prefix.elegant=Elegant -potion.prefix.fancy=Fancy -potion.prefix.charming=Charming -potion.prefix.dashing=Dashing -potion.prefix.refined=Refined -potion.prefix.cordial=Cordial -potion.prefix.sparkling=Sparkling -potion.prefix.potent=Potent -potion.prefix.foul=Foul -potion.prefix.odorless=Odorless -potion.prefix.rank=Rank -potion.prefix.harsh=Harsh -potion.prefix.acrid=Acrid -potion.prefix.gross=Gross -potion.prefix.stinky=Stinky - -enchantment.damage.all=Sharpness -enchantment.damage.undead=Smite -enchantment.damage.arthropods=Bane of Arthropods -enchantment.knockback=Knockback -enchantment.fire=Fire Aspect -enchantment.protect.all=Protection -enchantment.protect.fire=Fire Protection -enchantment.protect.fall=Feather Falling -enchantment.protect.explosion=Blast Protection -enchantment.protect.projectile=Projectile Protection -enchantment.oxygen=Respiration -enchantment.waterWorker=Aqua Affinity -enchantment.waterWalker=Depth Strider -enchantment.frostWalker=Frost Walker -enchantment.digging=Efficiency -enchantment.untouching=Silk Touch -enchantment.durability=Unbreaking -enchantment.lootBonus=Looting -enchantment.lootBonusDigger=Fortune -enchantment.lootBonusFishing=Luck of the Sea -enchantment.fishingSpeed=Lure -enchantment.arrowDamage=Power -enchantment.arrowFire=Flame -enchantment.arrowKnockback=Punch -enchantment.arrowInfinite=Infinity -enchantment.thorns=Thorns -enchantment.mending=Mending - -enchantment.level.1=I -enchantment.level.2=II -enchantment.level.3=III -enchantment.level.4=IV -enchantment.level.5=V -enchantment.level.6=VI -enchantment.level.7=VII -enchantment.level.8=VIII -enchantment.level.9=IX -enchantment.level.10=X - -gui.achievements=Achievements -gui.stats=Statistics - -stats.tooltip.type.achievement=Achievement -stats.tooltip.type.statistic=Statistic -stat.generalButton=General -stat.blocksButton=Blocks -stat.itemsButton=Items -stat.mobsButton=Mobs - -stat.used=Times Used -stat.mined=Times Mined -stat.depleted=Times Depleted -stat.crafted=Times Crafted -stat.entityKills=You killed %d %s -stat.entityKilledBy=%s killed you %d time(s) -stat.entityKills.none=You have never killed %s -stat.entityKilledBy.none=You have never been killed by %s - -stat.startGame=Times played -stat.createWorld=Worlds created -stat.loadWorld=Saves loaded -stat.joinMultiplayer=Multiplayer joins -stat.leaveGame=Games quit - -stat.playOneMinute=Minutes Played -stat.timeSinceDeath=Since Last Death - -stat.walkOneCm=Distance Walked -stat.crouchOneCm=Distance Crouched -stat.sprintOneCm=Distance Sprinted -stat.fallOneCm=Distance Fallen -stat.swimOneCm=Distance Swum -stat.flyOneCm=Distance Flown -stat.climbOneCm=Distance Climbed -stat.diveOneCm=Distance Dove -stat.minecartOneCm=Distance by Minecart -stat.boatOneCm=Distance by Boat -stat.pigOneCm=Distance by Pig -stat.horseOneCm=Distance by Horse -stat.jump=Jumps -stat.drop=Items Dropped - -stat.damageDealt=Damage Dealt -stat.damageTaken=Damage Taken -stat.deaths=Number of Deaths -stat.mobKills=Mob Kills -stat.animalsBred=Animals Bred -stat.playerKills=Player Kills -stat.fishCaught=Fish Caught -stat.treasureFished=Treasure Fished -stat.junkFished=Junk Fished -stat.talkedToVillager=Talked to Villagers -stat.tradedWithVillager=Traded with Villagers - -stat.cakeSlicesEaten=Cake Slices Eaten -stat.cauldronFilled=Cauldrons Filled -stat.cauldronUsed=Water Taken from Cauldron -stat.armorCleaned=Armor Pieces Cleaned -stat.bannerCleaned=Banners Cleaned -stat.brewingstandInteraction=Interactions with Brewing Stand -stat.beaconInteraction=Interactions with Beacon -stat.dropperInspected=Droppers Searched -stat.hopperInspected=Hoppers Searched -stat.dispenserInspected=Dispensers Searched -stat.noteblockPlayed=Noteblocks played -stat.noteblockTuned=Noteblocks tuned -stat.flowerPotted=Plants potted -stat.trappedChestTriggered=Trapped Chests Triggered -stat.enderchestOpened=Ender Chests Opened -stat.itemEnchanted=Items Enchanted -stat.recordPlayed=Records Played -stat.furnaceInteraction=Interactions with Furnace -stat.workbenchInteraction=Interactions with Crafting Table -stat.chestOpened=Chests Opened - -stat.mineBlock=%1$s Mined -stat.craftItem=%1$s Crafted -stat.useItem=%1$s Used -stat.breakItem=%1$s Depleted - -achievement.get=Achievement get! - -achievement.taken=Taken! -achievement.unknown=??? - -achievement.requires=Requires '%1$s' -achievement.openInventory=Taking Inventory -achievement.openInventory.desc=Press '%1$s' to open your inventory. -achievement.mineWood=Getting Wood -achievement.mineWood.desc=Attack a tree until a block of wood pops out -achievement.buildWorkBench=Benchmarking -achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks -achievement.buildPickaxe=Time to Mine! -achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe -achievement.buildFurnace=Hot Topic -achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks -achievement.acquireIron=Acquire Hardware -achievement.acquireIron.desc=Smelt an iron ingot -achievement.buildHoe=Time to Farm! -achievement.buildHoe.desc=Use planks and sticks to make a hoe -achievement.makeBread=Bake Bread -achievement.makeBread.desc=Turn wheat into bread -achievement.bakeCake=The Lie -achievement.bakeCake.desc=Wheat, sugar, milk and eggs! -achievement.buildBetterPickaxe=Getting an Upgrade -achievement.buildBetterPickaxe.desc=Construct a better pickaxe -achievement.overpowered=Overpowered -achievement.overpowered.desc=Build a Notch apple -achievement.cookFish=Delicious Fish -achievement.cookFish.desc=Catch and cook fish! -achievement.onARail=On A Rail -achievement.onARail.desc=Travel by minecart at least 1 km from where you started -achievement.buildSword=Time to Strike! -achievement.buildSword.desc=Use planks and sticks to make a sword -achievement.killEnemy=Monster Hunter -achievement.killEnemy.desc=Attack and destroy a monster -achievement.killCow=Cow Tipper -achievement.killCow.desc=Harvest some leather -achievement.breedCow=Repopulation -achievement.breedCow.desc=Breed two cows with wheat -achievement.flyPig=When Pigs Fly -achievement.flyPig.desc=Fly a pig off a cliff -achievement.snipeSkeleton=Sniper Duel -achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters -achievement.diamonds=DIAMONDS! -achievement.diamonds.desc=Acquire diamonds with your iron tools -achievement.diamondsToYou=Diamonds to you! -achievement.diamondsToYou.desc=Throw diamonds at another player. -achievement.portal=We Need to Go Deeper -achievement.portal.desc=Build a portal to the Nether -achievement.ghast=Return to Sender -achievement.ghast.desc=Destroy a Ghast with a fireball -achievement.blazeRod=Into Fire -achievement.blazeRod.desc=Relieve a Blaze of its rod -achievement.potion=Local Brewery -achievement.potion.desc=Brew a potion -achievement.theEnd=The End? -achievement.theEnd.desc=Locate the End -achievement.theEnd2=The End. -achievement.theEnd2.desc=Defeat the Ender Dragon -achievement.spawnWither=The Beginning? -achievement.spawnWither.desc=Spawn the Wither -achievement.killWither=The Beginning. -achievement.killWither.desc=Kill the Wither -achievement.fullBeacon=Beaconator -achievement.fullBeacon.desc=Create a full beacon -achievement.exploreAllBiomes=Adventuring Time -achievement.exploreAllBiomes.desc=Discover all biomes -achievement.enchantments=Enchanter -achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table -achievement.overkill=Overkill -achievement.overkill.desc=Deal nine hearts of damage in a single hit -achievement.bookcase=Librarian -achievement.bookcase.desc=Build some bookshelves to improve your enchantment table - -commands.generic.exception=An unknown error occurred while attempting to perform this command -commands.generic.permission=You do not have permission to use this command -commands.generic.syntax=Invalid command syntax -commands.generic.player.notFound=That player cannot be found -commands.generic.entity.notFound=That entity cannot be found -commands.generic.entity.invalidUuid=The entity UUID provided is in an invalid format -commands.generic.entity.invalidType=Entity type '%s' is invalid -commands.generic.notFound=Unknown command. Try /help for a list of commands -commands.generic.parameter.invalid='%s' is not a valid parameter -commands.generic.num.invalid='%s' is not a valid number -commands.generic.boolean.invalid='%s' is not true or false -commands.generic.num.tooSmall=The number you have entered (%d) is too small, it must be at least %d -commands.generic.num.tooBig=The number you have entered (%d) is too big, it must be at most %d -commands.generic.double.tooSmall=The number you have entered (%.2f) is too small, it must be at least %.2f -commands.generic.double.tooBig=The number you have entered (%.2f) is too big, it must be at most %.2f -commands.generic.usage=Usage: %s - -commands.setidletimeout.usage=/setidletimeout -commands.setidletimeout.success=Successfully set the idle timeout to %d minutes. -commands.xp.failure.widthdrawXp=Cannot give player negative experience points -commands.xp.success=Given %d experience to %s -commands.xp.success.levels=Given %d levels to %s -commands.xp.success.negative.levels=Taken %d levels from %s -commands.xp.usage=/xp [player] OR /xp L [player] -commands.playsound.usage=/playsound [x] [y] [z] [volume] [pitch] [minimumVolume] -commands.playsound.success=Played sound '%s' to %s -commands.playsound.playerTooFar=Player %s is too far away to hear the sound -commands.give.usage=/give [amount] [data] [dataTag] -commands.give.item.notFound=There is no such item with name %d -commands.give.block.notFound=There is no such block with name %d -commands.give.success=Given %s * %d to %s -commands.give.tagError=Data tag parsing failed: %s -commands.replaceitem.usage=/replaceitem ... -commands.replaceitem.entity.usage=/replaceitem entity [amount] [data] [dataTag] -commands.replaceitem.block.usage=/replaceitem block [amount] [data] [dataTag] -commands.replaceitem.tagError=Data tag parsing failed: %s -commands.replaceitem.noContainer=Block at %d, %d, %d is not a container -commands.replaceitem.failed=Could not replace slot %d with %d * %s -commands.replaceitem.success=Replaced slot %d with %d * %s -commands.stats.usage=/stats ... -commands.stats.entity.usage=/stats entity -commands.stats.entity.set.usage=/stats entity set -commands.stats.entity.clear.usage=/stats entity clear -commands.stats.block.usage=/stats block ... -commands.stats.block.set.usage=/stats block set -commands.stats.block.clear.usage=/stats block clear -commands.stats.noCompatibleBlock=Block at %d, %d, %d can not track stats -commands.stats.failed=Invalid parameters -commands.stats.cleared=Cleared %s stats -commands.stats.success=Storing %s stats in %s on %s -commands.summon.usage=/summon [x] [y] [z] [dataTag] -commands.summon.success=Object successfully summoned -commands.summon.failed=Unable to summon object -commands.summon.tagError=Data tag parsing failed: %s -commands.summon.outOfWorld=Cannot summon the object out of the world -commands.testforblock.usage=/testforblock [dataValue] [dataTag] -commands.testforblock.failed.tile=The block at %d,%d,%d is %s (expected: %s). -commands.testforblock.failed.data=The block at %d,%d,%d had the data value of %s (expected: %s). -commands.testforblock.failed.nbt=The block at %d,%d,%d did not have the required NBT keys. -commands.testforblock.failed.tileEntity=The block at %d,%d,%d is not a tile entity and cannot support tag matching. -commands.testforblock.success=Successfully found the block at %d,%d,%d. -commands.testforblock.outOfWorld=Cannot test for block outside of the world -commands.setblock.usage=/setblock [dataValue] [oldBlockHandling] [dataTag] -commands.setblock.success=Block placed -commands.setblock.failed=Unable to place block -commands.setblock.tagError=Data tag parsing failed: %s -commands.setblock.outOfWorld=Cannot place block outside of the world -commands.setblock.notFound=There is no such block with ID/name %s -commands.setblock.noChange=The block couldn't be placed -commands.fill.usage=/fill [dataValue] [oldBlockHandling] [dataTag] -commands.fill.outOfWorld=Cannot place blocks outside of the world -commands.fill.tagError=Data tag parsing failed: %s -commands.fill.success=%d blocks filled -commands.fill.failed=No blocks filled -commands.fill.tooManyBlocks=Too many blocks in the specified area (%d > %d) -commands.clone.usage=/clone [maskMode] [cloneMode] -commands.clone.outOfWorld=Cannot access blocks outside of the world -commands.clone.noOverlap=Source and destination can not overlap -commands.clone.success=%d blocks cloned -commands.clone.failed=No blocks cloned -commands.clone.tooManyBlocks=Too many blocks in the specified area (%d > %d) -commands.compare.usage=/testforblocks [mode] -commands.compare.outOfWorld=Cannot access blocks outside of the world -commands.compare.failed=Source and destination are not identical -commands.compare.success=%d blocks compared -commands.compare.tooManyBlocks=Too many blocks in the specified area (%d > %d) -commands.blockdata.usage=/blockdata -commands.blockdata.success=Block data updated to: %s -commands.blockdata.tagError=Data tag parsing failed: %s -commands.blockdata.outOfWorld=Cannot change block outside of the world -commands.blockdata.notValid=The target block is not a data holder block -commands.blockdata.failed=The data tag did not change: %s -commands.entitydata.usage=/entitydata -commands.entitydata.success=Entity data updated to: %s -commands.entitydata.tagError=Data tag parsing failed: %s -commands.entitydata.noPlayers=%s is a player and cannot be changed -commands.entitydata.failed=The data tag did not change: %s -commands.effect.usage=/effect [seconds] [amplifier] [hideParticles] OR /effect clear -commands.effect.notFound=There is no such mob effect with ID %d -commands.effect.success=Given %1$s (ID %2$d) * %3$d to %4$s for %5$d seconds -commands.effect.success.removed=Took %1$s from %2$s -commands.effect.success.removed.all=Took all effects from %s -commands.effect.failure.notActive=Couldn't take %1$s from %2$s as they do not have the effect -commands.effect.failure.notActive.all=Couldn't take any effects from %s as they do not have any -commands.enchant.usage=/enchant [level] -commands.enchant.notFound=There is no such enchantment with ID %d -commands.enchant.noItem=The target doesn't hold an item -commands.enchant.cantEnchant=The selected enchantment can't be added to the target item -commands.enchant.cantCombine=%1$s can't be combined with %2$s -commands.enchant.success=Enchanting succeeded -commands.particle.usage=/particle [count] [mode] -commands.particle.success=Playing effect %s for %d times -commands.particle.notFound=Unknown effect name (%s) -commands.clear.usage=/clear [player] [item] [data] [maxCount] [dataTag] -commands.clear.success=Cleared the inventory of %s, removing %d items -commands.clear.testing=%s has %d items that match the criteria -commands.clear.failure=Could not clear the inventory of %s, no items to remove -commands.clear.tagError=Data tag parsing failed: %s -commands.downfall.usage=/toggledownfall -commands.downfall.success=Toggled downfall -commands.time.usage=/time -commands.time.added=Added %d to the time -commands.time.set=Set the time to %d -commands.time.query=Time is %d -commands.players.usage=/list -commands.players.list=There are %d/%d players online: -commands.banlist.ips=There are %d total banned IP addresses: -commands.banlist.players=There are %d total banned players: -commands.banlist.usage=/banlist [ips|players] -commands.kill.usage=/kill [player|entity] -commands.kill.successful=Killed %s -commands.kick.success=Kicked %s from the game -commands.kick.success.reason=Kicked %s from the game: '%s' -commands.kick.usage=/kick [reason ...] -commands.op.success=Opped %s -commands.op.failed=Could not op %s -commands.op.usage=/op -commands.deop.success=De-opped %s -commands.deop.failed=Could not de-op %s -commands.deop.usage=/deop -commands.say.usage=/say -commands.ban.success=Banned player %s -commands.ban.failed=Could not ban player %s -commands.ban.usage=/ban [reason ...] -commands.unban.success=Unbanned player %s -commands.unban.failed=Could not unban player %s -commands.unban.usage=/pardon -commands.banip.invalid=You have entered an invalid IP address or a player that is not online -commands.banip.success=Banned IP address %s -commands.banip.success.players=Banned IP address %s belonging to %s -commands.banip.usage=/ban-ip [reason ...] -commands.unbanip.invalid=You have entered an invalid IP address -commands.unbanip.success=Unbanned IP address %s -commands.unbanip.usage=/pardon-ip
-commands.save.usage=/save-all -commands.save-on.alreadyOn=Saving is already turned on. -commands.save-on.usage=/save-on -commands.save-off.alreadyOff=Saving is already turned off. -commands.save-off.usage=/save-off -commands.save.enabled=Turned on world auto-saving -commands.save.disabled=Turned off world auto-saving -commands.save.start=Saving... -commands.save.success=Saved the world -commands.save.failed=Saving failed: %s -commands.stop.usage=/stop -commands.stop.start=Stopping the server -commands.tp.success=Teleported %s to %s -commands.tp.success.coordinates=Teleported %s to %s, %s, %s -commands.tp.usage=/tp [target player] OR /tp [target player] [ ] -commands.tp.notSameDimension=Unable to teleport because players are not in the same dimension -commands.whitelist.list=There are %d (out of %d seen) whitelisted players: -commands.whitelist.enabled=Turned on the whitelist -commands.whitelist.disabled=Turned off the whitelist -commands.whitelist.reloaded=Reloaded the whitelist -commands.whitelist.add.success=Added %s to the whitelist -commands.whitelist.add.failed=Could not add %s to the whitelist -commands.whitelist.add.usage=/whitelist add -commands.whitelist.remove.success=Removed %s from the whitelist -commands.whitelist.remove.failed=Could not remove %s from the whitelist -commands.whitelist.remove.usage=/whitelist remove -commands.whitelist.usage=/whitelist -commands.scoreboard.usage=/scoreboard ... -commands.scoreboard.noMultiWildcard=Only one user wildcard allowed -commands.scoreboard.allMatchesFailed=All matches failed -commands.scoreboard.teamNotFound=No team was found by the name '%s' -commands.scoreboard.objectiveNotFound=No objective was found by the name '%s' -commands.scoreboard.objectiveReadOnly=The objective '%s' is read-only and cannot be set -commands.scoreboard.objectives.usage=/scoreboard objectives ... -commands.scoreboard.objectives.setdisplay.usage=/scoreboard objectives setdisplay [objective] -commands.scoreboard.objectives.setdisplay.invalidSlot=No such display slot '%s' -commands.scoreboard.objectives.setdisplay.successCleared=Cleared objective display slot '%s' -commands.scoreboard.objectives.setdisplay.successSet=Set the display objective in slot '%s' to '%s' -commands.scoreboard.objectives.add.usage=/scoreboard objectives add [display name ...] -commands.scoreboard.objectives.add.wrongType=Invalid objective criteria type '%s' -commands.scoreboard.objectives.add.alreadyExists=An objective with the name '%s' already exists -commands.scoreboard.objectives.add.tooLong=The name '%s' is too long for an objective, it can be at most %d characters long -commands.scoreboard.objectives.add.displayTooLong=The display name '%s' is too long for an objective, it can be at most %d characters long -commands.scoreboard.objectives.add.success=Added new objective '%s' successfully -commands.scoreboard.objectives.remove.usage=/scoreboard objectives remove -commands.scoreboard.objectives.remove.success=Removed objective '%s' successfully -commands.scoreboard.objectives.list.count=Showing %d objective(s) on scoreboard: -commands.scoreboard.objectives.list.entry=- %s: displays as '%s' and is type '%s' -commands.scoreboard.objectives.list.empty=There are no objectives on the scoreboard -commands.scoreboard.players.usage=/scoreboard players ... -commands.scoreboard.players.name.tooLong=The name '%s' is too long for a player, it can be at most %d characters long -commands.scoreboard.players.set.success=Set score of %s for player %s to %d -commands.scoreboard.players.set.tagMismatch=The dataTag does not match for %s -commands.scoreboard.players.set.tagError=Could not parse dataTag, reason: %s -commands.scoreboard.players.set.usage=/scoreboard players set [dataTag] -commands.scoreboard.players.add.usage=/scoreboard players add [dataTag] -commands.scoreboard.players.remove.usage=/scoreboard players remove [dataTag] -commands.scoreboard.players.reset.usage=/scoreboard players reset [objective] -commands.scoreboard.players.reset.success=Reset scores of player %s -commands.scoreboard.players.resetscore.success=Reset score %s of player %s -commands.scoreboard.players.list.usage=/scoreboard players list [name] -commands.scoreboard.players.list.count=Showing %d tracked players on the scoreboard: -commands.scoreboard.players.list.empty=There are no tracked players on the scoreboard -commands.scoreboard.players.list.player.count=Showing %d tracked objective(s) for %s: -commands.scoreboard.players.list.player.entry=- %2$s: %1$d (%3$s) -commands.scoreboard.players.list.player.empty=Player %s has no scores recorded -commands.scoreboard.players.enable.usage=/scoreboard players enable -commands.scoreboard.players.enable.success=Enabled trigger %s for %s -commands.scoreboard.players.enable.noTrigger=Objective %s is not a trigger -commands.scoreboard.players.test.usage=/scoreboard players test -commands.scoreboard.players.test.notFound=No %s score for %s found -commands.scoreboard.players.test.failed=Score %d is NOT in range %d to %d -commands.scoreboard.players.test.success=Score %d is in range %d to %d -commands.scoreboard.players.operation.usage=/scoreboard players operation -commands.scoreboard.players.operation.notFound=No %s score for %s found -commands.scoreboard.players.operation.invalidOperation=Invalid operation %s -commands.scoreboard.players.operation.success=Operation applied successfully -commands.scoreboard.teams.usage=/scoreboard teams ... -commands.scoreboard.teams.add.usage=/scoreboard teams add [display name ...] -commands.scoreboard.teams.add.alreadyExists=A team with the name '%s' already exists -commands.scoreboard.teams.add.tooLong=The name '%s' is too long for a team, it can be at most %d characters long -commands.scoreboard.teams.add.displayTooLong=The display name '%s' is too long for a team, it can be at most %d characters long -commands.scoreboard.teams.add.success=Added new team '%s' successfully -commands.scoreboard.teams.list.usage=/scoreboard teams list [name] -commands.scoreboard.teams.list.count=Showing %d teams on the scoreboard: -commands.scoreboard.teams.list.entry=- %1$s: '%2$s' has %3$d players -commands.scoreboard.teams.list.empty=There are no teams registered on the scoreboard -commands.scoreboard.teams.list.player.count=Showing %d player(s) in team %s: -commands.scoreboard.teams.list.player.entry=- %2$s: %1$d (%3$s) -commands.scoreboard.teams.list.player.empty=Team %s has no players -commands.scoreboard.teams.empty.usage=/scoreboard teams empty -commands.scoreboard.teams.empty.alreadyEmpty=Team %s is already empty, cannot remove nonexistant players -commands.scoreboard.teams.empty.success=Removed all %d player(s) from team %s -commands.scoreboard.teams.remove.usage=/scoreboard teams remove -commands.scoreboard.teams.remove.success=Removed team %s -commands.scoreboard.teams.join.usage=/scoreboard teams join [player] -commands.scoreboard.teams.join.success=Added %d player(s) to team %s: %s -commands.scoreboard.teams.join.failure=Could not add %d player(s) to team %s: %s -commands.scoreboard.teams.leave.usage=/scoreboard teams leave [player] -commands.scoreboard.teams.leave.success=Removed %d player(s) from their teams: %s -commands.scoreboard.teams.leave.failure=Could not remove %d player(s) from their teams: %s -commands.scoreboard.teams.leave.noTeam=You are not in a team -commands.scoreboard.teams.option.usage=/scoreboard teams option -commands.scoreboard.teams.option.noValue=Valid values for option %s are: %s -commands.scoreboard.teams.option.success=Set option %s for team %s to %s -commands.execute.usage=/execute OR /execute detect -commands.execute.allInvocationsFailed=All invocations failed: '%s' -commands.execute.failed=Failed to execute '%s' as %s -commands.gamemode.success.self=Set own game mode to %s -commands.gamemode.success.other=Set %s's game mode to %s -commands.gamemode.usage=/gamemode [player] -commands.defaultgamemode.usage=/defaultgamemode -commands.defaultgamemode.success=The world's default game mode is now %s -commands.me.usage=/me -commands.help.header=--- Showing help page %d of %d (/help ) --- -commands.help.footer=Tip: Use the key while typing a command to auto-complete the command or its arguments -commands.help.usage=/help [page|command name] -commands.trigger.usage=/trigger -commands.trigger.invalidObjective=Invalid trigger name %s -commands.trigger.invalidMode=Invalid trigger mode %s -commands.trigger.disabled=Trigger %s is not enabled -commands.trigger.invalidPlayer=Only players can use the /trigger command -commands.trigger.success=Trigger %s changed with %s %s -commands.publish.usage=/publish -commands.publish.started=Local game hosted on port %s -commands.publish.failed=Unable to host local game -commands.debug.start=Started debug profiling -commands.debug.stop=Stopped debug profiling after %.2f seconds (%d ticks) -commands.debug.notStarted=Can't stop profiling when we haven't started yet! -commands.debug.usage=/debug -commands.chunkinfo.usage=/chunkinfo [ ] -commands.chunkinfo.location=Chunk location: (%d, %d, %d) -commands.chunkinfo.noChunk=No chunk found at chunk position %d, %d, %d -commands.chunkinfo.notEmpty=Chunk is not empty. -commands.chunkinfo.empty=Chunk is empty. -commands.chunkinfo.notCompiled=Chunk is not compiled. -commands.chunkinfo.compiled=Chunk is compiled. -commands.chunkinfo.hasNoRenderableLayers=Chunk has no renderable layers. -commands.chunkinfo.hasLayers=Chunk has layers: %s -commands.chunkinfo.isEmpty=Chunk has empty layers: %s -commands.chunkinfo.vertices=%s layer's buffer contains %d vertices -commands.chunkinfo.data=First 64 vertices are: %s -commands.tellraw.usage=/tellraw -commands.tellraw.jsonException=Invalid json: %s -commands.message.usage=/tell -commands.message.sameTarget=You can't send a private message to yourself! -commands.message.display.outgoing=You whisper to %s: %s -commands.message.display.incoming=%s whispers to you: %s -commands.difficulty.usage=/difficulty -commands.difficulty.success=Set game difficulty to %s -commands.spawnpoint.usage=/spawnpoint [player] [ ] -commands.spawnpoint.success=Set %s's spawn point to (%d, %d, %d) -commands.setworldspawn.usage=/setworldspawn [ ] -commands.setworldspawn.success=Set the world spawn point to (%d, %d, %d) -commands.gamerule.usage=/gamerule [value] -commands.gamerule.success=Game rule has been updated -commands.gamerule.norule=No game rule called '%s' is available -commands.gamerule.nopermission=Only server owners can change '%s' -commands.weather.usage=/weather [duration in seconds] -commands.weather.clear=Changing to clear weather -commands.weather.rain=Changing to rainy weather -commands.weather.thunder=Changing to rain and thunder -commands.testfor.usage=/testfor [dataTag] -commands.testfor.failure=%s did not match the required data structure -commands.testfor.success=Found %s -commands.testfor.tagError=Data tag parsing failed: %s -commands.seed.usage=/seed -commands.seed.success=Seed: %s -commands.spreadplayers.usage=/spreadplayers -commands.spreadplayers.spreading.teams=Spreading %s teams %s blocks around %s,%s (min %s blocks apart) -commands.spreadplayers.spreading.players=Spreading %s players %s blocks around %s,%s (min %s blocks apart) -commands.spreadplayers.success.teams=Successfully spread %s teams around %s,%s -commands.spreadplayers.success.players=Successfully spread %s players around %s,%s -commands.spreadplayers.info.teams=(Average distance between teams is %s blocks apart after %s iterations) -commands.spreadplayers.info.players=(Average distance between players is %s blocks apart after %s iterations) -commands.spreadplayers.failure.teams=Could not spread %s teams around %s,%s (too many players for space - try using spread of at most %s) -commands.spreadplayers.failure.players=Could not spread %s players around %s,%s (too many players for space - try using spread of at most %s) -commands.achievement.usage=/achievement [player] -commands.achievement.unknownAchievement=Unknown achievement or statistic '%s' -commands.achievement.alreadyHave=Player %s already has achievement %s -commands.achievement.dontHave=Player %s doesn't have achievement %s -commands.achievement.give.success.all=Successfully given all achievements to %s -commands.achievement.give.success.one=Successfully given %s the stat %s -commands.achievement.take.success.all=Successfully taken all achievements from %s -commands.achievement.take.success.one=Successfully taken the stat %s from %s -commands.achievement.statTooLow=Player %s does not have the stat %s -commands.worldborder.usage=/worldborder ... -commands.worldborder.add.usage=/worldborder add [timeInSeconds] -commands.worldborder.set.usage=/worldborder set [timeInSeconds] -commands.worldborder.set.success=Set world border to %s blocks wide (from %s blocks) -commands.worldborder.get.success=World border is currently %s blocks wide -commands.worldborder.setSlowly.shrink.success=Shrinking world border to %s blocks wide (down from %s blocks) over %s seconds -commands.worldborder.setSlowly.grow.success=Growing world border to %s blocks wide (up from %s blocks) over %s seconds -commands.worldborder.center.usage=/worldborder center -commands.worldborder.center.success=Set world border center to %s,%s -commands.worldborder.damage.usage=/worldborder damage -commands.worldborder.damage.buffer.usage=/worldborder damage buffer -commands.worldborder.damage.buffer.success=Set world border damage buffer to %s blocks (from %s blocks) -commands.worldborder.damage.amount.usage=/worldborder damage amount -commands.worldborder.damage.amount.success=Set world border damage amount to %s per block (from %s per block) -commands.worldborder.warning.usage=/worldborder warning -commands.worldborder.warning.time.usage=/worldborder warning time -commands.worldborder.warning.time.success=Set world border warning to %s seconds away (from %s seconds) -commands.worldborder.warning.distance.usage=/worldborder warning distance -commands.worldborder.warning.distance.success=Set world border warning to %s blocks away (from %s blocks) -commands.title.usage=/title ... -commands.title.usage.title=/title title|subtitle -commands.title.usage.clear=/title clear|reset -commands.title.usage.times=/title times -commands.title.success=Title command successfully executed - -itemGroup.buildingBlocks=Building Blocks -itemGroup.decorations=Decoration Blocks -itemGroup.redstone=Redstone -itemGroup.transportation=Transportation -itemGroup.misc=Miscellaneous -itemGroup.search=Search Items -itemGroup.food=Foodstuffs -itemGroup.tools=Tools -itemGroup.combat=Combat -itemGroup.brewing=Brewing -itemGroup.materials=Materials -itemGroup.inventory=Survival Inventory - -inventory.binSlot=Destroy Item - -advMode.setCommand=Set Console Command for Block -advMode.setCommand.success=Command set: %s -advMode.command=Console Command -advMode.nearestPlayer=Use "@p" to target nearest player -advMode.randomPlayer=Use "@r" to target random player -advMode.allPlayers=Use "@a" to target all players -advMode.allEntities=Use "@e" to target all entities -advMode.previousOutput=Previous Output - -advMode.notEnabled=Command blocks are not enabled on this server -advMode.notAllowed=Must be an opped player in creative mode - -mount.onboard=Press %1$s to dismount - -build.tooHigh=Height limit for building is %s blocks - -item.modifiers.mainhand=When in main hand: -item.modifiers.offhand=When in off hand: -item.modifiers.feet=When on feet: -item.modifiers.legs=When on legs: -item.modifiers.chest=When on body: -item.modifiers.head=When on head: - -attribute.modifier.plus.0=+%d %s -attribute.modifier.plus.1=+%d%% %s -attribute.modifier.plus.2=+%d%% %s -attribute.modifier.take.0=-%d %s -attribute.modifier.take.1=-%d%% %s -attribute.modifier.take.2=-%d%% %s -attribute.modifier.equals.0=%d %s -attribute.modifier.equals.1=%d%% %s -attribute.modifier.equals.2=%d%% %s - -attribute.name.horse.jumpStrength=Horse Jump Strength -attribute.name.zombie.spawnReinforcements=Zombie Reinforcements -attribute.name.generic.maxHealth=Max Health -attribute.name.generic.followRange=Mob Follow Range -attribute.name.generic.knockbackResistance=Knockback Resistance -attribute.name.generic.movementSpeed=Speed -attribute.name.generic.attackDamage=Attack Damage -attribute.name.generic.attackSpeed=Attack Speed -attribute.name.generic.luck=Luck -attribute.name.generic.armor=Armor -attribute.name.generic.armorToughness=Armor Toughness - -screenshot.success=Saved screenshot as %s -screenshot.failure=Couldn't save screenshot: %s - -stream.user.mode.moderator=Moderator -stream.user.mode.moderator.self=Moderator on your channel -stream.user.mode.moderator.other=Moderator on %s's channel -stream.user.mode.broadcaster=Broadcaster -stream.user.mode.broadcaster.self=Broadcaster (You!) -stream.user.mode.broadcaster.other=Broadcaster -stream.user.mode.administrator=Twitch Administrator -stream.user.mode.staff=Twitch Staff -stream.user.mode.banned=Banned -stream.user.mode.banned.self=Banned on your channel -stream.user.mode.banned.other=Banned on %s's channel -stream.user.subscription.subscriber=Subscriber -stream.user.subscription.subscriber.self=Subscriber to your channel -stream.user.subscription.subscriber.other=Subscriber to %s's channel -stream.user.subscription.turbo=Twitch Turbo - -stream.unavailable.title=Twitch Broadcasting Unavailable -stream.unavailable.report_to_mojang=Report to Mojang - -stream.confirm_start=Are you sure you want to start broadcasting? - -stream.unavailable.account_not_bound=Before you can broadcast Minecraft through Twitch, you will need to link your Twitch account on mojang.com. Would you like to do that now? -stream.unavailable.account_not_bound.okay=Link Accounts -stream.unavailable.account_not_migrated=Before you can broadcast Minecraft through Twitch, you will need to migrate your Minecraft account to a Mojang account. Would you like to do that now? -stream.unavailable.account_not_migrated.okay=Migrate Account -stream.unavailable.failed_auth=Authentication to Twitch failed. Please go to mojang.com and rebind your Twitch account. -stream.unavailable.failed_auth.okay=Rebind Accounts -stream.unavailable.failed_auth_error=Unable to authenticate to Twitch. Please try again later. -stream.unavailable.initialization_failure=Unable to initialize the Twitch SDK. -stream.unavailable.initialization_failure.extra=(Reason: %s) -stream.unavailable.library_arch_mismatch=The custom java version used to launch Minecraft has a different architecture than the one used to run the launcher. Please make sure these are the same, either 32-bit or 64-bit for both. -stream.unavailable.library_failure=Unable to load the libraries needed for the integrated Twitch broadcasting service. -stream.unavailable.no_fbo=Your video card needs to support at least OpenGL version 3.0 or support Framebuffer Objects via an extension to use the integrated Twitch broadcasting. -stream.unavailable.no_fbo.version=You are currently using: %s -stream.unavailable.no_fbo.blend=Separate blending support via EXT is: %s -stream.unavailable.no_fbo.arb=Framebuffer object support via ARB is: %s -stream.unavailable.no_fbo.ext=Framebuffer object support via EXT is: %s -stream.unavailable.not_supported.windows=Unfortunately the integrated Twitch broadcasting requires a newer version of Windows than you are on. You must have at least Windows Vista or newer. -stream.unavailable.not_supported.mac=Unfortunately the integrated Twitch broadcasting on Mac requires a version of OSX newer than the one you are on. You must use 10.7 (Mac OS X Lion) or newer to be able to use this service. Would you like to visit apple.com to learn about upgrading? -stream.unavailable.not_supported.mac.okay=Upgrade -stream.unavailable.not_supported.other=Unfortunately the integrated Twitch broadcasting service requires Windows (Vista or newer) or Mac OS X (10.7/Lion or newer) -stream.unavailable.unknown=Unfortunately you cannot broadcast to Twitch at this time. And we don't know why :'( -stream.unavailable.unknown.chat=Could not start stream: %s - -stream.unavailable.soundflower.chat=Soundflower is required to be able to stream on Mac. %s -stream.unavailable.soundflower.chat.link=Please click here to install it. - -stream.userinfo.chatTooltip=Click to manage user -stream.userinfo.timeout=Timeout -stream.userinfo.ban=Ban -stream.userinfo.unban=Unban -stream.userinfo.mod=Promote to Moderator -stream.userinfo.unmod=Demote from Moderator - -item.banner.black.name=Black Banner -item.banner.red.name=Red Banner -item.banner.green.name=Green Banner -item.banner.brown.name=Brown Banner -item.banner.blue.name=Blue Banner -item.banner.purple.name=Purple Banner -item.banner.cyan.name=Cyan Banner -item.banner.silver.name=Light Gray Banner -item.banner.gray.name=Gray Banner -item.banner.pink.name=Pink Banner -item.banner.lime.name=Lime Banner -item.banner.yellow.name=Yellow Banner -item.banner.lightBlue.name=Light Blue Banner -item.banner.magenta.name=Magenta Banner -item.banner.orange.name=Orange Banner -item.banner.white.name=White Banner - -item.banner.square_bottom_left.black=Black Base Dexter Canton -item.banner.square_bottom_left.red=Red Base Dexter Canton -item.banner.square_bottom_left.green=Green Base Dexter Canton -item.banner.square_bottom_left.brown=Brown Base Dexter Canton -item.banner.square_bottom_left.blue=Blue Base Dexter Canton -item.banner.square_bottom_left.purple=Purple Base Dexter Canton -item.banner.square_bottom_left.cyan=Cyan Base Dexter Canton -item.banner.square_bottom_left.silver=Light Gray Base Dexter Canton -item.banner.square_bottom_left.gray=Gray Base Dexter Canton -item.banner.square_bottom_left.pink=Pink Base Dexter Canton -item.banner.square_bottom_left.lime=Lime Base Dexter Canton -item.banner.square_bottom_left.yellow=Yellow Base Dexter Canton -item.banner.square_bottom_left.lightBlue=Light Blue Base Dexter Canton -item.banner.square_bottom_left.magenta=Magenta Base Dexter Canton -item.banner.square_bottom_left.orange=Orange Base Dexter Canton -item.banner.square_bottom_left.white=White Base Dexter Canton - -item.banner.square_bottom_right.black=Black Base Sinister Canton -item.banner.square_bottom_right.red=Red Base Sinister Canton -item.banner.square_bottom_right.green=Green Base Sinister Canton -item.banner.square_bottom_right.brown=Brown Base Sinister Canton -item.banner.square_bottom_right.blue=Blue Base Sinister Canton -item.banner.square_bottom_right.purple=Purple Base Sinister Canton -item.banner.square_bottom_right.cyan=Cyan Base Sinister Canton -item.banner.square_bottom_right.silver=Light Gray Base Sinister Canton -item.banner.square_bottom_right.gray=Gray Base Sinister Canton -item.banner.square_bottom_right.pink=Pink Base Sinister Canton -item.banner.square_bottom_right.lime=Lime Base Sinister Canton -item.banner.square_bottom_right.yellow=Yellow Base Sinister Canton -item.banner.square_bottom_right.lightBlue=Light Blue Base Sinister Canton -item.banner.square_bottom_right.magenta=Magenta Base Sinister Canton -item.banner.square_bottom_right.orange=Orange Base Sinister Canton -item.banner.square_bottom_right.white=White Base Sinister Canton - -item.banner.square_top_left.black=Black Chief Dexter Canton -item.banner.square_top_left.red=Red Chief Dexter Canton -item.banner.square_top_left.green=Green Chief Dexter Canton -item.banner.square_top_left.brown=Brown Chief Dexter Canton -item.banner.square_top_left.blue=Blue Chief Dexter Canton -item.banner.square_top_left.purple=Purple Chief Dexter Canton -item.banner.square_top_left.cyan=Cyan Chief Dexter Canton -item.banner.square_top_left.silver=Light Gray Chief Dexter Canton -item.banner.square_top_left.gray=Gray Chief Dexter Canton -item.banner.square_top_left.pink=Pink Chief Dexter Canton -item.banner.square_top_left.lime=Lime Chief Dexter Canton -item.banner.square_top_left.yellow=Yellow Chief Dexter Canton -item.banner.square_top_left.lightBlue=Light Blue Chief Dexter Canton -item.banner.square_top_left.magenta=Magenta Chief Dexter Canton -item.banner.square_top_left.orange=Orange Chief Dexter Canton -item.banner.square_top_left.white=White Chief Dexter Canton - -item.banner.square_top_right.black=Black Chief Sinister Canton -item.banner.square_top_right.red=Red Chief Sinister Canton -item.banner.square_top_right.green=Green Chief Sinister Canton -item.banner.square_top_right.brown=Brown Chief Sinister Canton -item.banner.square_top_right.blue=Blue Chief Sinister Canton -item.banner.square_top_right.purple=Purple Chief Sinister Canton -item.banner.square_top_right.cyan=Cyan Chief Sinister Canton -item.banner.square_top_right.silver=Light Gray Chief Sinister Canton -item.banner.square_top_right.gray=Gray Chief Sinister Canton -item.banner.square_top_right.pink=Pink Chief Sinister Canton -item.banner.square_top_right.lime=Lime Chief Sinister Canton -item.banner.square_top_right.yellow=Yellow Chief Sinister Canton -item.banner.square_top_right.lightBlue=Light Blue Chief Sinister Canton -item.banner.square_top_right.magenta=Magenta Chief Sinister Canton -item.banner.square_top_right.orange=Orange Chief Sinister Canton -item.banner.square_top_right.white=White Chief Sinister Canton - -item.banner.stripe_bottom.black=Black Base Fess -item.banner.stripe_bottom.red=Red Base Fess -item.banner.stripe_bottom.green=Green Base Fess -item.banner.stripe_bottom.brown=Brown Base Fess -item.banner.stripe_bottom.blue=Blue Base Fess -item.banner.stripe_bottom.purple=Purple Base Fess -item.banner.stripe_bottom.cyan=Cyan Base Fess -item.banner.stripe_bottom.silver=Light Gray Base Fess -item.banner.stripe_bottom.gray=Gray Base Fess -item.banner.stripe_bottom.pink=Pink Base Fess -item.banner.stripe_bottom.lime=Lime Base Fess -item.banner.stripe_bottom.yellow=Yellow Base Fess -item.banner.stripe_bottom.lightBlue=Light Blue Base Fess -item.banner.stripe_bottom.magenta=Magenta Base Fess -item.banner.stripe_bottom.orange=Orange Base Fess -item.banner.stripe_bottom.white=White Base Fess - -item.banner.stripe_top.black=Black Chief Fess -item.banner.stripe_top.red=Red Chief Fess -item.banner.stripe_top.green=Green Chief Fess -item.banner.stripe_top.brown=Brown Chief Fess -item.banner.stripe_top.blue=Blue Chief Fess -item.banner.stripe_top.purple=Purple Chief Fess -item.banner.stripe_top.cyan=Cyan Chief Fess -item.banner.stripe_top.silver=Light Gray Chief Fess -item.banner.stripe_top.gray=Gray Chief Fess -item.banner.stripe_top.pink=Pink Chief Fess -item.banner.stripe_top.lime=Lime Chief Fess -item.banner.stripe_top.yellow=Yellow Chief Fess -item.banner.stripe_top.lightBlue=Light Blue Chief Fess -item.banner.stripe_top.magenta=Magenta Chief Fess -item.banner.stripe_top.orange=Orange Chief Fess -item.banner.stripe_top.white=White Chief Fess - -item.banner.stripe_left.black=Black Pale Dexter -item.banner.stripe_left.red=Red Pale Dexter -item.banner.stripe_left.green=Green Pale Dexter -item.banner.stripe_left.brown=Brown Pale Dexter -item.banner.stripe_left.blue=Blue Pale Dexter -item.banner.stripe_left.purple=Purple Pale Dexter -item.banner.stripe_left.cyan=Cyan Pale Dexter -item.banner.stripe_left.silver=Light Gray Pale Dexter -item.banner.stripe_left.gray=Gray Pale Dexter -item.banner.stripe_left.pink=Pink Pale Dexter -item.banner.stripe_left.lime=Lime Pale Dexter -item.banner.stripe_left.yellow=Yellow Pale Dexter -item.banner.stripe_left.lightBlue=Light Blue Pale Dexter -item.banner.stripe_left.magenta=Magenta Pale Dexter -item.banner.stripe_left.orange=Orange Pale Dexter -item.banner.stripe_left.white=White Pale Dexter - -item.banner.stripe_right.black=Black Pale Sinister -item.banner.stripe_right.red=Red Pale Sinister -item.banner.stripe_right.green=Green Pale Sinister -item.banner.stripe_right.brown=Brown Pale Sinister -item.banner.stripe_right.blue=Blue Pale Sinister -item.banner.stripe_right.purple=Purple Pale Sinister -item.banner.stripe_right.cyan=Cyan Pale Sinister -item.banner.stripe_right.silver=Light Gray Pale Sinister -item.banner.stripe_right.gray=Gray Pale Sinister -item.banner.stripe_right.pink=Pink Pale Sinister -item.banner.stripe_right.lime=Lime Pale Sinister -item.banner.stripe_right.yellow=Yellow Pale Sinister -item.banner.stripe_right.lightBlue=Light Blue Pale Sinister -item.banner.stripe_right.magenta=Magenta Pale Sinister -item.banner.stripe_right.orange=Orange Pale Sinister -item.banner.stripe_right.white=White Pale Sinister - -item.banner.stripe_center.black=Black Pale -item.banner.stripe_center.red=Red Pale -item.banner.stripe_center.green=Green Pale -item.banner.stripe_center.brown=Brown Pale -item.banner.stripe_center.blue=Blue Pale -item.banner.stripe_center.purple=Purple Pale -item.banner.stripe_center.cyan=Cyan Pale -item.banner.stripe_center.silver=Light Gray Pale -item.banner.stripe_center.gray=Gray Pale -item.banner.stripe_center.pink=Pink Pale -item.banner.stripe_center.lime=Lime Pale -item.banner.stripe_center.yellow=Yellow Pale -item.banner.stripe_center.lightBlue=Light Blue Pale -item.banner.stripe_center.magenta=Magenta Pale -item.banner.stripe_center.orange=Orange Pale -item.banner.stripe_center.white=White Pale - -item.banner.stripe_middle.black=Black Fess -item.banner.stripe_middle.red=Red Fess -item.banner.stripe_middle.green=Green Fess -item.banner.stripe_middle.brown=Brown Fess -item.banner.stripe_middle.blue=Blue Fess -item.banner.stripe_middle.purple=Purple Fess -item.banner.stripe_middle.cyan=Cyan Fess -item.banner.stripe_middle.silver=Light Gray Fess -item.banner.stripe_middle.gray=Gray Fess -item.banner.stripe_middle.pink=Pink Fess -item.banner.stripe_middle.lime=Lime Fess -item.banner.stripe_middle.yellow=Yellow Fess -item.banner.stripe_middle.lightBlue=Light Blue Fess -item.banner.stripe_middle.magenta=Magenta Fess -item.banner.stripe_middle.orange=Orange Fess -item.banner.stripe_middle.white=White Fess - -item.banner.stripe_downright.black=Black Bend -item.banner.stripe_downright.red=Red Bend -item.banner.stripe_downright.green=Green Bend -item.banner.stripe_downright.brown=Brown Bend -item.banner.stripe_downright.blue=Blue Bend -item.banner.stripe_downright.purple=Purple Bend -item.banner.stripe_downright.cyan=Cyan Bend -item.banner.stripe_downright.silver=Light Gray Bend -item.banner.stripe_downright.gray=Gray Bend -item.banner.stripe_downright.pink=Pink Bend -item.banner.stripe_downright.lime=Lime Bend -item.banner.stripe_downright.yellow=Yellow Bend -item.banner.stripe_downright.lightBlue=Light Blue Bend -item.banner.stripe_downright.magenta=Magenta Bend -item.banner.stripe_downright.orange=Orange Bend -item.banner.stripe_downright.white=White Bend - -item.banner.stripe_downleft.black=Black Bend Sinister -item.banner.stripe_downleft.red=Red Bend Sinister -item.banner.stripe_downleft.green=Green Bend Sinister -item.banner.stripe_downleft.brown=Brown Bend Sinister -item.banner.stripe_downleft.blue=Blue Bend Sinister -item.banner.stripe_downleft.purple=Purple Bend Sinister -item.banner.stripe_downleft.cyan=Cyan Bend Sinister -item.banner.stripe_downleft.silver=Light Gray Bend Sinister -item.banner.stripe_downleft.gray=Gray Bend Sinister -item.banner.stripe_downleft.pink=Pink Bend Sinister -item.banner.stripe_downleft.lime=Lime Bend Sinister -item.banner.stripe_downleft.yellow=Yellow Bend Sinister -item.banner.stripe_downleft.lightBlue=Light Blue Bend Sinister -item.banner.stripe_downleft.magenta=Magenta Bend Sinister -item.banner.stripe_downleft.orange=Orange Bend Sinister -item.banner.stripe_downleft.white=White Bend Sinister - -item.banner.small_stripes.black=Black Paly -item.banner.small_stripes.red=Red Paly -item.banner.small_stripes.green=Green Paly -item.banner.small_stripes.brown=Brown Paly -item.banner.small_stripes.blue=Blue Paly -item.banner.small_stripes.purple=Purple Paly -item.banner.small_stripes.cyan=Cyan Paly -item.banner.small_stripes.silver=Light Gray Paly -item.banner.small_stripes.gray=Gray Paly -item.banner.small_stripes.pink=Pink Paly -item.banner.small_stripes.lime=Lime Paly -item.banner.small_stripes.yellow=Yellow Paly -item.banner.small_stripes.lightBlue=Light Blue Paly -item.banner.small_stripes.magenta=Magenta Paly -item.banner.small_stripes.orange=Orange Paly -item.banner.small_stripes.white=White Paly - -item.banner.cross.black=Black Saltire -item.banner.cross.red=Red Saltire -item.banner.cross.green=Green Saltire -item.banner.cross.brown=Brown Saltire -item.banner.cross.blue=Blue Saltire -item.banner.cross.purple=Purple Saltire -item.banner.cross.cyan=Cyan Saltire -item.banner.cross.silver=Light Gray Saltire -item.banner.cross.gray=Gray Saltire -item.banner.cross.pink=Pink Saltire -item.banner.cross.lime=Lime Saltire -item.banner.cross.yellow=Yellow Saltire -item.banner.cross.lightBlue=Light Blue Saltire -item.banner.cross.magenta=Magenta Saltire -item.banner.cross.orange=Orange Saltire -item.banner.cross.white=White Saltire - -item.banner.triangle_bottom.black=Black Chevron -item.banner.triangle_bottom.red=Red Chevron -item.banner.triangle_bottom.green=Green Chevron -item.banner.triangle_bottom.brown=Brown Chevron -item.banner.triangle_bottom.blue=Blue Chevron -item.banner.triangle_bottom.purple=Purple Chevron -item.banner.triangle_bottom.cyan=Cyan Chevron -item.banner.triangle_bottom.silver=Light Gray Chevron -item.banner.triangle_bottom.gray=Gray Chevron -item.banner.triangle_bottom.pink=Pink Chevron -item.banner.triangle_bottom.lime=Lime Chevron -item.banner.triangle_bottom.yellow=Yellow Chevron -item.banner.triangle_bottom.lightBlue=Light Blue Chevron -item.banner.triangle_bottom.magenta=Magenta Chevron -item.banner.triangle_bottom.orange=Orange Chevron -item.banner.triangle_bottom.white=White Chevron - -item.banner.triangle_top.black=Black Inverted Chevron -item.banner.triangle_top.red=Red Inverted Chevron -item.banner.triangle_top.green=Green Inverted Chevron -item.banner.triangle_top.brown=Brown Inverted Chevron -item.banner.triangle_top.blue=Blue Inverted Chevron -item.banner.triangle_top.purple=Purple Inverted Chevron -item.banner.triangle_top.cyan=Cyan Inverted Chevron -item.banner.triangle_top.silver=Light Gray Inverted Chevron -item.banner.triangle_top.gray=Gray Inverted Chevron -item.banner.triangle_top.pink=Pink Inverted Chevron -item.banner.triangle_top.lime=Lime Inverted Chevron -item.banner.triangle_top.yellow=Yellow Inverted Chevron -item.banner.triangle_top.lightBlue=Light Blue Inverted Chevron -item.banner.triangle_top.magenta=Magenta Inverted Chevron -item.banner.triangle_top.orange=Orange Inverted Chevron -item.banner.triangle_top.white=White Inverted Chevron - -item.banner.triangles_bottom.black=Black Base Indented -item.banner.triangles_bottom.red=Red Base Indented -item.banner.triangles_bottom.green=Green Base Indented -item.banner.triangles_bottom.brown=Brown Base Indented -item.banner.triangles_bottom.blue=Blue Base Indented -item.banner.triangles_bottom.purple=Purple Base Indented -item.banner.triangles_bottom.cyan=Cyan Base Indented -item.banner.triangles_bottom.silver=Light Gray Base Indented -item.banner.triangles_bottom.gray=Gray Base Indented -item.banner.triangles_bottom.pink=Pink Base Indented -item.banner.triangles_bottom.lime=Lime Base Indented -item.banner.triangles_bottom.yellow=Yellow Base Indented -item.banner.triangles_bottom.lightBlue=Light Blue Base Indented -item.banner.triangles_bottom.magenta=Magenta Base Indented -item.banner.triangles_bottom.orange=Orange Base Indented -item.banner.triangles_bottom.white=White Base Indented - -item.banner.triangles_top.black=Black Chief Indented -item.banner.triangles_top.red=Red Chief Indented -item.banner.triangles_top.green=Green Chief Indented -item.banner.triangles_top.brown=Brown Chief Indented -item.banner.triangles_top.blue=Blue Chief Indented -item.banner.triangles_top.purple=Purple Chief Indented -item.banner.triangles_top.cyan=Cyan Chief Indented -item.banner.triangles_top.silver=Light Gray Chief Indented -item.banner.triangles_top.gray=Gray Chief Indented -item.banner.triangles_top.pink=Pink Chief Indented -item.banner.triangles_top.lime=Lime Chief Indented -item.banner.triangles_top.yellow=Yellow Chief Indented -item.banner.triangles_top.lightBlue=Light Blue Chief Indented -item.banner.triangles_top.magenta=Magenta Chief Indented -item.banner.triangles_top.orange=Orange Chief Indented -item.banner.triangles_top.white=White Chief Indented - -item.banner.diagonal_left.black=Black Per Bend Sinister -item.banner.diagonal_left.red=Red Per Bend Sinister -item.banner.diagonal_left.green=Green Per Bend Sinister -item.banner.diagonal_left.brown=Brown Per Bend Sinister -item.banner.diagonal_left.blue=Blue Per Bend Sinister -item.banner.diagonal_left.purple=Purple Per Bend Sinister -item.banner.diagonal_left.cyan=Cyan Per Bend Sinister -item.banner.diagonal_left.silver=Light Gray Per Bend Sinister -item.banner.diagonal_left.gray=Gray Per Bend Sinister -item.banner.diagonal_left.pink=Pink Per Bend Sinister -item.banner.diagonal_left.lime=Lime Per Bend Sinister -item.banner.diagonal_left.yellow=Yellow Per Bend Sinister -item.banner.diagonal_left.lightBlue=Light Blue Per Bend Sinister -item.banner.diagonal_left.magenta=Magenta Per Bend Sinister -item.banner.diagonal_left.orange=Orange Per Bend Sinister -item.banner.diagonal_left.white=White Per Bend Sinister - -item.banner.diagonal_right.black=Black Per Bend -item.banner.diagonal_right.red=Red Per Bend -item.banner.diagonal_right.green=Green Per Bend -item.banner.diagonal_right.brown=Brown Per Bend -item.banner.diagonal_right.blue=Blue Per Bend -item.banner.diagonal_right.purple=Purple Per Bend -item.banner.diagonal_right.cyan=Cyan Per Bend -item.banner.diagonal_right.silver=Light Gray Per Bend -item.banner.diagonal_right.gray=Gray Per Bend -item.banner.diagonal_right.pink=Pink Per Bend -item.banner.diagonal_right.lime=Lime Per Bend -item.banner.diagonal_right.yellow=Yellow Per Bend -item.banner.diagonal_right.lightBlue=Light Blue Per Bend -item.banner.diagonal_right.magenta=Magenta Per Bend -item.banner.diagonal_right.orange=Orange Per Bend -item.banner.diagonal_right.white=White Per Bend - -item.banner.diagonal_up_left.black=Black Per Bend Inverted -item.banner.diagonal_up_left.red=Red Per Bend Inverted -item.banner.diagonal_up_left.green=Green Per Bend Inverted -item.banner.diagonal_up_left.brown=Brown Per Bend Inverted -item.banner.diagonal_up_left.blue=Blue Per Bend Inverted -item.banner.diagonal_up_left.purple=Purple Per Bend Inverted -item.banner.diagonal_up_left.cyan=Cyan Per Bend Inverted -item.banner.diagonal_up_left.silver=Light Gray Per Bend Inverted -item.banner.diagonal_up_left.gray=Gray Per Bend Inverted -item.banner.diagonal_up_left.pink=Pink Per Bend Inverted -item.banner.diagonal_up_left.lime=Lime Per Bend Inverted -item.banner.diagonal_up_left.yellow=Yellow Per Bend Inverted -item.banner.diagonal_up_left.lightBlue=Light Blue Per Bend Inverted -item.banner.diagonal_up_left.magenta=Magenta Per Bend Inverted -item.banner.diagonal_up_left.orange=Orange Per Bend Inverted -item.banner.diagonal_up_left.white=White Per Bend Inverted - -item.banner.diagonal_up_right.black=Black Per Bend Sinister Inverted -item.banner.diagonal_up_right.red=Red Per Bend Sinister Inverted -item.banner.diagonal_up_right.green=Green Per Bend Sinister Inverted -item.banner.diagonal_up_right.brown=Brown Per Bend Sinister Inverted -item.banner.diagonal_up_right.blue=Blue Per Bend Sinister Inverted -item.banner.diagonal_up_right.purple=Purple Per Bend Sinister Inverted -item.banner.diagonal_up_right.cyan=Cyan Per Bend Sinister Inverted -item.banner.diagonal_up_right.silver=Light Gray Per Bend Sinister Inverted -item.banner.diagonal_up_right.gray=Gray Per Bend Sinister Inverted -item.banner.diagonal_up_right.pink=Pink Per Bend Sinister Inverted -item.banner.diagonal_up_right.lime=Lime Per Bend Sinister Inverted -item.banner.diagonal_up_right.yellow=Yellow Per Bend Sinister Inverted -item.banner.diagonal_up_right.lightBlue=Light Blue Per Bend Sinister Inverted -item.banner.diagonal_up_right.magenta=Magenta Per Bend Sinister Inverted -item.banner.diagonal_up_right.orange=Orange Per Bend Sinister Inverted -item.banner.diagonal_up_right.white=White Per Bend Sinister Inverted - -item.banner.circle.black=Black Roundel -item.banner.circle.red=Red Roundel -item.banner.circle.green=Green Roundel -item.banner.circle.brown=Brown Roundel -item.banner.circle.blue=Blue Roundel -item.banner.circle.purple=Purple Roundel -item.banner.circle.cyan=Cyan Roundel -item.banner.circle.silver=Light Gray Roundel -item.banner.circle.gray=Gray Roundel -item.banner.circle.pink=Pink Roundel -item.banner.circle.lime=Lime Roundel -item.banner.circle.yellow=Yellow Roundel -item.banner.circle.lightBlue=Light Blue Roundel -item.banner.circle.magenta=Magenta Roundel -item.banner.circle.orange=Orange Roundel -item.banner.circle.white=White Roundel - -item.banner.rhombus.black=Black Lozenge -item.banner.rhombus.red=Red Lozenge -item.banner.rhombus.green=Green Lozenge -item.banner.rhombus.brown=Brown Lozenge -item.banner.rhombus.blue=Blue Lozenge -item.banner.rhombus.purple=Purple Lozenge -item.banner.rhombus.cyan=Cyan Lozenge -item.banner.rhombus.silver=Light Gray Lozenge -item.banner.rhombus.gray=Gray Lozenge -item.banner.rhombus.pink=Pink Lozenge -item.banner.rhombus.lime=Lime Lozenge -item.banner.rhombus.yellow=Yellow Lozenge -item.banner.rhombus.lightBlue=Light Blue Lozenge -item.banner.rhombus.magenta=Magenta Lozenge -item.banner.rhombus.orange=Orange Lozenge -item.banner.rhombus.white=White Lozenge - -item.banner.half_vertical.black=Black Per Pale -item.banner.half_vertical.red=Red Per Pale -item.banner.half_vertical.green=Green Per Pale -item.banner.half_vertical.brown=Brown Per Pale -item.banner.half_vertical.blue=Blue Per Pale -item.banner.half_vertical.purple=Purple Per Pale -item.banner.half_vertical.cyan=Cyan Per Pale -item.banner.half_vertical.silver=Light Gray Per Pale -item.banner.half_vertical.gray=Gray Per Pale -item.banner.half_vertical.pink=Pink Per Pale -item.banner.half_vertical.lime=Lime Per Pale -item.banner.half_vertical.yellow=Yellow Per Pale -item.banner.half_vertical.lightBlue=Light Blue Per Pale -item.banner.half_vertical.magenta=Magenta Per Pale -item.banner.half_vertical.orange=Orange Per Pale -item.banner.half_vertical.white=White Per Pale - -item.banner.half_horizontal.black=Black Per Fess -item.banner.half_horizontal.red=Red Per Fess -item.banner.half_horizontal.green=Green Per Fess -item.banner.half_horizontal.brown=Brown Per Fess -item.banner.half_horizontal.blue=Blue Per Fess -item.banner.half_horizontal.purple=Purple Per Fess -item.banner.half_horizontal.cyan=Cyan Per Fess -item.banner.half_horizontal.silver=Light Gray Per Fess -item.banner.half_horizontal.gray=Gray Per Fess -item.banner.half_horizontal.pink=Pink Per Fess -item.banner.half_horizontal.lime=Lime Per Fess -item.banner.half_horizontal.yellow=Yellow Per Fess -item.banner.half_horizontal.lightBlue=Light Blue Per Fess -item.banner.half_horizontal.magenta=Magenta Per Fess -item.banner.half_horizontal.orange=Orange Per Fess -item.banner.half_horizontal.white=White Per Fess - -item.banner.half_vertical_right.black=Black Per Pale Inverted -item.banner.half_vertical_right.red=Red Per Pale Inverted -item.banner.half_vertical_right.green=Green Per Pale Inverted -item.banner.half_vertical_right.brown=Brown Per Pale Inverted -item.banner.half_vertical_right.blue=Blue Per Pale Inverted -item.banner.half_vertical_right.purple=Purple Per Pale Inverted -item.banner.half_vertical_right.cyan=Cyan Per Pale Inverted -item.banner.half_vertical_right.silver=Light Gray Per Pale Inverted -item.banner.half_vertical_right.gray=Gray Per Pale Inverted -item.banner.half_vertical_right.pink=Pink Per Pale Inverted -item.banner.half_vertical_right.lime=Lime Per Pale Inverted -item.banner.half_vertical_right.yellow=Yellow Per Pale Inverted -item.banner.half_vertical_right.lightBlue=Light Blue Per Pale Inverted -item.banner.half_vertical_right.magenta=Magenta Per Pale Inverted -item.banner.half_vertical_right.orange=Orange Per Pale Inverted -item.banner.half_vertical_right.white=White Per Pale Inverted - -item.banner.half_horizontal_bottom.black=Black Per Fess Inverted -item.banner.half_horizontal_bottom.red=Red Per Fess Inverted -item.banner.half_horizontal_bottom.green=Green Per Fess Inverted -item.banner.half_horizontal_bottom.brown=Brown Per Fess Inverted -item.banner.half_horizontal_bottom.blue=Blue Per Fess Inverted -item.banner.half_horizontal_bottom.purple=Purple Per Fess Inverted -item.banner.half_horizontal_bottom.cyan=Cyan Per Fess Inverted -item.banner.half_horizontal_bottom.silver=Light Gray Per Fess Inverted -item.banner.half_horizontal_bottom.gray=Gray Per Fess Inverted -item.banner.half_horizontal_bottom.pink=Pink Per Fess Inverted -item.banner.half_horizontal_bottom.lime=Lime Per Fess Inverted -item.banner.half_horizontal_bottom.yellow=Yellow Per Fess Inverted -item.banner.half_horizontal_bottom.lightBlue=Light Blue Per Fess Inverted -item.banner.half_horizontal_bottom.magenta=Magenta Per Fess Inverted -item.banner.half_horizontal_bottom.orange=Orange Per Fess Inverted -item.banner.half_horizontal_bottom.white=White Per Fess Inverted - -item.banner.creeper.black=Black Creeper Charge -item.banner.creeper.red=Red Creeper Charge -item.banner.creeper.green=Green Creeper Charge -item.banner.creeper.brown=Brown Creeper Charge -item.banner.creeper.blue=Blue Creeper Charge -item.banner.creeper.purple=Purple Creeper Charge -item.banner.creeper.cyan=Cyan Creeper Charge -item.banner.creeper.silver=Light Gray Creeper Charge -item.banner.creeper.gray=Gray Creeper Charge -item.banner.creeper.pink=Pink Creeper Charge -item.banner.creeper.lime=Lime Creeper Charge -item.banner.creeper.yellow=Yellow Creeper Charge -item.banner.creeper.lightBlue=Light Blue Creeper Charge -item.banner.creeper.magenta=Magenta Creeper Charge -item.banner.creeper.orange=Orange Creeper Charge -item.banner.creeper.white=White Creeper Charge - -item.banner.bricks.black=Black Field Masoned -item.banner.bricks.red=Red Field Masoned -item.banner.bricks.green=Green Field Masoned -item.banner.bricks.brown=Brown Field Masoned -item.banner.bricks.blue=Blue Field Masoned -item.banner.bricks.purple=Purple Field Masoned -item.banner.bricks.cyan=Cyan Field Masoned -item.banner.bricks.silver=Light Gray Field Masoned -item.banner.bricks.gray=Gray Field Masoned -item.banner.bricks.pink=Pink Field Masoned -item.banner.bricks.lime=Lime Field Masoned -item.banner.bricks.yellow=Yellow Field Masoned -item.banner.bricks.lightBlue=Light Blue Field Masoned -item.banner.bricks.magenta=Magenta Field Masoned -item.banner.bricks.orange=Orange Field Masoned -item.banner.bricks.white=White Field Masoned - -item.banner.gradient.black=Black Gradient -item.banner.gradient.red=Red Gradient -item.banner.gradient.green=Green Gradient -item.banner.gradient.brown=Brown Gradient -item.banner.gradient.blue=Blue Gradient -item.banner.gradient.purple=Purple Gradient -item.banner.gradient.cyan=Cyan Gradient -item.banner.gradient.silver=Light Gray Gradient -item.banner.gradient.gray=Gray Gradient -item.banner.gradient.pink=Pink Gradient -item.banner.gradient.lime=Lime Gradient -item.banner.gradient.yellow=Yellow Gradient -item.banner.gradient.lightBlue=Light Blue Gradient -item.banner.gradient.magenta=Magenta Gradient -item.banner.gradient.orange=Orange Gradient -item.banner.gradient.white=White Gradient - -item.banner.gradient_up.black=Black Base Gradient -item.banner.gradient_up.red=Red Base Gradient -item.banner.gradient_up.green=Green Base Gradient -item.banner.gradient_up.brown=Brown Base Gradient -item.banner.gradient_up.blue=Blue Base Gradient -item.banner.gradient_up.purple=Purple Base Gradient -item.banner.gradient_up.cyan=Cyan Base Gradient -item.banner.gradient_up.silver=Light Gray Base Gradient -item.banner.gradient_up.gray=Gray Base Gradient -item.banner.gradient_up.pink=Pink Base Gradient -item.banner.gradient_up.lime=Lime Base Gradient -item.banner.gradient_up.yellow=Yellow Base Gradient -item.banner.gradient_up.lightBlue=Light Blue Base Gradient -item.banner.gradient_up.magenta=Magenta Base Gradient -item.banner.gradient_up.orange=Orange Base Gradient -item.banner.gradient_up.white=White Base Gradient - -item.banner.skull.black=Black Skull Charge -item.banner.skull.red=Red Skull Charge -item.banner.skull.green=Green Skull Charge -item.banner.skull.brown=Brown Skull Charge -item.banner.skull.blue=Blue Skull Charge -item.banner.skull.purple=Purple Skull Charge -item.banner.skull.cyan=Cyan Skull Charge -item.banner.skull.silver=Light Gray Skull Charge -item.banner.skull.gray=Gray Skull Charge -item.banner.skull.pink=Pink Skull Charge -item.banner.skull.lime=Lime Skull Charge -item.banner.skull.yellow=Yellow Skull Charge -item.banner.skull.lightBlue=Light Blue Skull Charge -item.banner.skull.magenta=Magenta Skull Charge -item.banner.skull.orange=Orange Skull Charge -item.banner.skull.white=White Skull Charge - -item.banner.flower.black=Black Flower Charge -item.banner.flower.red=Red Flower Charge -item.banner.flower.green=Green Flower Charge -item.banner.flower.brown=Brown Flower Charge -item.banner.flower.blue=Blue Flower Charge -item.banner.flower.purple=Purple Flower Charge -item.banner.flower.cyan=Cyan Flower Charge -item.banner.flower.silver=Light Gray Flower Charge -item.banner.flower.gray=Gray Flower Charge -item.banner.flower.pink=Pink Flower Charge -item.banner.flower.lime=Lime Flower Charge -item.banner.flower.yellow=Yellow Flower Charge -item.banner.flower.lightBlue=Light Blue Flower Charge -item.banner.flower.magenta=Magenta Flower Charge -item.banner.flower.orange=Orange Flower Charge -item.banner.flower.white=White Flower Charge - -item.banner.border.black=Black Bordure -item.banner.border.red=Red Bordure -item.banner.border.green=Green Bordure -item.banner.border.brown=Brown Bordure -item.banner.border.blue=Blue Bordure -item.banner.border.purple=Purple Bordure -item.banner.border.cyan=Cyan Bordure -item.banner.border.silver=Light Gray Bordure -item.banner.border.gray=Gray Bordure -item.banner.border.pink=Pink Bordure -item.banner.border.lime=Lime Bordure -item.banner.border.yellow=Yellow Bordure -item.banner.border.lightBlue=Light Blue Bordure -item.banner.border.magenta=Magenta Bordure -item.banner.border.orange=Orange Bordure -item.banner.border.white=White Bordure - -item.banner.curly_border.black=Black Bordure Indented -item.banner.curly_border.red=Red Bordure Indented -item.banner.curly_border.green=Green Bordure Indented -item.banner.curly_border.brown=Brown Bordure Indented -item.banner.curly_border.blue=Blue Bordure Indented -item.banner.curly_border.purple=Purple Bordure Indented -item.banner.curly_border.cyan=Cyan Bordure Indented -item.banner.curly_border.silver=Light Gray Bordure Indented -item.banner.curly_border.gray=Gray Bordure Indented -item.banner.curly_border.pink=Pink Bordure Indented -item.banner.curly_border.lime=Lime Bordure Indented -item.banner.curly_border.yellow=Yellow Bordure Indented -item.banner.curly_border.lightBlue=Light Blue Bordure Indented -item.banner.curly_border.magenta=Magenta Bordure Indented -item.banner.curly_border.orange=Orange Bordure Indented -item.banner.curly_border.white=White Bordure Indented - -item.banner.mojang.black=Black Thing -item.banner.mojang.red=Red Thing -item.banner.mojang.green=Green Thing -item.banner.mojang.brown=Brown Thing -item.banner.mojang.blue=Blue Thing -item.banner.mojang.purple=Purple Thing -item.banner.mojang.cyan=Cyan Thing -item.banner.mojang.silver=Light Gray Thing -item.banner.mojang.gray=Gray Thing -item.banner.mojang.pink=Pink Thing -item.banner.mojang.lime=Lime Thing -item.banner.mojang.yellow=Yellow Thing -item.banner.mojang.lightBlue=Light Blue Thing -item.banner.mojang.magenta=Magenta Thing -item.banner.mojang.orange=Orange Thing -item.banner.mojang.white=White Thing - -item.banner.straight_cross.black=Black Cross -item.banner.straight_cross.red=Red Cross -item.banner.straight_cross.green=Green Cross -item.banner.straight_cross.brown=Brown Cross -item.banner.straight_cross.blue=Blue Cross -item.banner.straight_cross.purple=Purple Cross -item.banner.straight_cross.cyan=Cyan Cross -item.banner.straight_cross.silver=Light Gray Cross -item.banner.straight_cross.gray=Gray Cross -item.banner.straight_cross.pink=Pink Cross -item.banner.straight_cross.lime=Lime Cross -item.banner.straight_cross.yellow=Yellow Cross -item.banner.straight_cross.lightBlue=Light Blue Cross -item.banner.straight_cross.magenta=Magenta Cross -item.banner.straight_cross.orange=Orange Cross -item.banner.straight_cross.white=White Cross - -tile.purpurBlock.name=Purpur Block -tile.purpurPillar.name=Purpur Pillar -tile.purpurStairs.name=Purpur Stairs -tile.purpurSlab.name=Purpur Slab -tile.purpurSlab.default.name=Purpur Slab -tile.purpurStairs.name=Purpur Stairs -tile.endRod.name=End Rod -tile.chorusPlant.name=Chorus Plant -tile.chorusFlower.name=Chorus Flower -tile.grassPath.name=Grass Path -tile.endBricks.name=End Stone Bricks - -item.chorusFruit.name=Chorus Fruit -item.chorusFruitPopped.name=Popped Chorus Fruit -item.beetroot.name=Beetroot -item.beetroot_seeds.name=Beetroot Seeds -item.beetroot_soup.name=Beetroot Soup + +language.name=English +language.region=US +language.code=en_US + +gui.done=Done +gui.cancel=Cancel +gui.back=Back +gui.toTitle=Back to title screen +gui.toMenu=Back to server list +gui.up=Up +gui.down=Down +gui.yes=Yes +gui.no=No +gui.none=None +gui.all=All + +eaglercraft.resourcePack.prompt.title=What do you want to do with '%s'? +eaglercraft.resourcePack.prompt.text=Tip: Hold Shift to skip this screen when selecting a resource pack! +eaglercraft.resourcePack.prompt.delete=Delete this resource pack +eaglercraft.resourcePack.prompt.add=Select this resource pack +eaglercraft.resourcePack.load.refreshing=Refreshing Resources... +eaglercraft.resourcePack.load.pleaseWait=Please Wait. +eaglercraft.resourcePack.load.loading=Loading resource pack... +eaglercraft.resourcePack.load.deleting=Deleting resource pack... + +eaglercraft.gui.exitKey=Use '%s' to close this screen! +eaglercraft.gui.exitKeyRetarded=Use Backtick (`) to close this screen! +eaglercraft.gui.continue=Continue + +eaglercraft.menu.forkOnGitlab=Fork on GitLab +eaglercraft.menu.editProfile=Edit Profile +eaglercraft.menu.openToLan=Invite +eaglercraft.menu.closeLan=Stop Sharing + +eaglercraft.editProfile.title=Edit Profile +eaglercraft.editProfile.username=Username +eaglercraft.editProfile.playerSkin=Player Skin +eaglercraft.editProfile.addSkin=Add Skin +eaglercraft.editProfile.clearSkin=Clear List +eaglercraft.editProfile.capes=Capes +eaglercraft.editProfile.disableFNAW=(Note: go to 'Options...' > 'Skin Customization' to disable FNAW skins) +eaglercraft.editProfile.enableFNAW=(Note: go to 'Options...' > 'Skin Customization' to enable FNAW skins) + +eaglercraft.editCape.title=Edit Cape +eaglercraft.editCape.playerCape=Player Cape +eaglercraft.editCape.addCape=Add Cape +eaglercraft.editCape.clearCape=Clear List + +eaglercraft.editProfile.importExport=Import/Export + +eaglercraft.settingsBackup.importExport.title=What do you wanna do? +eaglercraft.settingsBackup.importExport.import=Import Profile and Settings... +eaglercraft.settingsBackup.importExport.export=Export Profile and Settings... + +eaglercraft.settingsBackup.import.title=Import Profile and Settings +eaglercraft.settingsBackup.import.option.profile=Import Profile: +eaglercraft.settingsBackup.import.option.settings=Import Settings: +eaglercraft.settingsBackup.import.option.servers=Import Servers: +eaglercraft.settingsBackup.import.option.resourcePacks=Resource Packs: +eaglercraft.settingsBackup.import.option.import=Import + +eaglercraft.settingsBackup.export.title=Export Profile and Settings +eaglercraft.settingsBackup.export.option.profile=Export Profile: +eaglercraft.settingsBackup.export.option.settings=Export Settings: +eaglercraft.settingsBackup.export.option.servers=Export Servers: +eaglercraft.settingsBackup.export.option.resourcePacks=Resource Packs: +eaglercraft.settingsBackup.export.option.export=Export + +eaglercraft.settingsBackup.exporting.1=Exporting Profile... +eaglercraft.settingsBackup.exporting.2=Please Wait. + +eaglercraft.settingsBackup.exporting.failed.1=Export Failed! +eaglercraft.settingsBackup.exporting.failed.2=Could not compile EPK + +eaglercraft.settingsBackup.importing.1=Importing Profile... +eaglercraft.settingsBackup.importing.2=Please Wait. + +eaglercraft.settingsBackup.importing.failed.1=Import Failed! +eaglercraft.settingsBackup.importing.failed.2=Could not load EPK + +eaglercraft.resourcePack.importFailed.1=Import Failed! +eaglercraft.resourcePack.importFailed.2=Could not import ZIP file + +eaglercraft.singleplayer.integratedStartup=Starting integrated server + +eaglercraft.addServer.SSLWarn1=you are on an https: page! +eaglercraft.addServer.SSLWarn2=html5 will only allow wss:// + +eaglercraft.chat.exit=Exit Chat + +eaglercraft.handshakeApprove.plaintext.title=Protocol Warning + +eaglercraft.handshakeApprove.plaintext.body.0=§eThis server's 3rd party login system is +eaglercraft.handshakeApprove.plaintext.body.1=§eusing insecure plain-text password login + +eaglercraft.handshakeApprove.plaintext.body.3=This means your password can be stolen +eaglercraft.handshakeApprove.plaintext.body.4=by who is running this server, and also +eaglercraft.handshakeApprove.plaintext.body.5=any proxy you use to connect to it with + +eaglercraft.handshakeApprove.plaintext.body.7=§7Would you like to continue? + +eaglercraft.handshakeApprove.unsupportedAuth.title=Protocol Unsupported + +eaglercraft.handshakeApprove.unsupportedAuth.body.0=§cThis server's login system is using a +eaglercraft.handshakeApprove.unsupportedAuth.body.1=§cprotocol not supported by this client + +eaglercraft.handshakeApprove.unsupportedAuth.body.3=Please make sure you are using the +eaglercraft.handshakeApprove.unsupportedAuth.body.4=latest version of EaglercraftX or +eaglercraft.handshakeApprove.unsupportedAuth.body.5=another fork made for this server + +eaglercraft.options.hud.fps=Show FPS +eaglercraft.options.hud.coords=Show XYZ +eaglercraft.options.hud.stats=Show Stats HUD +eaglercraft.options.hud.world=Show World HUD +eaglercraft.options.hud.player=Show Player +eaglercraft.options.hud.note=Check 'Video Settings' for the option to hide XYZ +eaglercraft.options.hud.24h=24h Day +eaglercraft.options.chunkFix=Chunk Lag Fix +eaglercraft.options.fog=Fog +eaglercraft.options.fxaa=FXAA Antialiasing +eaglercraft.options.fxaa.auto=Auto +eaglercraft.options.fastMath=Fast Math +eaglercraft.options.fastMath.0=OFF +eaglercraft.options.fastMath.1=Low +eaglercraft.options.fastMath.2=High +eaglercraft.options.dynamicLights=Dynamic Lights + +eaglercraft.key.function=Function +eaglercraft.key.zoomCamera=Zoom Camera +eaglercraft.key.close=Close Screen + +eaglercraft.disconnect.tooManyRequests=Too Many Requests! + +eaglercraft.auth.required=Authentication Required +eaglercraft.auth.continue=Join Server + +eaglercraft.shaders.gui.optionsButton=Shaders... +eaglercraft.shaders.gui.title=Shaders +eaglercraft.shaders.gui.enable=Enable Shaders +eaglercraft.shaders.gui.headerTier1=Simple Effects (fast) +eaglercraft.shaders.gui.headerTier2=Intermediate Effects (not as fast) + + +eaglercraft.shaders.gui.option.WAVING_BLOCKS.label=Waving Grass + +eaglercraft.shaders.gui.option.WAVING_BLOCKS.desc.0=The classic vanilla shader that make grass and leaf blocks move with the wind +eaglercraft.shaders.gui.option.WAVING_BLOCKS.desc.2=ON: slower, complex control flow +eaglercraft.shaders.gui.option.WAVING_BLOCKS.desc.3=OFF: faster, simple control flow + +eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.label=Dynamic Lights + +eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.0=Items like torches, glowstone, jack o'lanterns, etc. light up the area around them when they are held or dropped +eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.2=This feature usually does not affect FPS unless there is a large number of dynamic light sources close to the player +eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.4=ON: enable dynamic lights +eaglercraft.shaders.gui.option.DYNAMIC_LIGHTS.desc.5=OFF: disable dynamic lights + +eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.label=Global SSAO + +eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.0=Applies realistic screenspace ambient occlusion (SSAO) to areas of the screen that vanilla Minecraft's default "Smooth Lighting" feature cannot +eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.2=This effect can greatly reduce lag spikes caused by chunk updates in exchange for a decreased maximum FPS when standing still +eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.4=ON: use SSAO +eaglercraft.shaders.gui.option.GLOBAL_AMBIENT_OCCLUSION.desc.5=OFF: use vanilla + +eaglercraft.shaders.gui.option.SHADOWS_SUN.label=Sun Shadow Dist + +eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.0=Makes the sun and moon cast shadows on the world, you almost definitely want to enable this +eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.2=0: off +eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.3=1: 16 blocks +eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.4=2: 32 blocks +eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.5=3: 64 blocks +eaglercraft.shaders.gui.option.SHADOWS_SUN.desc.6=4: 128 blocks + +eaglercraft.shaders.gui.option.SHADOWS_COLORED.label=Color Shadow + +eaglercraft.shaders.gui.option.SHADOWS_COLORED.desc.0=Makes blocks like stained glass cast colored shadows +eaglercraft.shaders.gui.option.SHADOWS_COLORED.desc.2=ON: colored shadows (slower) +eaglercraft.shaders.gui.option.SHADOWS_COLORED.desc.3=OFF: grayscale shadows (faster) + +eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.label=Smooth Shadow + +eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.desc.0=Smooths out the edges of sun and dynamic light shadows to get rid of aliasing +eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.desc.2=ON: smooth shadows (slower) +eaglercraft.shaders.gui.option.SHADOWS_SMOOTHED.desc.3=OFF: aliased shadows (faster) + +eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.label=Env. Mapping + +eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.0=Renders an environment map, allows transparent blocks and moving entities to reflect their surroundings +eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.2=Uses dual paraboloid mapping, only 2 render passes are required to render all reflections, instead of a full 6 render passes like a conventional cube map +eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.4=ON: render env. map (slower) +eaglercraft.shaders.gui.option.REFLECTIONS_PARABOLOID.desc.5=OFF: disable env. map (faster) + +eaglercraft.shaders.gui.option.REALISTIC_WATER.label=Realistic Water + +eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.0=Makes water realistic, adds reflection and refraction effects, uses raytracing +eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.2=ON: render realistic water (slower) +eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.3=OFF: render vanilla water (faster) + +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.label=God Rays + +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.0=Render god rays (light shafts) for sunlight and moonlight shadows +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.2=ON: render god rays (slower) +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.3=OFF: disable god rays (faster) + +eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.label=Raytracing + +eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.0=Renders raytraced reflections off of blocks +eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.2=Thanks to some advanced optimizations, this feature only has a small impact on FPS and is compatible with old hardware +eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.4=Both raymarching and raytracing are employed, raymarching is initially used to locate and track groups of pixels on the screen to reflect, then raytracing is used to reproject that data between multiple frames so the raymarching process only has to be repeated once or twice every few seconds +eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.6=ON: enable raytracing (slower) +eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.desc.7=OFF: disable raytracing (faster) + +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.0=Render god rays (light shafts) for sunlight and moonlight shadows +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.2=ON: render god rays (slower) +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.3=OFF: disable god rays (faster) + +eaglercraft.shaders.gui.option.POST_LENS_DISTORION.label=Lens Distort + +eaglercraft.shaders.gui.option.POST_LENS_DISTORION.desc.0=Renders chromatic abberation and lens distorion +eaglercraft.shaders.gui.option.POST_LENS_DISTORION.desc.2=ON: render lens distortion (slower) +eaglercraft.shaders.gui.option.POST_LENS_DISTORION.desc.3=OFF: disable lens distortion (faster) + +eaglercraft.shaders.gui.option.POST_LENS_FLARES.label=Lens Flares + +eaglercraft.shaders.gui.option.POST_LENS_FLARES.desc.0=Renders filmic lens flares for the sun +eaglercraft.shaders.gui.option.POST_LENS_FLARES.desc.2=ON: render lens flares (slower) +eaglercraft.shaders.gui.option.POST_LENS_FLARES.desc.3=OFF: disable lens flares (faster) + +eaglercraft.shaders.gui.option.POST_BLOOM.label=Bloom + +eaglercraft.shaders.gui.option.POST_BLOOM.desc.0=Renders bloom for emissive textures and sunlight +eaglercraft.shaders.gui.option.POST_BLOOM.desc.2=ON: render bloom (slower) +eaglercraft.shaders.gui.option.POST_BLOOM.desc.3=OFF: disable bloom (faster) + +eaglercraft.shaders.gui.option.POST_FXAA.label=FXAA + +eaglercraft.shaders.gui.option.POST_FXAA.desc.0=Applies FXAA antialiasing in post processing +eaglercraft.shaders.gui.option.POST_FXAA.desc.2=This is the preferred antialiasing method for minecraft, as classical MSAA cannot antialias the pixels of upscaled textures +eaglercraft.shaders.gui.option.POST_FXAA.desc.4=ON: enable fxaa (slower) +eaglercraft.shaders.gui.option.POST_FXAA.desc.5=OFF: disable fxaa (faster) + +eaglercraft.shaders.gui.unsupported.title=Shaders are unavailable on this device! +eaglercraft.shaders.gui.unsupported.reason.hdrFramebuffer=Reason: No HDR render target support +eaglercraft.shaders.gui.unsupported.reason.oldOpenGLVersion=Reason: OpenGL ES 3.0 (WebGL 2.0) is not supported! + +eaglercraft.shaders.debugMenuTip=Press %s+4 to access the shader debug menu + +eaglercraft.command.skull.tip=Use /eagskull to create custom skulls +eaglercraft.command.skull.usage=/eagskull +eaglercraft.command.skull.nopermission=Cheats are not enabled! +eaglercraft.command.skull.feedback=Created new skull: "%s" +eaglercraft.command.skull.error.invalid.format=Invalid or unsupported image file! +eaglercraft.command.skull.error.invalid.skin=Image with size %dx%d is not a valid minecraft skin! + +eaglercraft.command.clientStub=This command is client side! + +menu.game=Game menu +menu.singleplayer=Singleplayer +menu.multiplayer=Multiplayer +menu.online=Minecraft Realms +menu.options=Options... +menu.quit=Quit Game +menu.returnToMenu=Save and Quit to Title +menu.disconnect=Disconnect +menu.returnToGame=Back to Game +menu.switchingLevel=Switching worlds +menu.generatingLevel=Generating world +menu.loadingLevel=Loading world +menu.generatingTerrain=Building terrain +menu.convertingLevel=Converting world +menu.simulating=Simulating the world for a bit +menu.respawning=Respawning +menu.shareToLan=Open to LAN + +selectWorld.title=Select World +selectWorld.empty=empty +selectWorld.world=World +selectWorld.select=Play Selected World +selectWorld.create=Create New World +selectWorld.recreate=Re-Create +selectWorld.createDemo=Play New Demo World +selectWorld.delete=Delete +selectWorld.rename=Rename +selectWorld.deleteQuestion=Are you sure you want to delete this world? +selectWorld.deleteWarning=will be lost forever! (A long time!) +selectWorld.deleteButton=Delete +selectWorld.renameButton=Rename +selectWorld.renameTitle=Rename World +selectWorld.conversion=Must be converted! +selectWorld.newWorld=New World +selectWorld.newWorld.copyOf=Copy of %s +selectWorld.enterName=World Name +selectWorld.resultFolder=Will be saved in: +selectWorld.enterSeed=Seed for the World Generator +selectWorld.seedInfo=Leave blank for a random seed +selectWorld.cheats=Cheats +selectWorld.customizeType=Customize + +createWorld.customize.presets=Presets +createWorld.customize.presets.title=Select a Preset +createWorld.customize.presets.select=Use Preset +createWorld.customize.presets.share=Want to share your preset with someone? Use the below box! +createWorld.customize.presets.list=Alternatively, here's some we made earlier! +createWorld.customize.flat.title=Superflat Customization +createWorld.customize.flat.tile=Layer Material +createWorld.customize.flat.height=Height +createWorld.customize.flat.addLayer=Add Layer +createWorld.customize.flat.editLayer=Edit Layer +createWorld.customize.flat.removeLayer=Remove Layer +createWorld.customize.flat.layer.top=Top - %d +createWorld.customize.flat.layer=%d +createWorld.customize.flat.layer.bottom=Bottom - %d + +createWorld.customize.custom.page0=Basic Settings +createWorld.customize.custom.page1=Ore Settings +createWorld.customize.custom.page2=Advanced Settings (Expert Users Only!) +createWorld.customize.custom.page3=Extra Advanced Settings (Expert Users Only!) +createWorld.customize.custom.randomize=Randomize +createWorld.customize.custom.prev=Previous Page +createWorld.customize.custom.next=Next Page +createWorld.customize.custom.defaults=Defaults +createWorld.customize.custom.confirm1=This will overwrite your current +createWorld.customize.custom.confirm2=settings and cannot be undone. +createWorld.customize.custom.confirmTitle=Warning! +createWorld.customize.custom.mainNoiseScaleX=Main Noise Scale X +createWorld.customize.custom.mainNoiseScaleY=Main Noise Scale Y +createWorld.customize.custom.mainNoiseScaleZ=Main Noise Scale Z +createWorld.customize.custom.depthNoiseScaleX=Depth Noise Scale X +createWorld.customize.custom.depthNoiseScaleZ=Depth Noise Scale Z +createWorld.customize.custom.depthNoiseScaleExponent=Depth Noise Exponent +createWorld.customize.custom.baseSize=Depth Base Size +createWorld.customize.custom.coordinateScale=Coordinate Scale +createWorld.customize.custom.heightScale=Height Scale +createWorld.customize.custom.stretchY=Height Stretch +createWorld.customize.custom.upperLimitScale=Upper Limit Scale +createWorld.customize.custom.lowerLimitScale=Lower Limit Scale +createWorld.customize.custom.biomeDepthWeight=Biome Depth Weight +createWorld.customize.custom.biomeDepthOffset=Biome Depth Offset +createWorld.customize.custom.biomeScaleWeight=Biome Scale Weight +createWorld.customize.custom.biomeScaleOffset=Biome Scale Offset +createWorld.customize.custom.seaLevel=Sea Level +createWorld.customize.custom.useCaves=Caves +createWorld.customize.custom.useStrongholds=Strongholds +createWorld.customize.custom.useVillages=Villages +createWorld.customize.custom.useMineShafts=Mineshafts +createWorld.customize.custom.useTemples=Temples +createWorld.customize.custom.useMonuments=Ocean Monuments +createWorld.customize.custom.useRavines=Ravines +createWorld.customize.custom.useDungeons=Dungeons +createWorld.customize.custom.dungeonChance=Dungeon Count +createWorld.customize.custom.useWaterLakes=Water Lakes +createWorld.customize.custom.waterLakeChance=Water Lake Rarity +createWorld.customize.custom.useLavaLakes=Lava Lakes +createWorld.customize.custom.lavaLakeChance=Lava Lake Rarity +createWorld.customize.custom.useLavaOceans=Lava Oceans +createWorld.customize.custom.fixedBiome=Biome +createWorld.customize.custom.biomeSize=Biome Size +createWorld.customize.custom.riverSize=River Size + +createWorld.customize.custom.size= Spawn Size +createWorld.customize.custom.count= Spawn Tries +createWorld.customize.custom.minHeight= Min. Height +createWorld.customize.custom.maxHeight= Max. Height +createWorld.customize.custom.center= Center Height +createWorld.customize.custom.spread= Spread Height + +createWorld.customize.custom.presets.title=Customize World Presets +createWorld.customize.custom.presets=Presets +createWorld.customize.custom.preset.waterWorld=Water World +createWorld.customize.custom.preset.isleLand=Isle Land +createWorld.customize.custom.preset.caveDelight=Caver's Delight +createWorld.customize.custom.preset.mountains=Mountain Madness +createWorld.customize.custom.preset.drought=Drought +createWorld.customize.custom.preset.caveChaos=Caves of Chaos +createWorld.customize.custom.preset.goodLuck=Good Luck + +gameMode.survival=Survival Mode +gameMode.creative=Creative Mode +gameMode.adventure=Adventure Mode +gameMode.spectator=Spectator Mode +gameMode.hardcore=Hardcore Mode! +gameMode.changed=Your game mode has been updated + +selectWorld.gameMode=Game Mode +selectWorld.gameMode.survival=Survival +selectWorld.gameMode.survival.line1=Search for resources, crafting, gain +selectWorld.gameMode.survival.line2=levels, health and hunger +selectWorld.gameMode.creative=Creative +selectWorld.gameMode.creative.line1=Unlimited resources, free flying and +selectWorld.gameMode.creative.line2=destroy blocks instantly +selectWorld.gameMode.spectator=Spectator +selectWorld.gameMode.spectator.line1=You can look but don't touch +selectWorld.gameMode.spectator.line2= +selectWorld.gameMode.hardcore=Hardcore +selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest +selectWorld.gameMode.hardcore.line2=difficulty, and one life only +selectWorld.gameMode.adventure=Adventure +selectWorld.gameMode.adventure.line1=Same as survival mode, but blocks can't +selectWorld.gameMode.adventure.line2=be added or removed +selectWorld.moreWorldOptions=More World Options... +selectWorld.mapFeatures=Generate Structures: +selectWorld.mapFeatures.info=Villages, dungeons etc +selectWorld.mapType=World Type: +selectWorld.mapType.normal=Normal +selectWorld.allowCommands=Allow Cheats: +selectWorld.allowCommands.info=Commands like /gamemode, /xp +selectWorld.hardcoreMode=Hardcore: +selectWorld.hardcoreMode.info=World is deleted upon death +selectWorld.bonusItems=Bonus Chest: + +generator.default=Default +generator.flat=Superflat +generator.largeBiomes=Large Biomes +generator.amplified=AMPLIFIED +generator.customized=Customized +generator.debug_all_block_states=Debug Mode + +generator.amplified.info=Notice: Just for fun, requires beefy computer + +eaglercraft.singleplayer.busy.killTask=Cancel Task +eaglercraft.singleplayer.busy.cancelWarning=Are you sure? +eaglercraft.singleplayer.busy.confirmCancel=Confirm Cancel + +eaglercraft.singleplayer.crashed.title=Recieved a crash report from integrated server! +eaglercraft.singleplayer.crashed.checkConsole=Check the console for more details +eaglercraft.singleplayer.crashed.continue=Continue +eaglercraft.singleplayer.crashed.singleThreadCont=Single Thread Mode + +eaglercraft.singleplayer.failed.title=Singleplayer Task Failed! +eaglercraft.singleplayer.failed.killed=The worker process was killed +eaglercraft.singleplayer.failed.notStarted=The worker process could not start + +eaglercraft.singleplayer.failed.singleThreadWarning.1=Worker Thread Issue Detected +eaglercraft.singleplayer.failed.singleThreadWarning.2=The integrated server is running in single-threaded mode + +eaglercraft.singleplayer.busy.listingworlds=Loading worlds +eaglercraft.singleplayer.failed.listingworlds=Could not fetch worlds list! + +eaglercraft.singleplayer.busy.deleting=Deleting world +eaglercraft.singleplayer.failed.deleting=Failed to delete world! + +eaglercraft.singleplayer.busy.renaming=Renaming world +eaglercraft.singleplayer.failed.renaming=Failed to rename world! + +eaglercraft.singleplayer.busy.duplicating=Duplicating world +eaglercraft.singleplayer.failed.duplicating=Failed to duplicate world! + +eaglercraft.singleplayer.busy.importing.1=Importing world as EPK +eaglercraft.singleplayer.failed.importing.1=Failed to import world as EPK! + +eaglercraft.singleplayer.busy.importing.2=Importing world as vanilla +eaglercraft.singleplayer.failed.importing.2=Failed to import world as vanilla! + +eaglercraft.singleplayer.busy.exporting.1=Exporting world as EPK +eaglercraft.singleplayer.failed.exporting.1=Failed to export world as EPK! + +eaglercraft.singleplayer.busy.exporting.2=Exporting world as vanilla +eaglercraft.singleplayer.failed.exporting.2=Failed to export world as vanilla! + +eaglercraft.singleplayer.busy.clearplayers=Clearing players +eaglercraft.singleplayer.failed.clearplayers=Failed to clear players! + +eaglercraft.singleplayer.busy.startingIntegratedServer=Starting up integrated server +eaglercraft.singleplayer.failed.startingIntegratedServer=Failed to start up integrated server! + +eaglercraft.singleplayer.busy.stoppingIntegratedServer=Shutting down integrated server +eaglercraft.singleplayer.failed.stoppingIntegratedServer=Failed to shut down integrated server! + +eaglercraft.singleplayer.failed.serverCrash=Integrated server encountered an exception! + +eaglercraft.singleplayer.failed.demo.title=Could not start demo +eaglercraft.singleplayer.failed.demo.desc=Failed to start integrated server! + +eaglercraft.singleplayer.notSupported.title=Shared worlds are not supported on this browser! +eaglercraft.singleplayer.notSupported.desc=WebRTC is not supported + +eaglercraft.singleplayer.import.title=Import World +eaglercraft.singleplayer.import.enterName=Enter world name: +eaglercraft.singleplayer.import.continue=Continue +eaglercraft.singleplayer.import.reading=Reading: '%s' +eaglercraft.singleplayer.import.loadSpawnChunks=Keep spawn chunks loaded: %s +eaglercraft.singleplayer.import.enhancedGameRules=Add extra game features: %s + +eaglercraft.singleplayer.create.title=What do you wanna do? +eaglercraft.singleplayer.create.create=Create New World +eaglercraft.singleplayer.create.create.tooltip=Make a new world for eaglercraft +eaglercraft.singleplayer.create.import=Load EPK File +eaglercraft.singleplayer.create.import.tooltip=Load an Eaglercraft .epk world file +eaglercraft.singleplayer.create.vanilla=Import Vanilla World +eaglercraft.singleplayer.create.vanilla.tooltip=Load a vanilla minecraft 1.8 world + +eaglercraft.singleplayer.backup.title=World Backup Menu: '%s' +eaglercraft.singleplayer.backup.recreate=Re-Create World +eaglercraft.singleplayer.backup.recreate.tooltip=Create a new world with the same seed +eaglercraft.singleplayer.backup.seed=Seed: +eaglercraft.singleplayer.backup.duplicate=Duplicate World +eaglercraft.singleplayer.backup.duplicate.tooltip=Copy this world into a new save +eaglercraft.singleplayer.backup.export=Export EPK File +eaglercraft.singleplayer.backup.export.tooltip=Download this world as a compressed .epk file +eaglercraft.singleplayer.backup.vanilla=Convert to Vanilla +eaglercraft.singleplayer.backup.vanilla.tooltip=Download this world as a vanilla 1.8 world +eaglercraft.singleplayer.backup.clearPlayerData=Delete Player Data +eaglercraft.singleplayer.backup.clearPlayerData.tooltip=Clears the inventories of all players except the owner + +eaglercraft.singleplayer.backup.clearPlayerData.warning1=Are you sure you want to delete all player data? +eaglercraft.singleplayer.backup.clearPlayerData.warning2=All players in '%s' will lose their inventories (besides %s) + +eaglercraft.singleplayer.ramdiskdetected.title=Worker Issues Detected +eaglercraft.singleplayer.ramdiskdetected.text0=Running in "RAMDisk mode", worlds cannot be saved! +eaglercraft.singleplayer.ramdiskdetected.text1=Single thread mode may solve this issue +eaglercraft.singleplayer.ramdiskdetected.continue=Continue +eaglercraft.singleplayer.ramdiskdetected.singleThreadCont=Single Thread Mode + +eaglercraft.selectWorld.backup=Backup + +eaglercraft.selectWorld.duplicate=Duplicate World: +eaglercraft.selectWorld.duplicateButton=Duplicate + +eaglercraft.selectWorld.ramdiskWarning=WARNING: Running in "RAMDisk mode", worlds will NOT be saved to local storage! + +eaglercraft.singleplayer.tpscounter.singleThreadMode=Single Thread Mode + +eaglercraft.networkSettings.title=Shared World Relay Servers +eaglercraft.networkSettings.add=Add Relay +eaglercraft.networkSettings.delete=Delete Relay +eaglercraft.networkSettings.default=Set Primary +eaglercraft.networkSettings.refresh=Refresh +eaglercraft.networkSettings.loadDefaults=Load Defaults +eaglercraft.networkSettings.relayTimeout=Connection Timeout: +eaglercraft.networkSettings.relayTimeoutChange=change +eaglercraft.networkSettings.relayTimeoutTitle=Change Connection Timeout +eaglercraft.networkSettings.downloadRelay=Download JAR + +eaglercraft.directConnect.prompt=What would you like to do? +eaglercraft.directConnect.lanWorld=Join Shared World +eaglercraft.directConnect.lanWorldCode=Enter Join Code: +eaglercraft.directConnect.networkSettingsNote=Click 'Network Settings' to add a relay URL +eaglercraft.directConnect.ipGrabNote=Note: The world's owner can get your IP address +eaglercraft.directConnect.serverJoin=Connect to Server +eaglercraft.directConnect.lanWorldJoin=Join World +eaglercraft.directConnect.lanWorldRelay=Network Settings + +eaglercraft.lanInfo.title=Shared World Info +eaglercraft.lanInfo.desc.0=Eaglercraft shared worlds are NOT limited to your local network like vanilla LAN worlds. This means that anyone with an internet connection and connection to the invite relay can join your world provided they have the code. +eaglercraft.lanInfo.desc.1=Join a shared world from the %s screen, or create one with the %s button while in a world. + +eaglercraft.lanServer.legacyClient=Please use EaglercraftL 1.9! + +eaglercraft.lanServer.pauseMenu0=Sharing World +eaglercraft.lanServer.pauseMenu1=Relay URL: +eaglercraft.lanServer.pauseMenu2=Join Code: + +eaglercraft.lanServer.wouldYouLikeToKick=Would you like to kick all players? + +eaglercraft.lanServer.worldName=World Name: +eaglercraft.lanServer.hidden=Hidden: +eaglercraft.lanServer.hideCode=hide details +eaglercraft.lanServer.showCode=show details +eaglercraft.lanServer.opened=Shared world opened on $relay$, join code is §a$code$ +eaglercraft.lanServer.closed=Shared world closed +eaglercraft.lanServer.pleaseWait=Please Wait... +eaglercraft.lanServer.relayDisconnected=Warning: connection to invite relay was lost, you must re-share the world to invite more people +eaglercraft.lanServer.ipGrabNote=Note: Players joining your world can get your IP address + +eaglercraft.addRelay.title=Add New Relay +eaglercraft.addRelay.name=Relay Comment +eaglercraft.addRelay.address=Relay Address +eaglercraft.addRelay.add=Add Relay +eaglercraft.addRelay.primary=Set Primary +eaglercraft.addRelay.removeText1=Do you want to remove this relay? + +eaglercraft.noRelay.title=No Relays are Configured! +eaglercraft.noRelay.titleFail=No Working Relays Available! +eaglercraft.noRelay.noRelay1=Shared Worlds Unavailable: No Relays Configured! +eaglercraft.noRelay.noRelay2=Click '§nNetwork Settings§r' to fix +eaglercraft.noRelay.worldNotFound1=Could not locate '§c$code$§r'! +eaglercraft.noRelay.worldNotFound2=Make sure to add the '§f$code$§r' world's relay URL +eaglercraft.noRelay.worldNotFound3=to the relay list 'Network Settings' to connect +eaglercraft.noRelay.worldFail=Failed to connect to '$code$'! + +eaglercraft.singleplayer.demo.create.title=What do you wanna do? +eaglercraft.singleplayer.demo.create.create=Play Demo World +eaglercraft.singleplayer.demo.create.create.tooltip=Play the Minecraft 1.8 demo and invite others +eaglercraft.singleplayer.demo.create.join=Join Shared World +eaglercraft.singleplayer.demo.create.join.tooltip=Join someone else's world and play multiplayer + +eaglercraft.createWorld.seedNote=Note: Vanilla seeds now work! + +eaglercraft.singleplayer.oldseedwarning.title=Old World Detected! +eaglercraft.singleplayer.oldseedwarning.msg1=Please use EaglercraftX u32 or older to "Re-Create" this world +eaglercraft.singleplayer.oldseedwarning.msg2=The world's seed will not be the same otherwise :( +eaglercraft.singleplayer.oldseedwarning.ok=OK + +eaglercraft.singleplayer.outdatedLANServerKick=This is a 1.5.2 LAN world! + +eaglercraft.options.debugConsoleButton=Open Debug Console + +eaglercraft.tile.bed.setspawn=Respawn point set + +eaglercraft.update.button=Check for updates: +eaglercraft.update.none=No updates available! +eaglercraft.update.noneNew=No new updates found +eaglercraft.update.found=Found new client update +eaglercraft.update.update=Update: +eaglercraft.update.author=Author: +eaglercraft.update.timestamp=Timestamp: +eaglercraft.update.size=Size (kB): +eaglercraft.update.startDownload=Download +eaglercraft.update.viewAll=View All (%d) +eaglercraft.update.dismiss=Dismiss +eaglercraft.update.downloading=Downloading Updates + +eaglercraft.update.downloadOffline=Download Offline +eaglercraft.update.digitallySigned=Digitally Signed (%s) +eaglercraft.update.signatureInvalid=Signature Invalid! + +eaglercraft.updateList.title=Versions Available +eaglercraft.updateList.download=Download +eaglercraft.updateList.refresh=Refresh +eaglercraft.updateList.note.0=Note: Updates are digitally signed, EaglercraftL will block any +eaglercraft.updateList.note.1=updates that were not created by HoosierTransfer + +eaglercraft.updateSuccess.title=Update Successful +eaglercraft.updateSuccess.installToBootMenu=Install to Boot Menu +eaglercraft.updateSuccess.downloadOffline=Download Offline + +eaglercraft.updateSuccess.downloading=Downloading Offline... +eaglercraft.updateSuccess.installing=Installing Client... + +eaglercraft.updateFailed.title=Update Failed + +eaglercraft.installFailed.title=Installation Failed + +eaglercraft.updateInstall.title=Install to Boot Menu +eaglercraft.updateInstall.setDefault=Make Default +eaglercraft.updateInstall.setCountdown=Enable Countdown +eaglercraft.updateInstall.install=Install + +eaglercraft.options.pressDeleteText=Press DEL to enter boot menu + +eaglercraft.enterBootMenu.title=Enter Boot Menu +eaglercraft.enterBootMenu.text0=Refresh the page to enter the boot menu + +eaglercraft.voice.title=Voice Channel +eaglercraft.voice.titleNoVoice=Voice is disabled on this server +eaglercraft.voice.titleVoiceUnavailable=Voice is unavailable +eaglercraft.voice.titleVoiceBrowserError=(browser issue) +eaglercraft.voice.ptt=Press '%s' to speak +eaglercraft.voice.pttChangeDesc=(Press Any Key) +eaglercraft.voice.changeKey=Change +eaglercraft.voice.off=OFF +eaglercraft.voice.radius=NEARBY +eaglercraft.voice.global=GLOBAL +eaglercraft.voice.volumeTitle=Change Volume +eaglercraft.voice.volumeListen=Speakers Volume: +eaglercraft.voice.volumeSpeak=Microphone Volume: +eaglercraft.voice.radiusTitle=Change Listener Radius +eaglercraft.voice.radiusLabel=Players Within: +eaglercraft.voice.radiusChange=change +eaglercraft.voice.notConnected=Not Connected +eaglercraft.voice.connecting=Connecting... +eaglercraft.voice.unavailable=Could not connect! +eaglercraft.voice.connectedGlobal=Connected - Global +eaglercraft.voice.connectedRadius=Connected - $f$Within $radius$m +eaglercraft.voice.playersListening=Players Listening: +eaglercraft.voice.muted=Players Muted: +eaglercraft.voice.unmute=unmute +eaglercraft.voice.mute=mute +eaglercraft.voice.apply=Apply +eaglercraft.voice.volumeSpeakerLabel=Speakers: +eaglercraft.voice.volumeMicrophoneLabel=Microphone: + +eaglercraft.voice.unsupportedWarning1=Voice Warning +eaglercraft.voice.unsupportedWarning2=Your network configuration may not support +eaglercraft.voice.unsupportedWarning3=eaglercraft's voice chat. +eaglercraft.voice.unsupportedWarning4=Voice chat is based on WebRTC and is +eaglercraft.voice.unsupportedWarning5=normally meant to be peer-to-peer, if your +eaglercraft.voice.unsupportedWarning6=firewall blocks peer-to-peer connections +eaglercraft.voice.unsupportedWarning7=a TURN server will be required. +eaglercraft.voice.unsupportedWarning8=The default OpenRelayProject TURN servers +eaglercraft.voice.unsupportedWarning9=are no longer working in 2024! + +eaglercraft.voice.unsupportedWarning10=Continue +eaglercraft.voice.unsupportedWarning11=Cancel + +eaglercraft.voice.ipGrabWarning1=IP Logger Warning +eaglercraft.voice.ipGrabWarning2=Using Eaglercraft voice chat may allow your +eaglercraft.voice.ipGrabWarning3=IP address to be logged by other players +eaglercraft.voice.ipGrabWarning4=also using voice on the server. +eaglercraft.voice.ipGrabWarning5=This issue will not be fixed, it is an +eaglercraft.voice.ipGrabWarning6=internal browser issue, not a mistake in the +eaglercraft.voice.ipGrabWarning7=game. Sorry about that. + +eaglercraft.revokeSessionToken.button=Revoke Session Token +eaglercraft.revokeSessionToken.title=Select session to revoke: +eaglercraft.revokeSessionToken.inspect=Inspect +eaglercraft.revokeSessionToken.revoke=Revoke +eaglercraft.revokeSessionToken.note.0=Use this tool when you believe someone has stolen your cookies +eaglercraft.revokeSessionToken.note.1=Eagler will request the server invalidate all session tokens + +eaglercraft.inspectSessionToken.title=Cookie Details +eaglercraft.inspectSessionToken.details.server=Server Address: +eaglercraft.inspectSessionToken.details.expires=Expires At: +eaglercraft.inspectSessionToken.details.length=Byte Length: + +eaglercraft.errorNoSessions.title=No Sessions Active! +eaglercraft.errorNoSessions.desc=There are no revokable sessions + +eaglercraft.revokeSendingScreen.title=Revoking Session +eaglercraft.revokeSendingScreen.message.opening=Connecting to %s... +eaglercraft.revokeSendingScreen.message.sending=Sending Request... + +eaglercraft.revokeSuccess.title=Revoke Success +eaglercraft.revokeSuccess.desc=The session was revoked sucessfully! + +eaglercraft.revokeFailure.title=Revoke Failed +eaglercraft.revokeFailure.desc.notSupported=The server does not support this feature! +eaglercraft.revokeFailure.desc.notAllowed=The server does not allow revoking this token! +eaglercraft.revokeFailure.desc.notFound=The session was not found on the server! +eaglercraft.revokeFailure.desc.serverError=Internal server error! +eaglercraft.revokeFailure.desc.clientError=Error handling server's response! +eaglercraft.revokeFailure.desc.genericCode=Error code received! (Check Console) +eaglercraft.revokeFailure.desc.connectionError=Failed to connect to the server! +eaglercraft.revokeFailure.desc.cancelled=Connection closed + +eaglercraft.recieveServerInfo.title=Retrieving Server Info +eaglercraft.recieveServerInfo.checkingCache=Checking Cache +eaglercraft.recieveServerInfo.contactingServer=Contacting Server +eaglercraft.recieveServerInfo.recievingData=Recieving Data +eaglercraft.recieveServerInfo.decompressing=Decompressing + Verifying + +eaglercraft.serverInfoFailure.title=Retrieval Failed +eaglercraft.serverInfoFailure.desc=Failed to retrieve server info! + +eaglercraft.webviewNotSupported.title=WebView Error +eaglercraft.webviewNotSupported.desc=WebView is not supported on this platform! + +eaglercraft.webviewInvalidURL.title=WebView Error +eaglercraft.webviewInvalidURL.desc=Server provided an invalid URL! + +eaglercraft.fallbackWebViewScreen.text0=View in your browser at: +eaglercraft.fallbackWebViewScreen.startingUp=Starting Up... +eaglercraft.fallbackWebViewScreen.pleaseWait=Please Wait... +eaglercraft.fallbackWebViewScreen.exited=(exited) +eaglercraft.fallbackWebViewScreen.openButton=Open +eaglercraft.fallbackWebViewScreen.exitButton=Exit + +eaglercraft.webviewPhishingWaring.title=WARNING!!! +eaglercraft.webviewPhishingWaring.text0=If you see a login page, think before you enter a password +eaglercraft.webviewPhishingWaring.text1=Passwords can be stolen by the owner of this server or website +eaglercraft.webviewPhishingWaring.text2=Do not log in to accounts you don't want hackers to steal +eaglercraft.webviewPhishingWaring.dontShowAgain=Do not show this message again +eaglercraft.webviewPhishingWaring.continue=Continue + +eaglercraft.notifications.title=Notifications +eaglercraft.notifications.priority=Priority: %s +eaglercraft.notifications.priority.low=All +eaglercraft.notifications.priority.normal=Info +eaglercraft.notifications.priority.higher=Warning +eaglercraft.notifications.priority.highest=Severe +eaglercraft.notifications.clearAll=Clear All + +eaglercraft.options.touchControlOpacity=Touch Ctrls Opacity + +eaglercraft.options.profanityFilterButton=Profanity Filter + +eaglercraft.profanityFilterWarning.title=Content Warning +eaglercraft.profanityFilterWarning.text0=If you are streaming this game on Twitch, or +eaglercraft.profanityFilterWarning.text1=are under the wise old age of 14, please enable +eaglercraft.profanityFilterWarning.text2=the profanity filter before playing Multiplayer +eaglercraft.profanityFilterWarning.text4=(Disable in the 'Options' -> 'Chat Settings' menu) + +eaglercraft.options.screenRecording.unsupported=Recording Unsupported! +eaglercraft.options.screenRecording.button=Record Screen... + +eaglercraft.options.screenRecording.title=Screen Recording +eaglercraft.options.screenRecording.codec=Output Format: %s +eaglercraft.options.screenRecording.codecButton=Change... +eaglercraft.options.screenRecording.start=Start Recording +eaglercraft.options.screenRecording.stop=Stop Recording +eaglercraft.options.screenRecording.status=Status: %s +eaglercraft.options.screenRecording.status.0=Not Recording +eaglercraft.options.screenRecording.status.1=Recording! +eaglercraft.options.screenRecording.audioBitrate=Audio Bitrate +eaglercraft.options.screenRecording.videoBitrate=Video Bitrate +eaglercraft.options.screenRecording.videoResolution=Video Resolution +eaglercraft.options.screenRecording.microphoneVolume=Microphone Volume +eaglercraft.options.screenRecording.gameVolume=Game Volume +eaglercraft.options.screenRecording.videoFPS=Video Frame Rate +eaglercraft.options.screenRecording.onVSync=VSync +eaglercraft.options.screenRecording.failed=Failed to begin recording! + +eaglercraft.options.recordingCodec.title=Select Codec +eaglercraft.options.recordingCodec.showAdvancedCodecs=Show Advanced: %s + +eaglercraft.options.recordingNote.title=Recording Note +eaglercraft.options.recordingNote.text0=If the recorded video does not play, +eaglercraft.options.recordingNote.text1=try opening the file in your browser + +eaglercraft.touch.interact.entity=Interact + +selectServer.title=Select Server +selectServer.empty=empty +selectServer.select=Join Server +selectServer.direct=Direct Connect +selectServer.edit=Edit +selectServer.delete=Delete +selectServer.add=Add server +selectServer.defaultName=Minecraft Server +selectServer.deleteQuestion=Are you sure you want to remove this server? +selectServer.deleteWarning=will be lost forever! (A long time!) +selectServer.deleteButton=Delete +selectServer.refresh=Refresh +selectServer.hiddenAddress=(Hidden) +addServer.title=Edit Server Info +addServer.enterName=Server Name +addServer.enterIp=Server Address +addServer.add=Done +addServer.hideAddress=Hide Address +eaglercraft.addServer.hideAddr=Hide Addr +addServer.resourcePack=Server Resource Packs +addServer.resourcePack.enabled=Enabled +addServer.resourcePack.disabled=Disabled +addServer.resourcePack.prompt=Prompt +eaglercraft.addServer.enableCookies=Cookies +eaglercraft.addServer.enableCookies.enabled=Enabled +eaglercraft.addServer.enableCookies.disabled=Disabled +lanServer.title=Shared World +lanServer.scanning=Scanning for games on your local network +lanServer.start=Start Shared World +lanServer.otherPlayers=Settings for Other Players +mcoServer.title=Minecraft Online World + +multiplayer.title=Play Multiplayer +multiplayer.connect=Connect +multiplayer.info1=Minecraft Multiplayer is currently not finished, but there +multiplayer.info2=is some buggy early testing going on. +multiplayer.ipinfo=Enter the IP of a server to connect to it: +multiplayer.texturePrompt.line1=This server recommends the use of a custom resource pack. +multiplayer.texturePrompt.line2=Would you like to download and install it automagically? +multiplayer.downloadingTerrain=Downloading terrain +multiplayer.downloadingStats=Downloading statistics & achievements... +multiplayer.stopSleeping=Leave Bed +multiplayer.player.joined=%s joined the game +multiplayer.player.joined.renamed=%s (formerly known as %s) joined the game +multiplayer.player.left=%s left the game + +chat.cannotSend=Cannot send chat message +chat.type.text=<%s> %s +chat.type.emote=* %s %s +chat.type.announcement=[%s] %s +chat.type.admin=[%s: %s] +chat.type.achievement=%s has just earned the achievement %s +chat.type.achievement.taken=%s has lost the achievement %s +chat.link.confirm=Are you sure you want to open the following website? +chat.link.warning=Never open links from people that you don't trust! +chat.copy=Copy to Clipboard +chat.link.confirmTrusted=Do you want to open this link or copy it to your clipboard? +chat.link.open=Open in browser + +chat.stream.text=(%s) <%s> %s +chat.stream.emote=(%s) * %s %s + +menu.playdemo=Play Demo World +menu.resetdemo=Reset Demo World + +demo.day.1=This demo will last five game days, do your best! +demo.day.2=Day Two +demo.day.3=Day Three +demo.day.4=Day Four +demo.day.5=This is your last day! +demo.day.warning=Your time is almost up! +demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation +demo.reminder=The demo time has expired, buy the game to continue or start a new world! +demo.remainingTime=Remaining time: %s +demo.demoExpired=Demo time's up! +demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around +demo.help.movementShort=Move by pressing %1$s, %2$s, %3$s, %4$s +demo.help.movementMouse=Look around using the mouse +demo.help.jump=Jump by pressing %1$s +demo.help.inventory=Use %1$s to open your inventory +demo.help.title=Minecraft Demo Mode +demo.help.fullWrapped=This demo will last 5 ingame days (about 1 hour and 40 minutes of real time). Check the achievements for hints! Have fun! +demo.help.buy=Purchase Now! +demo.help.later=Continue Playing! + +connect.connecting=Connecting to the server... +connect.authorizing=Logging in... +connect.failed=Failed to connect to the server + +disconnect.genericReason=%s +disconnect.disconnected=Disconnected by Server +disconnect.lost=Connection Lost +disconnect.kicked=Was kicked from the game +disconnect.timeout=Timed out +disconnect.closed=Connection closed +disconnect.loginFailed=Failed to login +disconnect.loginFailedInfo=Failed to login: %s +disconnect.loginFailedInfo.serversUnavailable=The authentication servers are currently down for maintenance. +disconnect.loginFailedInfo.invalidSession=Invalid session (Try restarting your game) +disconnect.quitting=Quitting +disconnect.endOfStream=End of stream +disconnect.overflow=Buffer overflow +disconnect.spam=Kicked for spamming + +soundCategory.master=Master Volume +soundCategory.music=Music +soundCategory.record=Jukebox/Noteblocks +soundCategory.weather=Weather +soundCategory.hostile=Hostile Creatures +soundCategory.neutral=Friendly Creatures +soundCategory.player=Players +soundCategory.block=Blocks +soundCategory.ambient=Ambient/Environment + +record.nowPlaying=Now playing: %s + +options.off=OFF +options.on=ON +options.visible=Shown +options.hidden=Hidden +options.title=Options +options.controls=Controls... +options.video=Video Settings... +options.language=Language... +options.stream=Broadcast Settings... +options.sounds=Music & Sounds... +options.sounds.title=Music & Sound Options +options.languageWarning=Language translations may not be 100%% accurate +options.videoTitle=Video Settings +options.customizeTitle=Customize World Settings +options.music=Music +options.sound=Sound +options.invertMouse=Invert Mouse +options.fov=FOV +options.fov.min=Normal +options.fov.max=Quake Pro +options.saturation=Saturation +options.gamma=Brightness +options.gamma.min=Moody +options.gamma.max=Bright +options.sensitivity=Sensitivity +options.sensitivity.min=*yawn* +options.sensitivity.max=HYPERSPEED!!! +options.renderDistance=Render Distance +options.renderDistance.tiny=Tiny +options.renderDistance.short=Short +options.renderDistance.normal=Normal +options.renderDistance.far=Far +options.viewBobbing=View Bobbing +options.ao=Smooth Lighting +options.ao.off=OFF +options.ao.min=Minimum +options.ao.max=Maximum +options.anaglyph=3D Anaglyph +options.framerateLimit=Max Framerate +options.framerateLimit.max=Unlimited +options.difficulty=Difficulty +options.difficulty.peaceful=Peaceful +options.difficulty.easy=Easy +options.difficulty.normal=Normal +options.difficulty.hard=Hard +options.difficulty.hardcore=Hardcore +options.graphics=Graphics +options.graphics.fancy=Fancy +options.graphics.fast=Fast +options.guiScale=GUI Scale +options.guiScale.auto=Auto +options.guiScale.small=Small +options.guiScale.normal=Normal +options.guiScale.large=Large +options.advancedOpengl=Advanced OpenGL +options.fboEnable=Enable FBOs +options.postProcessEnable=Enable Post-Processing +options.renderClouds=Clouds +options.qualityButton=Video Quality Settings... +options.qualityVideoTitle=Video Quality Settings +options.performanceButton=Video Performance Settings... +options.performanceVideoTitle=Video Performance Settings +options.advancedButton=Advanced Video Settings... +options.advancedVideoTitle=Advanced Video Settings +options.postButton=Post-Processing Settings... +options.postVideoTitle=Post-Processing Settings +options.farWarning1=A 64 bit Java installation is recommended +options.farWarning2=for 'Far' render distance (you have 32 bit) +options.particles=Particles +options.particles.all=All +options.particles.decreased=Decreased +options.particles.minimal=Minimal +options.multiplayer.title=Multiplayer Settings... +options.chat.title=Chat Settings... +options.chat.visibility=Chat +options.chat.visibility.full=Shown +options.chat.visibility.system=Commands Only +options.chat.visibility.hidden=Hidden +options.chat.color=Colors +options.chat.opacity=Opacity +options.chat.links=Web Links +options.chat.links.prompt=Prompt on Links +options.chat.scale=Scale +options.chat.width=Width +options.chat.height.focused=Focused Height +options.chat.height.unfocused=Unfocused Height +options.skinCustomisation=Skin Customization... +options.skinCustomisation.title=Skin Customization +options.skinCustomisation.enableFNAWSkins=Show FNAW Skins +options.modelPart.cape=Cape +options.modelPart.hat=Hat +options.modelPart.jacket=Jacket +options.modelPart.left_sleeve=Left Sleeve +options.modelPart.right_sleeve=Right Sleeve +options.modelPart.left_pants_leg=Left Pants Leg +options.modelPart.right_pants_leg=Right Pants Leg +options.snooper=Allow Snooper +options.snooper.view=Snooper Settings... +options.snooper.title=Machine Specs Collection +options.snooper.desc=We want to collect information about your machine to help improve Minecraft by knowing what we can support and where the biggest problems are. All of this information is completely anonymous and viewable below. We promise we won't do anything bad with this data, but if you want to opt out then feel free to toggle it off! +options.resourcepack=Resource Packs... +options.fullscreen=Fullscreen +options.vsync=Use VSync +options.vbo=Use VBOs +options.touchscreen=Touchscreen Mode +options.blockAlternatives=Alternate Blocks +options.reducedDebugInfo=Reduced Debug Info +options.entityShadows=Entity Shadows + +options.mipmapLevels=Mipmap Levels +options.forceUnicodeFont=Force Unicode Font + +options.stream.title=Twitch Broadcast Settings +options.stream.bytesPerPixel=Quality +options.stream.micVolumne=Mic Volume +options.stream.micToggleBehavior=Push To +options.stream.mic_toggle.mute=Mute +options.stream.mic_toggle.talk=Talk +options.stream.systemVolume=System Volume +options.stream.kbps=Bandwidth +options.stream.fps=Framerate +options.stream.sendMetadata=Send Metadata +options.stream.compression=Compression +options.stream.compression.low=Low +options.stream.compression.medium=Medium +options.stream.compression.high=High +options.stream.estimation=Estimated resolution: %dx%d +options.stream.changes=You may need to restart your stream for these changes to take place. +options.stream.ingestSelection=Broadcast Server List +options.stream.ingest.title=Twitch Broadcast Servers +options.stream.ingest.reset=Reset Preference +options.stream.chat.title=Twitch Chat Settings +options.stream.chat.enabled=Enable +options.stream.chat.enabled.streaming=Whilst Streaming +options.stream.chat.enabled.always=Always +options.stream.chat.enabled.never=Never +options.stream.chat.userFilter=User Filter +options.stream.chat.userFilter.all=All Viewers +options.stream.chat.userFilter.subs=Subscribers +options.stream.chat.userFilter.mods=Moderators + +difficulty.lock.title=Lock World Difficulty +difficulty.lock.question=Are you sure you want to lock the difficulty of this world? This will set this world to always be %1$s, and you will never be able to change that again. + +title.oldgl1=Old graphics card detected; this may prevent you from +title.oldgl2=playing in the future as OpenGL 2.0 will be required. + +controls.title=Controls +controls.reset=Reset +controls.resetAll=Reset Keys + +key.sprint=Sprint +key.forward=Walk Forwards +key.left=Strafe Left +key.back=Walk Backwards +key.right=Strafe Right +key.jump=Jump +key.inventory=Inventory +key.drop=Drop Item +key.chat=Open Chat +key.sneak=Sneak +key.playerlist=List Players +key.attack=Attack/Destroy +key.use=Use Item/Place Block +key.pickItem=Pick Block +key.mouseButton=Button %1$s +key.command=Open Command +key.screenshot=Take Screenshot +key.togglePerspective=Toggle Perspective +key.smoothCamera=Toggle Cinematic Camera +key.fullscreen=Toggle Fullscreen +key.spectatorOutlines=Highlight Players (Spectators) +key.hotbar.1=Hotbar Slot 1 +key.hotbar.2=Hotbar Slot 2 +key.hotbar.3=Hotbar Slot 3 +key.hotbar.4=Hotbar Slot 4 +key.hotbar.5=Hotbar Slot 5 +key.hotbar.6=Hotbar Slot 6 +key.hotbar.7=Hotbar Slot 7 +key.hotbar.8=Hotbar Slot 8 +key.hotbar.9=Hotbar Slot 9 +key.streamStartStop=Start/Stop Stream +key.streamPauseUnpause=Pause/Unpause Stream +key.streamCommercial=Show Stream Commercials +key.streamToggleMic=Push To Talk/Mute + +key.categories.movement=Movement +key.categories.misc=Miscellaneous +key.categories.multiplayer=Multiplayer +key.categories.gameplay=Gameplay +key.categories.ui=Game Interface +key.categories.inventory=Inventory +key.categories.stream=Streaming + +resourcePack.openFolder=Open resource pack +resourcePack.title=Select Resource Packs +resourcePack.available.title=Available Resource Packs +resourcePack.selected.title=Selected Resource Packs +resourcePack.folderInfo=(Select resource pack files here) +resourcePack.incompatible=Incompatible +resourcePack.incompatible.old=(Made for an older version of Minecraft) +resourcePack.incompatible.new=(Made for a newer version of Minecraft) +resourcePack.incompatible.confirm.title=Are you sure you want to load this resource pack? +resourcePack.incompatible.confirm.old=This resource pack was made for an older version of Minecraft and may no longer work correctly. +resourcePack.incompatible.confirm.new=This resource pack was made for a newer version of Minecraft and may no longer work correctly. + +sign.edit=Edit sign message + +book.pageIndicator=Page %1$s of %2$s +book.byAuthor=by %1$s +book.signButton=Sign +book.editTitle=Enter Book Title: +book.finalizeButton=Sign and Close +book.finalizeWarning=Note! When you sign the book, it will no longer be editable. +book.generation.0=Original +book.generation.1=Copy of original +book.generation.2=Copy of a copy +book.generation.3=Tattered + +merchant.deprecated=Trade something else to unlock! + +tile.barrier.name=Barrier +tile.stone.stone.name=Stone +tile.stone.granite.name=Granite +tile.stone.graniteSmooth.name=Polished Granite +tile.stone.diorite.name=Diorite +tile.stone.dioriteSmooth.name=Polished Diorite +tile.stone.andesite.name=Andesite +tile.stone.andesiteSmooth.name=Polished Andesite +tile.hayBlock.name=Hay Bale +tile.grass.name=Grass Block +tile.dirt.name=Dirt +tile.dirt.default.name=Dirt +tile.dirt.coarse.name=Coarse Dirt +tile.dirt.podzol.name=Podzol +tile.stonebrick.name=Cobblestone +tile.wood.name=Wooden Planks +tile.wood.oak.name=Oak Wood Planks +tile.wood.spruce.name=Spruce Wood Planks +tile.wood.birch.name=Birch Wood Planks +tile.wood.jungle.name=Jungle Wood Planks +tile.wood.acacia.name=Acacia Wood Planks +tile.wood.big_oak.name=Dark Oak Wood Planks +tile.sapling.oak.name=Oak Sapling +tile.sapling.spruce.name=Spruce Sapling +tile.sapling.birch.name=Birch Sapling +tile.sapling.jungle.name=Jungle Sapling +tile.sapling.acacia.name=Acacia Sapling +tile.sapling.big_oak.name=Dark Oak Sapling +tile.deadbush.name=Dead Bush +tile.bedrock.name=Bedrock +tile.water.name=Water +tile.lava.name=Lava +tile.sand.name=Sand +tile.sand.default.name=Sand +tile.sand.red.name=Red Sand +tile.sandStone.name=Sandstone +tile.sandStone.default.name=Sandstone +tile.sandStone.chiseled.name=Chiseled Sandstone +tile.sandStone.smooth.name=Smooth Sandstone +tile.redSandStone.name=Red Sandstone +tile.redSandStone.default.name=Red Sandstone +tile.redSandStone.chiseled.name=Chiseled Red Sandstone +tile.redSandStone.smooth.name=Smooth Red Sandstone +tile.gravel.name=Gravel +tile.oreGold.name=Gold Ore +tile.oreIron.name=Iron Ore +tile.oreCoal.name=Coal Ore +tile.log.name=Wood +tile.log.oak.name=Oak Wood +tile.log.spruce.name=Spruce Wood +tile.log.birch.name=Birch Wood +tile.log.jungle.name=Jungle Wood +tile.log.acacia.name=Acacia Wood +tile.log.big_oak.name=Dark Oak Wood +tile.leaves.name=Leaves +tile.leaves.oak.name=Oak Leaves +tile.leaves.spruce.name=Spruce Leaves +tile.leaves.birch.name=Birch Leaves +tile.leaves.jungle.name=Jungle Leaves +tile.leaves.acacia.name=Acacia Leaves +tile.leaves.big_oak.name=Dark Oak Leaves +tile.tallgrass.name=Grass +tile.tallgrass.shrub.name=Shrub +tile.tallgrass.grass.name=Grass +tile.tallgrass.fern.name=Fern +tile.sponge.dry.name=Sponge +tile.sponge.wet.name=Wet Sponge +tile.glass.name=Glass +tile.stainedGlass.name=Stained Glass +tile.stainedGlass.black.name=Black Stained Glass +tile.stainedGlass.red.name=Red Stained Glass +tile.stainedGlass.green.name=Green Stained Glass +tile.stainedGlass.brown.name=Brown Stained Glass +tile.stainedGlass.blue.name=Blue Stained Glass +tile.stainedGlass.purple.name=Purple Stained Glass +tile.stainedGlass.cyan.name=Cyan Stained Glass +tile.stainedGlass.silver.name=Light Gray Stained Glass +tile.stainedGlass.gray.name=Gray Stained Glass +tile.stainedGlass.pink.name=Pink Stained Glass +tile.stainedGlass.lime.name=Lime Stained Glass +tile.stainedGlass.yellow.name=Yellow Stained Glass +tile.stainedGlass.lightBlue.name=Light Blue Stained Glass +tile.stainedGlass.magenta.name=Magenta Stained Glass +tile.stainedGlass.orange.name=Orange Stained Glass +tile.stainedGlass.white.name=White Stained Glass +tile.thinStainedGlass.name=Stained Glass Pane +tile.thinStainedGlass.black.name=Black Stained Glass Pane +tile.thinStainedGlass.red.name=Red Stained Glass Pane +tile.thinStainedGlass.green.name=Green Stained Glass Pane +tile.thinStainedGlass.brown.name=Brown Stained Glass Pane +tile.thinStainedGlass.blue.name=Blue Stained Glass Pane +tile.thinStainedGlass.purple.name=Purple Stained Glass Pane +tile.thinStainedGlass.cyan.name=Cyan Stained Glass Pane +tile.thinStainedGlass.silver.name=Light Gray Stained Glass Pane +tile.thinStainedGlass.gray.name=Gray Stained Glass Pane +tile.thinStainedGlass.pink.name=Pink Stained Glass Pane +tile.thinStainedGlass.lime.name=Lime Stained Glass Pane +tile.thinStainedGlass.yellow.name=Yellow Stained Glass Pane +tile.thinStainedGlass.lightBlue.name=Light Blue Stained Glass Pane +tile.thinStainedGlass.magenta.name=Magenta Stained Glass Pane +tile.thinStainedGlass.orange.name=Orange Stained Glass Pane +tile.thinStainedGlass.white.name=White Stained Glass Pane +tile.thinGlass.name=Glass Pane +tile.cloth.name=Wool +tile.flower1.name=Flower +tile.flower1.dandelion.name=Dandelion +tile.flower2.name=Flower +tile.flower2.poppy.name=Poppy +tile.flower2.blueOrchid.name=Blue Orchid +tile.flower2.allium.name=Allium +tile.flower2.houstonia.name=Azure Bluet +tile.flower2.tulipRed.name=Red Tulip +tile.flower2.tulipOrange.name=Orange Tulip +tile.flower2.tulipWhite.name=White Tulip +tile.flower2.tulipPink.name=Pink Tulip +tile.flower2.oxeyeDaisy.name=Oxeye Daisy +tile.doublePlant.name=Plant +tile.doublePlant.sunflower.name=Sunflower +tile.doublePlant.syringa.name=Lilac +tile.doublePlant.grass.name=Double Tallgrass +tile.doublePlant.fern.name=Large Fern +tile.doublePlant.rose.name=Rose Bush +tile.doublePlant.paeonia.name=Peony +tile.mushroom.name=Mushroom +tile.blockGold.name=Block of Gold +tile.blockIron.name=Block of Iron +tile.stoneSlab.name=Stone Slab +tile.stoneSlab.stone.name=Stone Slab +tile.stoneSlab.sand.name=Sandstone Slab +tile.stoneSlab.wood.name=Wooden Slab +tile.stoneSlab.cobble.name=Cobblestone Slab +tile.stoneSlab.brick.name=Bricks Slab +tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab +tile.stoneSlab.netherBrick.name=Nether Brick Slab +tile.stoneSlab.quartz.name=Quartz Slab +tile.stoneSlab2.red_sandstone.name=Red Sandstone Slab +tile.woodSlab.name=Wood Slab +tile.woodSlab.oak.name=Oak Wood Slab +tile.woodSlab.spruce.name=Spruce Wood Slab +tile.woodSlab.birch.name=Birch Wood Slab +tile.woodSlab.jungle.name=Jungle Wood Slab +tile.woodSlab.acacia.name=Acacia Wood Slab +tile.woodSlab.big_oak.name=Dark Oak Wood Slab +tile.brick.name=Bricks +tile.tnt.name=TNT +tile.bookshelf.name=Bookshelf +tile.stoneMoss.name=Moss Stone +tile.obsidian.name=Obsidian +tile.torch.name=Torch +tile.fire.name=Fire +tile.mobSpawner.name=Monster Spawner +tile.stairsWood.name=Oak Wood Stairs +tile.stairsWoodSpruce.name=Spruce Wood Stairs +tile.stairsWoodBirch.name=Birch Wood Stairs +tile.stairsWoodJungle.name=Jungle Wood Stairs +tile.stairsWoodAcacia.name=Acacia Wood Stairs +tile.stairsWoodDarkOak.name=Dark Oak Wood Stairs +tile.chest.name=Chest +tile.chestTrap.name=Trapped Chest +tile.redstoneDust.name=Redstone Dust +tile.oreDiamond.name=Diamond Ore +tile.blockCoal.name=Block of Coal +tile.blockDiamond.name=Block of Diamond +tile.workbench.name=Crafting Table +tile.crops.name=Crops +tile.farmland.name=Farmland +tile.furnace.name=Furnace +tile.sign.name=Sign +tile.doorWood.name=Wooden Door +tile.ladder.name=Ladder +tile.rail.name=Rail +tile.goldenRail.name=Powered Rail +tile.activatorRail.name=Activator Rail +tile.detectorRail.name=Detector Rail +tile.stairsStone.name=Cobblestone Stairs +tile.stairsSandStone.name=Sandstone Stairs +tile.stairsRedSandStone.name=Red Sandstone Stairs +tile.lever.name=Lever +tile.pressurePlateStone.name=Stone Pressure Plate +tile.pressurePlateWood.name=Wooden Pressure Plate +tile.weightedPlate_light.name=Weighted Pressure Plate (Light) +tile.weightedPlate_heavy.name=Weighted Pressure Plate (Heavy) +tile.doorIron.name=Iron Door +tile.oreRedstone.name=Redstone Ore +tile.notGate.name=Redstone Torch +tile.button.name=Button +tile.snow.name=Snow +tile.woolCarpet.name=Carpet +tile.woolCarpet.black.name=Black Carpet +tile.woolCarpet.red.name=Red Carpet +tile.woolCarpet.green.name=Green Carpet +tile.woolCarpet.brown.name=Brown Carpet +tile.woolCarpet.blue.name=Blue Carpet +tile.woolCarpet.purple.name=Purple Carpet +tile.woolCarpet.cyan.name=Cyan Carpet +tile.woolCarpet.silver.name=Light Gray Carpet +tile.woolCarpet.gray.name=Gray Carpet +tile.woolCarpet.pink.name=Pink Carpet +tile.woolCarpet.lime.name=Lime Carpet +tile.woolCarpet.yellow.name=Yellow Carpet +tile.woolCarpet.lightBlue.name=Light Blue Carpet +tile.woolCarpet.magenta.name=Magenta Carpet +tile.woolCarpet.orange.name=Orange Carpet +tile.woolCarpet.white.name=Carpet +tile.ice.name=Ice +tile.frostedIce.name=Frosted Ice +tile.icePacked.name=Packed Ice +tile.cactus.name=Cactus +tile.clay.name=Clay +tile.clayHardenedStained.name=Stained Clay +tile.clayHardenedStained.black.name=Black Stained Clay +tile.clayHardenedStained.red.name=Red Stained Clay +tile.clayHardenedStained.green.name=Green Stained Clay +tile.clayHardenedStained.brown.name=Brown Stained Clay +tile.clayHardenedStained.blue.name=Blue Stained Clay +tile.clayHardenedStained.purple.name=Purple Stained Clay +tile.clayHardenedStained.cyan.name=Cyan Stained Clay +tile.clayHardenedStained.silver.name=Light Gray Stained Clay +tile.clayHardenedStained.gray.name=Gray Stained Clay +tile.clayHardenedStained.pink.name=Pink Stained Clay +tile.clayHardenedStained.lime.name=Lime Stained Clay +tile.clayHardenedStained.yellow.name=Yellow Stained Clay +tile.clayHardenedStained.lightBlue.name=Light Blue Stained Clay +tile.clayHardenedStained.magenta.name=Magenta Stained Clay +tile.clayHardenedStained.orange.name=Orange Stained Clay +tile.clayHardenedStained.white.name=White Stained Clay +tile.clayHardened.name=Hardened Clay +tile.reeds.name=Sugar cane +tile.jukebox.name=Jukebox +tile.fence.name=Oak Fence +tile.spruceFence.name=Spruce Fence +tile.birchFence.name=Birch Fence +tile.jungleFence.name=Jungle Fence +tile.darkOakFence.name=Dark Oak Fence +tile.acaciaFence.name=Acacia Fence +tile.fenceGate.name=Oak Fence Gate +tile.spruceFenceGate.name=Spruce Fence Gate +tile.birchFenceGate.name=Birch Fence Gate +tile.jungleFenceGate.name=Jungle Fence Gate +tile.darkOakFenceGate.name=Dark Oak Fence Gate +tile.acaciaFenceGate.name=Acacia Fence Gate +tile.pumpkinStem.name=Pumpkin Stem +tile.pumpkin.name=Pumpkin +tile.litpumpkin.name=Jack o'Lantern +tile.hellrock.name=Netherrack +tile.hellsand.name=Soul Sand +tile.lightgem.name=Glowstone +tile.portal.name=Portal +tile.cloth.black.name=Black Wool +tile.cloth.red.name=Red Wool +tile.cloth.green.name=Green Wool +tile.cloth.brown.name=Brown Wool +tile.cloth.blue.name=Blue Wool +tile.cloth.purple.name=Purple Wool +tile.cloth.cyan.name=Cyan Wool +tile.cloth.silver.name=Light Gray Wool +tile.cloth.gray.name=Gray Wool +tile.cloth.pink.name=Pink Wool +tile.cloth.lime.name=Lime Wool +tile.cloth.yellow.name=Yellow Wool +tile.cloth.lightBlue.name=Light Blue Wool +tile.cloth.magenta.name=Magenta Wool +tile.cloth.orange.name=Orange Wool +tile.cloth.white.name=Wool +tile.oreLapis.name=Lapis Lazuli Ore +tile.blockLapis.name=Lapis Lazuli Block +tile.dispenser.name=Dispenser +tile.dropper.name=Dropper +tile.musicBlock.name=Note Block +tile.cake.name=Cake +tile.bed.name=Bed +tile.bed.occupied=This bed is occupied +tile.bed.noSleep=You can only sleep at night +tile.bed.notSafe=You may not rest now, there are monsters nearby +tile.bed.notValid=Your home bed was missing or obstructed +tile.lockedchest.name=Locked chest +tile.trapdoor.name=Wooden Trapdoor +tile.ironTrapdoor.name=Iron Trapdoor +tile.web.name=Cobweb +tile.stonebricksmooth.name=Stone Bricks +tile.stonebricksmooth.default.name=Stone Bricks +tile.stonebricksmooth.mossy.name=Mossy Stone Bricks +tile.stonebricksmooth.cracked.name=Cracked Stone Bricks +tile.stonebricksmooth.chiseled.name=Chiseled Stone Bricks +tile.monsterStoneEgg.name=Stone Monster Egg +tile.monsterStoneEgg.stone.name=Stone Monster Egg +tile.monsterStoneEgg.cobble.name=Cobblestone Monster Egg +tile.monsterStoneEgg.brick.name=Stone Brick Monster Egg +tile.monsterStoneEgg.mossybrick.name=Mossy Stone Brick Monster Egg +tile.monsterStoneEgg.crackedbrick.name=Cracked Stone Brick Monster Egg +tile.monsterStoneEgg.chiseledbrick.name=Chiseled Stone Brick Monster Egg +tile.pistonBase.name=Piston +tile.pistonStickyBase.name=Sticky Piston +tile.fenceIron.name=Iron Bars +tile.melon.name=Melon +tile.stairsBrick.name=Brick Stairs +tile.stairsStoneBrickSmooth.name=Stone Brick Stairs +tile.vine.name=Vines +tile.netherBrick.name=Nether Brick +tile.netherFence.name=Nether Brick Fence +tile.stairsNetherBrick.name=Nether Brick Stairs +tile.netherStalk.name=Nether Wart +tile.cauldron.name=Cauldron +tile.enchantmentTable.name=Enchantment Table +tile.anvil.name=Anvil +tile.anvil.intact.name=Anvil +tile.anvil.slightlyDamaged.name=Slightly Damaged Anvil +tile.anvil.veryDamaged.name=Very Damaged Anvil +tile.whiteStone.name=End Stone +tile.endPortalFrame.name=End Portal +tile.mycel.name=Mycelium +tile.waterlily.name=Lily Pad +tile.dragonEgg.name=Dragon Egg +tile.redstoneLight.name=Redstone Lamp +tile.cocoa.name=Cocoa +tile.enderChest.name=Ender Chest +tile.oreRuby.name=Ruby Ore +tile.oreEmerald.name=Emerald Ore +tile.blockEmerald.name=Block of Emerald +tile.blockRedstone.name=Block of Redstone +tile.tripWire.name=Tripwire +tile.tripWireSource.name=Tripwire Hook +tile.commandBlock.name=Command Block +tile.beacon.name=Beacon +tile.beacon.primary=Primary Power +tile.beacon.secondary=Secondary Power +tile.cobbleWall.normal.name=Cobblestone Wall +tile.cobbleWall.mossy.name=Mossy Cobblestone Wall +tile.carrots.name=Carrots +tile.potatoes.name=Potatoes +tile.daylightDetector.name=Daylight Sensor +tile.netherquartz.name=Nether Quartz Ore +tile.hopper.name=Hopper +tile.quartzBlock.name=Block of Quartz +tile.quartzBlock.default.name=Block of Quartz +tile.quartzBlock.chiseled.name=Chiseled Quartz Block +tile.quartzBlock.lines.name=Pillar Quartz Block +tile.stairsQuartz.name=Quartz Stairs +tile.slime.name=Slime Block +tile.prismarine.rough.name=Prismarine +tile.prismarine.bricks.name=Prismarine Bricks +tile.prismarine.dark.name=Dark Prismarine +tile.seaLantern.name=Sea Lantern + +item.nameTag.name=Name Tag +item.leash.name=Lead +item.shovelIron.name=Iron Shovel +item.pickaxeIron.name=Iron Pickaxe +item.hatchetIron.name=Iron Axe +item.flintAndSteel.name=Flint and Steel +item.apple.name=Apple +item.cookie.name=Cookie +item.bow.name=Bow +item.arrow.name=Arrow +item.coal.name=Coal +item.charcoal.name=Charcoal +item.diamond.name=Diamond +item.emerald.name=Emerald +item.ingotIron.name=Iron Ingot +item.ingotGold.name=Gold Ingot +item.swordIron.name=Iron Sword +item.swordWood.name=Wooden Sword +item.shovelWood.name=Wooden Shovel +item.pickaxeWood.name=Wooden Pickaxe +item.hatchetWood.name=Wooden Axe +item.swordStone.name=Stone Sword +item.shovelStone.name=Stone Shovel +item.pickaxeStone.name=Stone Pickaxe +item.hatchetStone.name=Stone Axe +item.swordDiamond.name=Diamond Sword +item.shovelDiamond.name=Diamond Shovel +item.pickaxeDiamond.name=Diamond Pickaxe +item.hatchetDiamond.name=Diamond Axe +item.stick.name=Stick +item.bowl.name=Bowl +item.mushroomStew.name=Mushroom Stew +item.swordGold.name=Golden Sword +item.shovelGold.name=Golden Shovel +item.pickaxeGold.name=Golden Pickaxe +item.hatchetGold.name=Golden Axe +item.string.name=String +item.feather.name=Feather +item.sulphur.name=Gunpowder +item.hoeWood.name=Wooden Hoe +item.hoeStone.name=Stone Hoe +item.hoeIron.name=Iron Hoe +item.hoeDiamond.name=Diamond Hoe +item.hoeGold.name=Golden Hoe +item.seeds.name=Seeds +item.seeds_pumpkin.name=Pumpkin Seeds +item.seeds_melon.name=Melon Seeds +item.melon.name=Melon +item.wheat.name=Wheat +item.bread.name=Bread +item.helmetCloth.name=Leather Cap +item.chestplateCloth.name=Leather Tunic +item.leggingsCloth.name=Leather Pants +item.bootsCloth.name=Leather Boots +item.helmetChain.name=Chain Helmet +item.chestplateChain.name=Chain Chestplate +item.leggingsChain.name=Chain Leggings +item.bootsChain.name=Chain Boots +item.helmetIron.name=Iron Helmet +item.chestplateIron.name=Iron Chestplate +item.leggingsIron.name=Iron Leggings +item.bootsIron.name=Iron Boots +item.helmetDiamond.name=Diamond Helmet +item.chestplateDiamond.name=Diamond Chestplate +item.leggingsDiamond.name=Diamond Leggings +item.bootsDiamond.name=Diamond Boots +item.helmetGold.name=Golden Helmet +item.chestplateGold.name=Golden Chestplate +item.leggingsGold.name=Golden Leggings +item.bootsGold.name=Golden Boots +item.flint.name=Flint +item.porkchopRaw.name=Raw Porkchop +item.porkchopCooked.name=Cooked Porkchop +item.chickenRaw.name=Raw Chicken +item.chickenCooked.name=Cooked Chicken +item.muttonRaw.name=Raw Mutton +item.muttonCooked.name=Cooked Mutton +item.rabbitRaw.name=Raw Rabbit +item.rabbitCooked.name=Cooked Rabbit +item.rabbitStew.name=Rabbit Stew +item.rabbitFoot.name=Rabbit's Foot +item.rabbitHide.name=Rabbit Hide +item.beefRaw.name=Raw Beef +item.beefCooked.name=Steak +item.painting.name=Painting +item.frame.name=Item Frame +item.appleGold.name=Golden Apple +item.sign.name=Sign +item.doorOak.name=Oak Door +item.doorSpruce.name=Spruce Door +item.doorBirch.name=Birch Door +item.doorJungle.name=Jungle Door +item.doorAcacia.name=Acacia Door +item.doorDarkOak.name=Dark Oak Door +item.bucket.name=Bucket +item.bucketWater.name=Water Bucket +item.bucketLava.name=Lava Bucket +item.minecart.name=Minecart +item.saddle.name=Saddle +item.doorIron.name=Iron Door +item.redstone.name=Redstone +item.snowball.name=Snowball +item.boat.name=Boat +item.leather.name=Leather +item.milk.name=Milk +item.brick.name=Brick +item.clay.name=Clay +item.reeds.name=Sugar Canes +item.paper.name=Paper +item.book.name=Book +item.slimeball.name=Slimeball +item.minecartChest.name=Minecart with Chest +item.minecartFurnace.name=Minecart with Furnace +item.minecartTnt.name=Minecart with TNT +item.minecartHopper.name=Minecart with Hopper +item.minecartCommandBlock.name=Minecart with Command Block +item.egg.name=Egg +item.compass.name=Compass +item.fishingRod.name=Fishing Rod +item.clock.name=Clock +item.yellowDust.name=Glowstone Dust +item.fish.cod.raw.name=Raw Fish +item.fish.salmon.raw.name=Raw Salmon +item.fish.pufferfish.raw.name=Pufferfish +item.fish.clownfish.raw.name=Clownfish +item.fish.cod.cooked.name=Cooked Fish +item.fish.salmon.cooked.name=Cooked Salmon +item.record.name=Music Disc +item.record.13.desc=C418 - 13 +item.record.cat.desc=C418 - cat +item.record.blocks.desc=C418 - blocks +item.record.chirp.desc=C418 - chirp +item.record.far.desc=C418 - far +item.record.mall.desc=C418 - mall +item.record.mellohi.desc=C418 - mellohi +item.record.stal.desc=C418 - stal +item.record.strad.desc=C418 - strad +item.record.ward.desc=C418 - ward +item.record.11.desc=C418 - 11 +item.record.wait.desc=C418 - wait +item.bone.name=Bone +item.dyePowder.black.name=Ink Sac +item.dyePowder.red.name=Rose Red +item.dyePowder.green.name=Cactus Green +item.dyePowder.brown.name=Cocoa Beans +item.dyePowder.blue.name=Lapis Lazuli +item.dyePowder.purple.name=Purple Dye +item.dyePowder.cyan.name=Cyan Dye +item.dyePowder.silver.name=Light Gray Dye +item.dyePowder.gray.name=Gray Dye +item.dyePowder.pink.name=Pink Dye +item.dyePowder.lime.name=Lime Dye +item.dyePowder.yellow.name=Dandelion Yellow +item.dyePowder.lightBlue.name=Light Blue Dye +item.dyePowder.magenta.name=Magenta Dye +item.dyePowder.orange.name=Orange Dye +item.dyePowder.white.name=Bone Meal +item.sugar.name=Sugar +item.cake.name=Cake +item.bed.name=Bed +item.diode.name=Redstone Repeater +item.comparator.name=Redstone Comparator +item.map.name=Map +item.leaves.name=Leaves +item.shears.name=Shears +item.rottenFlesh.name=Rotten Flesh +item.enderPearl.name=Ender Pearl +item.blazeRod.name=Blaze Rod +item.ghastTear.name=Ghast Tear +item.netherStalkSeeds.name=Nether Wart +item.potion.name=Potion +item.emptyPotion.name=Water Bottle +item.goldNugget.name=Gold Nugget +item.glassBottle.name=Glass Bottle +item.spiderEye.name=Spider Eye +item.fermentedSpiderEye.name=Fermented Spider Eye +item.blazePowder.name=Blaze Powder +item.magmaCream.name=Magma Cream +item.cauldron.name=Cauldron +item.brewingStand.name=Brewing Stand +item.eyeOfEnder.name=Eye of Ender +item.speckledMelon.name=Glistering Melon +item.monsterPlacer.name=Spawn +item.expBottle.name=Bottle o' Enchanting +item.fireball.name=Fire Charge +item.writingBook.name=Book and Quill +item.writtenBook.name=Written Book +item.ruby.name=Ruby +item.flowerPot.name=Flower Pot +item.emptyMap.name=Empty Map +item.carrots.name=Carrot +item.carrotGolden.name=Golden Carrot +item.potato.name=Potato +item.potatoBaked.name=Baked Potato +item.potatoPoisonous.name=Poisonous Potato +item.skull.skeleton.name=Skeleton Skull +item.skull.wither.name=Wither Skeleton Skull +item.skull.zombie.name=Zombie Head +item.skull.char.name=Head +item.skull.player.name=%s's Head +item.skull.creeper.name=Creeper Head +item.skull.dragon.name=Dragon Head +item.carrotOnAStick.name=Carrot on a Stick +item.netherStar.name=Nether Star +item.pumpkinPie.name=Pumpkin Pie +item.enchantedBook.name=Enchanted Book +item.fireworks.name=Firework Rocket +item.fireworks.flight=Flight Duration: +item.fireworksCharge.name=Firework Star +item.fireworksCharge.black=Black +item.fireworksCharge.red=Red +item.fireworksCharge.green=Green +item.fireworksCharge.brown=Brown +item.fireworksCharge.blue=Blue +item.fireworksCharge.purple=Purple +item.fireworksCharge.cyan=Cyan +item.fireworksCharge.silver=Light Gray +item.fireworksCharge.gray=Gray +item.fireworksCharge.pink=Pink +item.fireworksCharge.lime=Lime +item.fireworksCharge.yellow=Yellow +item.fireworksCharge.lightBlue=Light Blue +item.fireworksCharge.magenta=Magenta +item.fireworksCharge.orange=Orange +item.fireworksCharge.white=White +item.fireworksCharge.customColor=Custom +item.fireworksCharge.fadeTo=Fade to +item.fireworksCharge.flicker=Twinkle +item.fireworksCharge.trail=Trail +item.fireworksCharge.type.0=Small Ball +item.fireworksCharge.type.1=Large Ball +item.fireworksCharge.type.2=Star-shaped +item.fireworksCharge.type.3=Creeper-shaped +item.fireworksCharge.type.4=Burst +item.fireworksCharge.type=Unknown Shape +item.netherbrick.name=Nether Brick +item.netherquartz.name=Nether Quartz +item.armorStand.name=Armor Stand +item.horsearmormetal.name=Iron Horse Armor +item.horsearmorgold.name=Gold Horse Armor +item.horsearmordiamond.name=Diamond Horse Armor +item.prismarineShard.name=Prismarine Shard +item.prismarineCrystals.name=Prismarine Crystals + +container.inventory=Inventory +container.hopper=Item Hopper +container.crafting=Crafting +container.dispenser=Dispenser +container.dropper=Dropper +container.furnace=Furnace +container.enchant=Enchant +container.enchant.lapis.one=1 Lapis Lazuli +container.enchant.lapis.many=%d Lapis Lazuli +container.enchant.level.one=1 Enchantment Level +container.enchant.level.many=%d Enchantment Levels +container.enchant.clue=%s . . . ? +container.repair=Repair & Name +container.repair.cost=Enchantment Cost: %1$d +container.repair.expensive=Too Expensive! +container.creative=Item Selection +container.brewing=Brewing Stand +container.chest=Chest +container.chestDouble=Large Chest +container.minecart=Minecart +container.enderchest=Ender Chest +container.beacon=Beacon + +container.isLocked=%s is locked! + +item.dyed=Dyed +item.unbreakable=Unbreakable +item.canBreak=Can break: +item.canPlace=Can be placed on: + +entity.Item.name=Item +entity.XPOrb.name=Experience Orb +entity.SmallFireball.name=Small Fireball +entity.Fireball.name=Fireball +entity.ThrownPotion.name=Potion + +entity.Arrow.name=Arrow +entity.Snowball.name=Snowball +entity.Painting.name=Painting +entity.ArmorStand.name=Armor Stand + +entity.Mob.name=Mob +entity.Monster.name=Monster + +entity.Creeper.name=Creeper +entity.Skeleton.name=Skeleton +entity.Spider.name=Spider +entity.Giant.name=Giant +entity.Zombie.name=Zombie +entity.Slime.name=Slime +entity.Ghast.name=Ghast +entity.PigZombie.name=Zombie Pigman +entity.Enderman.name=Enderman +entity.Endermite.name=Endermite +entity.Silverfish.name=Silverfish +entity.CaveSpider.name=Cave Spider +entity.Blaze.name=Blaze +entity.LavaSlime.name=Magma Cube +entity.MushroomCow.name=Mooshroom +entity.Villager.name=Villager +entity.VillagerGolem.name=Iron Golem +entity.SnowMan.name=Snow Golem +entity.EnderDragon.name=Ender Dragon +entity.WitherBoss.name=Wither +entity.Witch.name=Witch +entity.Guardian.name=Guardian + +entity.Villager.farmer=Farmer +entity.Villager.fisherman=Fisherman +entity.Villager.shepherd=Shepherd +entity.Villager.fletcher=Fletcher +entity.Villager.librarian=Librarian +entity.Villager.cleric=Cleric +entity.Villager.armor=Armorer +entity.Villager.weapon=Weapon Smith +entity.Villager.tool=Tool Smith +entity.Villager.butcher=Butcher +entity.Villager.leather=Leatherworker + +entity.Pig.name=Pig +entity.Sheep.name=Sheep +entity.Cow.name=Cow +entity.Chicken.name=Chicken +entity.Squid.name=Squid +entity.Wolf.name=Wolf +entity.Ozelot.name=Ocelot +entity.Cat.name=Cat +entity.Bat.name=Bat +entity.EntityHorse.name=Horse +entity.horse.name=Horse +entity.donkey.name=Donkey +entity.mule.name=Mule +entity.skeletonhorse.name=Skeleton Horse +entity.zombiehorse.name=Zombie Horse +entity.Rabbit.name=Rabbit +entity.KillerBunny.name=The Killer Bunny + +entity.PrimedTnt.name=Block of TNT +entity.FallingSand.name=Falling Block + +entity.Minecart.name=Minecart +entity.Boat.name=Boat + +entity.generic.name=unknown + +death.fell.accident.ladder=%1$s fell off a ladder +death.fell.accident.vines=%1$s fell off some vines +death.fell.accident.water=%1$s fell out of the water +death.fell.accident.generic=%1$s fell from a high place +death.fell.killer=%1$s was doomed to fall +death.fell.assist=%1$s was doomed to fall by %2$s +death.fell.assist.item=%1$s was doomed to fall by %2$s using %3$s +death.fell.finish=%1$s fell too far and was finished by %2$s +death.fell.finish.item=%1$s fell too far and was finished by %2$s using %3$s + +death.attack.lightningBolt=%1$s was struck by lightning +death.attack.inFire=%1$s went up in flames +death.attack.inFire.player=%1$s walked into fire whilst fighting %2$s +death.attack.onFire=%1$s burned to death +death.attack.onFire.player=%1$s was burnt to a crisp whilst fighting %2$s +death.attack.lava=%1$s tried to swim in lava +death.attack.lava.player=%1$s tried to swim in lava to escape %2$s +death.attack.inWall=%1$s suffocated in a wall +death.attack.drown=%1$s drowned +death.attack.drown.player=%1$s drowned whilst trying to escape %2$s +death.attack.starve=%1$s starved to death +death.attack.cactus=%1$s was pricked to death +death.attack.cactus.player=%1$s walked into a cactus whilst trying to escape %2$s +death.attack.generic=%1$s died +death.attack.explosion=%1$s blew up +death.attack.explosion.player=%1$s was blown up by %2$s +death.attack.magic=%1$s was killed by magic +death.attack.wither=%1$s withered away +death.attack.anvil=%1$s was squashed by a falling anvil +death.attack.fallingBlock=%1$s was squashed by a falling block +death.attack.mob=%1$s was slain by %2$s +death.attack.player=%1$s was slain by %2$s +death.attack.player.item=%1$s was slain by %2$s using %3$s +death.attack.arrow=%1$s was shot by %2$s +death.attack.arrow.item=%1$s was shot by %2$s using %3$s +death.attack.fireball=%1$s was fireballed by %2$s +death.attack.fireball.item=%1$s was fireballed by %2$s using %3$s +death.attack.thrown=%1$s was pummeled by %2$s +death.attack.thrown.item=%1$s was pummeled by %2$s using %3$s +death.attack.indirectMagic=%1$s was killed by %2$s using magic +death.attack.indirectMagic.item=%1$s was killed by %2$s using %3$s +death.attack.thorns=%1$s was killed trying to hurt %2$s +death.attack.fall=%1$s hit the ground too hard +death.attack.outOfWorld=%1$s fell out of the world + +deathScreen.respawn=Respawn +deathScreen.deleteWorld=Delete world +deathScreen.titleScreen=Title screen +deathScreen.score=Score +deathScreen.title.hardcore=Game over! +deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! +deathScreen.title=You died! +deathScreen.leaveServer=Leave server +deathScreen.quit.confirm=Are you sure you want to quit? + +potion.effects.whenDrank=When Applied: +potion.empty=No Effects +potion.moveSpeed=Speed +potion.moveSlowdown=Slowness +potion.digSpeed=Haste +potion.digSlowDown=Mining Fatigue +potion.damageBoost=Strength +potion.heal=Instant Health +potion.harm=Instant Damage +potion.jump=Jump Boost +potion.confusion=Nausea +potion.regeneration=Regeneration +potion.resistance=Resistance +potion.fireResistance=Fire Resistance +potion.waterBreathing=Water Breathing +potion.invisibility=Invisibility +potion.blindness=Blindness +potion.nightVision=Night Vision +potion.hunger=Hunger +potion.weakness=Weakness +potion.poison=Poison +potion.wither=Wither +potion.healthBoost=Health Boost +potion.absorption=Absorption +potion.saturation=Saturation +potion.glowing=Glowing +potion.luck=Luck +potion.unluck=Bad Luck +potion.levitation=Levitation + +potion.moveSpeed.postfix=Potion of Swiftness +potion.moveSlowdown.postfix=Potion of Slowness +potion.digSpeed.postfix=Potion of Haste +potion.digSlowDown.postfix=Potion of Dullness +potion.damageBoost.postfix=Potion of Strength +potion.weakness.postfix=Potion of Weakness +potion.heal.postfix=Potion of Healing +potion.harm.postfix=Potion of Harming +potion.jump.postfix=Potion of Leaping +potion.confusion.postfix=Potion of Nausea +potion.regeneration.postfix=Potion of Regeneration +potion.resistance.postfix=Potion of Resistance +potion.fireResistance.postfix=Potion of Fire Resistance +potion.waterBreathing.postfix=Potion of Water Breathing +potion.invisibility.postfix=Potion of Invisibility +potion.blindness.postfix=Potion of Blindness +potion.nightVision.postfix=Potion of Night Vision +potion.hunger.postfix=Potion of Hunger +potion.poison.postfix=Potion of Poison +potion.wither.postfix=Potion of Decay +potion.healthBoost.postfix=Potion of Health Boost +potion.absorption.postfix=Potion of Absorption +potion.saturation.postfix=Potion of Saturation + +potion.potency.0= +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV + +potion.prefix.grenade=Splash +potion.prefix.mundane=Mundane +potion.prefix.uninteresting=Uninteresting +potion.prefix.bland=Bland +potion.prefix.clear=Clear +potion.prefix.milky=Milky +potion.prefix.diffuse=Diffuse +potion.prefix.artless=Artless +potion.prefix.thin=Thin +potion.prefix.awkward=Awkward +potion.prefix.flat=Flat +potion.prefix.bulky=Bulky +potion.prefix.bungling=Bungling +potion.prefix.buttered=Buttered +potion.prefix.smooth=Smooth +potion.prefix.suave=Suave +potion.prefix.debonair=Debonair +potion.prefix.thick=Thick +potion.prefix.elegant=Elegant +potion.prefix.fancy=Fancy +potion.prefix.charming=Charming +potion.prefix.dashing=Dashing +potion.prefix.refined=Refined +potion.prefix.cordial=Cordial +potion.prefix.sparkling=Sparkling +potion.prefix.potent=Potent +potion.prefix.foul=Foul +potion.prefix.odorless=Odorless +potion.prefix.rank=Rank +potion.prefix.harsh=Harsh +potion.prefix.acrid=Acrid +potion.prefix.gross=Gross +potion.prefix.stinky=Stinky + +enchantment.damage.all=Sharpness +enchantment.damage.undead=Smite +enchantment.damage.arthropods=Bane of Arthropods +enchantment.knockback=Knockback +enchantment.fire=Fire Aspect +enchantment.protect.all=Protection +enchantment.protect.fire=Fire Protection +enchantment.protect.fall=Feather Falling +enchantment.protect.explosion=Blast Protection +enchantment.protect.projectile=Projectile Protection +enchantment.oxygen=Respiration +enchantment.waterWorker=Aqua Affinity +enchantment.waterWalker=Depth Strider +enchantment.frostWalker=Frost Walker +enchantment.digging=Efficiency +enchantment.untouching=Silk Touch +enchantment.durability=Unbreaking +enchantment.lootBonus=Looting +enchantment.lootBonusDigger=Fortune +enchantment.lootBonusFishing=Luck of the Sea +enchantment.fishingSpeed=Lure +enchantment.arrowDamage=Power +enchantment.arrowFire=Flame +enchantment.arrowKnockback=Punch +enchantment.arrowInfinite=Infinity +enchantment.thorns=Thorns +enchantment.mending=Mending + +enchantment.level.1=I +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.level.10=X + +gui.achievements=Achievements +gui.stats=Statistics + +stats.tooltip.type.achievement=Achievement +stats.tooltip.type.statistic=Statistic +stat.generalButton=General +stat.blocksButton=Blocks +stat.itemsButton=Items +stat.mobsButton=Mobs + +stat.used=Times Used +stat.mined=Times Mined +stat.depleted=Times Depleted +stat.crafted=Times Crafted +stat.entityKills=You killed %d %s +stat.entityKilledBy=%s killed you %d time(s) +stat.entityKills.none=You have never killed %s +stat.entityKilledBy.none=You have never been killed by %s + +stat.startGame=Times played +stat.createWorld=Worlds created +stat.loadWorld=Saves loaded +stat.joinMultiplayer=Multiplayer joins +stat.leaveGame=Games quit + +stat.playOneMinute=Minutes Played +stat.timeSinceDeath=Since Last Death + +stat.walkOneCm=Distance Walked +stat.crouchOneCm=Distance Crouched +stat.sprintOneCm=Distance Sprinted +stat.fallOneCm=Distance Fallen +stat.swimOneCm=Distance Swum +stat.flyOneCm=Distance Flown +stat.climbOneCm=Distance Climbed +stat.diveOneCm=Distance Dove +stat.minecartOneCm=Distance by Minecart +stat.boatOneCm=Distance by Boat +stat.pigOneCm=Distance by Pig +stat.horseOneCm=Distance by Horse +stat.jump=Jumps +stat.drop=Items Dropped + +stat.damageDealt=Damage Dealt +stat.damageTaken=Damage Taken +stat.deaths=Number of Deaths +stat.mobKills=Mob Kills +stat.animalsBred=Animals Bred +stat.playerKills=Player Kills +stat.fishCaught=Fish Caught +stat.treasureFished=Treasure Fished +stat.junkFished=Junk Fished +stat.talkedToVillager=Talked to Villagers +stat.tradedWithVillager=Traded with Villagers + +stat.cakeSlicesEaten=Cake Slices Eaten +stat.cauldronFilled=Cauldrons Filled +stat.cauldronUsed=Water Taken from Cauldron +stat.armorCleaned=Armor Pieces Cleaned +stat.bannerCleaned=Banners Cleaned +stat.brewingstandInteraction=Interactions with Brewing Stand +stat.beaconInteraction=Interactions with Beacon +stat.dropperInspected=Droppers Searched +stat.hopperInspected=Hoppers Searched +stat.dispenserInspected=Dispensers Searched +stat.noteblockPlayed=Noteblocks played +stat.noteblockTuned=Noteblocks tuned +stat.flowerPotted=Plants potted +stat.trappedChestTriggered=Trapped Chests Triggered +stat.enderchestOpened=Ender Chests Opened +stat.itemEnchanted=Items Enchanted +stat.recordPlayed=Records Played +stat.furnaceInteraction=Interactions with Furnace +stat.workbenchInteraction=Interactions with Crafting Table +stat.chestOpened=Chests Opened + +stat.mineBlock=%1$s Mined +stat.craftItem=%1$s Crafted +stat.useItem=%1$s Used +stat.breakItem=%1$s Depleted + +achievement.get=Achievement get! + +achievement.taken=Taken! +achievement.unknown=??? + +achievement.requires=Requires '%1$s' +achievement.openInventory=Taking Inventory +achievement.openInventory.desc=Press '%1$s' to open your inventory. +achievement.mineWood=Getting Wood +achievement.mineWood.desc=Attack a tree until a block of wood pops out +achievement.buildWorkBench=Benchmarking +achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks +achievement.buildPickaxe=Time to Mine! +achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe +achievement.buildFurnace=Hot Topic +achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks +achievement.acquireIron=Acquire Hardware +achievement.acquireIron.desc=Smelt an iron ingot +achievement.buildHoe=Time to Farm! +achievement.buildHoe.desc=Use planks and sticks to make a hoe +achievement.makeBread=Bake Bread +achievement.makeBread.desc=Turn wheat into bread +achievement.bakeCake=The Lie +achievement.bakeCake.desc=Wheat, sugar, milk and eggs! +achievement.buildBetterPickaxe=Getting an Upgrade +achievement.buildBetterPickaxe.desc=Construct a better pickaxe +achievement.overpowered=Overpowered +achievement.overpowered.desc=Build a Notch apple +achievement.cookFish=Delicious Fish +achievement.cookFish.desc=Catch and cook fish! +achievement.onARail=On A Rail +achievement.onARail.desc=Travel by minecart at least 1 km from where you started +achievement.buildSword=Time to Strike! +achievement.buildSword.desc=Use planks and sticks to make a sword +achievement.killEnemy=Monster Hunter +achievement.killEnemy.desc=Attack and destroy a monster +achievement.killCow=Cow Tipper +achievement.killCow.desc=Harvest some leather +achievement.breedCow=Repopulation +achievement.breedCow.desc=Breed two cows with wheat +achievement.flyPig=When Pigs Fly +achievement.flyPig.desc=Fly a pig off a cliff +achievement.snipeSkeleton=Sniper Duel +achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters +achievement.diamonds=DIAMONDS! +achievement.diamonds.desc=Acquire diamonds with your iron tools +achievement.diamondsToYou=Diamonds to you! +achievement.diamondsToYou.desc=Throw diamonds at another player. +achievement.portal=We Need to Go Deeper +achievement.portal.desc=Build a portal to the Nether +achievement.ghast=Return to Sender +achievement.ghast.desc=Destroy a Ghast with a fireball +achievement.blazeRod=Into Fire +achievement.blazeRod.desc=Relieve a Blaze of its rod +achievement.potion=Local Brewery +achievement.potion.desc=Brew a potion +achievement.theEnd=The End? +achievement.theEnd.desc=Locate the End +achievement.theEnd2=The End. +achievement.theEnd2.desc=Defeat the Ender Dragon +achievement.spawnWither=The Beginning? +achievement.spawnWither.desc=Spawn the Wither +achievement.killWither=The Beginning. +achievement.killWither.desc=Kill the Wither +achievement.fullBeacon=Beaconator +achievement.fullBeacon.desc=Create a full beacon +achievement.exploreAllBiomes=Adventuring Time +achievement.exploreAllBiomes.desc=Discover all biomes +achievement.enchantments=Enchanter +achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table +achievement.overkill=Overkill +achievement.overkill.desc=Deal nine hearts of damage in a single hit +achievement.bookcase=Librarian +achievement.bookcase.desc=Build some bookshelves to improve your enchantment table + +commands.generic.exception=An unknown error occurred while attempting to perform this command +commands.generic.permission=You do not have permission to use this command +commands.generic.syntax=Invalid command syntax +commands.generic.player.notFound=That player cannot be found +commands.generic.entity.notFound=That entity cannot be found +commands.generic.entity.invalidUuid=The entity UUID provided is in an invalid format +commands.generic.entity.invalidType=Entity type '%s' is invalid +commands.generic.notFound=Unknown command. Try /help for a list of commands +commands.generic.parameter.invalid='%s' is not a valid parameter +commands.generic.num.invalid='%s' is not a valid number +commands.generic.boolean.invalid='%s' is not true or false +commands.generic.num.tooSmall=The number you have entered (%d) is too small, it must be at least %d +commands.generic.num.tooBig=The number you have entered (%d) is too big, it must be at most %d +commands.generic.double.tooSmall=The number you have entered (%.2f) is too small, it must be at least %.2f +commands.generic.double.tooBig=The number you have entered (%.2f) is too big, it must be at most %.2f +commands.generic.usage=Usage: %s + +commands.setidletimeout.usage=/setidletimeout +commands.setidletimeout.success=Successfully set the idle timeout to %d minutes. +commands.xp.failure.widthdrawXp=Cannot give player negative experience points +commands.xp.success=Given %d experience to %s +commands.xp.success.levels=Given %d levels to %s +commands.xp.success.negative.levels=Taken %d levels from %s +commands.xp.usage=/xp [player] OR /xp L [player] +commands.playsound.usage=/playsound [x] [y] [z] [volume] [pitch] [minimumVolume] +commands.playsound.success=Played sound '%s' to %s +commands.playsound.playerTooFar=Player %s is too far away to hear the sound +commands.give.usage=/give [amount] [data] [dataTag] +commands.give.item.notFound=There is no such item with name %d +commands.give.block.notFound=There is no such block with name %d +commands.give.success=Given %s * %d to %s +commands.give.tagError=Data tag parsing failed: %s +commands.replaceitem.usage=/replaceitem ... +commands.replaceitem.entity.usage=/replaceitem entity [amount] [data] [dataTag] +commands.replaceitem.block.usage=/replaceitem block [amount] [data] [dataTag] +commands.replaceitem.tagError=Data tag parsing failed: %s +commands.replaceitem.noContainer=Block at %d, %d, %d is not a container +commands.replaceitem.failed=Could not replace slot %d with %d * %s +commands.replaceitem.success=Replaced slot %d with %d * %s +commands.stats.usage=/stats ... +commands.stats.entity.usage=/stats entity +commands.stats.entity.set.usage=/stats entity set +commands.stats.entity.clear.usage=/stats entity clear +commands.stats.block.usage=/stats block ... +commands.stats.block.set.usage=/stats block set +commands.stats.block.clear.usage=/stats block clear +commands.stats.noCompatibleBlock=Block at %d, %d, %d can not track stats +commands.stats.failed=Invalid parameters +commands.stats.cleared=Cleared %s stats +commands.stats.success=Storing %s stats in %s on %s +commands.summon.usage=/summon [x] [y] [z] [dataTag] +commands.summon.success=Object successfully summoned +commands.summon.failed=Unable to summon object +commands.summon.tagError=Data tag parsing failed: %s +commands.summon.outOfWorld=Cannot summon the object out of the world +commands.testforblock.usage=/testforblock [dataValue] [dataTag] +commands.testforblock.failed.tile=The block at %d,%d,%d is %s (expected: %s). +commands.testforblock.failed.data=The block at %d,%d,%d had the data value of %s (expected: %s). +commands.testforblock.failed.nbt=The block at %d,%d,%d did not have the required NBT keys. +commands.testforblock.failed.tileEntity=The block at %d,%d,%d is not a tile entity and cannot support tag matching. +commands.testforblock.success=Successfully found the block at %d,%d,%d. +commands.testforblock.outOfWorld=Cannot test for block outside of the world +commands.setblock.usage=/setblock [dataValue] [oldBlockHandling] [dataTag] +commands.setblock.success=Block placed +commands.setblock.failed=Unable to place block +commands.setblock.tagError=Data tag parsing failed: %s +commands.setblock.outOfWorld=Cannot place block outside of the world +commands.setblock.notFound=There is no such block with ID/name %s +commands.setblock.noChange=The block couldn't be placed +commands.fill.usage=/fill [dataValue] [oldBlockHandling] [dataTag] +commands.fill.outOfWorld=Cannot place blocks outside of the world +commands.fill.tagError=Data tag parsing failed: %s +commands.fill.success=%d blocks filled +commands.fill.failed=No blocks filled +commands.fill.tooManyBlocks=Too many blocks in the specified area (%d > %d) +commands.clone.usage=/clone [maskMode] [cloneMode] +commands.clone.outOfWorld=Cannot access blocks outside of the world +commands.clone.noOverlap=Source and destination can not overlap +commands.clone.success=%d blocks cloned +commands.clone.failed=No blocks cloned +commands.clone.tooManyBlocks=Too many blocks in the specified area (%d > %d) +commands.compare.usage=/testforblocks [mode] +commands.compare.outOfWorld=Cannot access blocks outside of the world +commands.compare.failed=Source and destination are not identical +commands.compare.success=%d blocks compared +commands.compare.tooManyBlocks=Too many blocks in the specified area (%d > %d) +commands.blockdata.usage=/blockdata +commands.blockdata.success=Block data updated to: %s +commands.blockdata.tagError=Data tag parsing failed: %s +commands.blockdata.outOfWorld=Cannot change block outside of the world +commands.blockdata.notValid=The target block is not a data holder block +commands.blockdata.failed=The data tag did not change: %s +commands.entitydata.usage=/entitydata +commands.entitydata.success=Entity data updated to: %s +commands.entitydata.tagError=Data tag parsing failed: %s +commands.entitydata.noPlayers=%s is a player and cannot be changed +commands.entitydata.failed=The data tag did not change: %s +commands.effect.usage=/effect [seconds] [amplifier] [hideParticles] OR /effect clear +commands.effect.notFound=There is no such mob effect with ID %d +commands.effect.success=Given %1$s (ID %2$d) * %3$d to %4$s for %5$d seconds +commands.effect.success.removed=Took %1$s from %2$s +commands.effect.success.removed.all=Took all effects from %s +commands.effect.failure.notActive=Couldn't take %1$s from %2$s as they do not have the effect +commands.effect.failure.notActive.all=Couldn't take any effects from %s as they do not have any +commands.enchant.usage=/enchant [level] +commands.enchant.notFound=There is no such enchantment with ID %d +commands.enchant.noItem=The target doesn't hold an item +commands.enchant.cantEnchant=The selected enchantment can't be added to the target item +commands.enchant.cantCombine=%1$s can't be combined with %2$s +commands.enchant.success=Enchanting succeeded +commands.particle.usage=/particle [count] [mode] +commands.particle.success=Playing effect %s for %d times +commands.particle.notFound=Unknown effect name (%s) +commands.clear.usage=/clear [player] [item] [data] [maxCount] [dataTag] +commands.clear.success=Cleared the inventory of %s, removing %d items +commands.clear.testing=%s has %d items that match the criteria +commands.clear.failure=Could not clear the inventory of %s, no items to remove +commands.clear.tagError=Data tag parsing failed: %s +commands.downfall.usage=/toggledownfall +commands.downfall.success=Toggled downfall +commands.time.usage=/time +commands.time.added=Added %d to the time +commands.time.set=Set the time to %d +commands.time.query=Time is %d +commands.players.usage=/list +commands.players.list=There are %d/%d players online: +commands.banlist.ips=There are %d total banned IP addresses: +commands.banlist.players=There are %d total banned players: +commands.banlist.usage=/banlist [ips|players] +commands.kill.usage=/kill [player|entity] +commands.kill.successful=Killed %s +commands.kick.success=Kicked %s from the game +commands.kick.success.reason=Kicked %s from the game: '%s' +commands.kick.usage=/kick [reason ...] +commands.op.success=Opped %s +commands.op.failed=Could not op %s +commands.op.usage=/op +commands.deop.success=De-opped %s +commands.deop.failed=Could not de-op %s +commands.deop.usage=/deop +commands.say.usage=/say +commands.ban.success=Banned player %s +commands.ban.failed=Could not ban player %s +commands.ban.usage=/ban [reason ...] +commands.unban.success=Unbanned player %s +commands.unban.failed=Could not unban player %s +commands.unban.usage=/pardon +commands.banip.invalid=You have entered an invalid IP address or a player that is not online +commands.banip.success=Banned IP address %s +commands.banip.success.players=Banned IP address %s belonging to %s +commands.banip.usage=/ban-ip [reason ...] +commands.unbanip.invalid=You have entered an invalid IP address +commands.unbanip.success=Unbanned IP address %s +commands.unbanip.usage=/pardon-ip
+commands.save.usage=/save-all +commands.save-on.alreadyOn=Saving is already turned on. +commands.save-on.usage=/save-on +commands.save-off.alreadyOff=Saving is already turned off. +commands.save-off.usage=/save-off +commands.save.enabled=Turned on world auto-saving +commands.save.disabled=Turned off world auto-saving +commands.save.start=Saving... +commands.save.success=Saved the world +commands.save.failed=Saving failed: %s +commands.stop.usage=/stop +commands.stop.start=Stopping the server +commands.tp.success=Teleported %s to %s +commands.tp.success.coordinates=Teleported %s to %s, %s, %s +commands.tp.usage=/tp [target player] OR /tp [target player] [ ] +commands.tp.notSameDimension=Unable to teleport because players are not in the same dimension +commands.whitelist.list=There are %d (out of %d seen) whitelisted players: +commands.whitelist.enabled=Turned on the whitelist +commands.whitelist.disabled=Turned off the whitelist +commands.whitelist.reloaded=Reloaded the whitelist +commands.whitelist.add.success=Added %s to the whitelist +commands.whitelist.add.failed=Could not add %s to the whitelist +commands.whitelist.add.usage=/whitelist add +commands.whitelist.remove.success=Removed %s from the whitelist +commands.whitelist.remove.failed=Could not remove %s from the whitelist +commands.whitelist.remove.usage=/whitelist remove +commands.whitelist.usage=/whitelist +commands.scoreboard.usage=/scoreboard ... +commands.scoreboard.noMultiWildcard=Only one user wildcard allowed +commands.scoreboard.allMatchesFailed=All matches failed +commands.scoreboard.teamNotFound=No team was found by the name '%s' +commands.scoreboard.objectiveNotFound=No objective was found by the name '%s' +commands.scoreboard.objectiveReadOnly=The objective '%s' is read-only and cannot be set +commands.scoreboard.objectives.usage=/scoreboard objectives ... +commands.scoreboard.objectives.setdisplay.usage=/scoreboard objectives setdisplay [objective] +commands.scoreboard.objectives.setdisplay.invalidSlot=No such display slot '%s' +commands.scoreboard.objectives.setdisplay.successCleared=Cleared objective display slot '%s' +commands.scoreboard.objectives.setdisplay.successSet=Set the display objective in slot '%s' to '%s' +commands.scoreboard.objectives.add.usage=/scoreboard objectives add [display name ...] +commands.scoreboard.objectives.add.wrongType=Invalid objective criteria type '%s' +commands.scoreboard.objectives.add.alreadyExists=An objective with the name '%s' already exists +commands.scoreboard.objectives.add.tooLong=The name '%s' is too long for an objective, it can be at most %d characters long +commands.scoreboard.objectives.add.displayTooLong=The display name '%s' is too long for an objective, it can be at most %d characters long +commands.scoreboard.objectives.add.success=Added new objective '%s' successfully +commands.scoreboard.objectives.remove.usage=/scoreboard objectives remove +commands.scoreboard.objectives.remove.success=Removed objective '%s' successfully +commands.scoreboard.objectives.list.count=Showing %d objective(s) on scoreboard: +commands.scoreboard.objectives.list.entry=- %s: displays as '%s' and is type '%s' +commands.scoreboard.objectives.list.empty=There are no objectives on the scoreboard +commands.scoreboard.players.usage=/scoreboard players ... +commands.scoreboard.players.name.tooLong=The name '%s' is too long for a player, it can be at most %d characters long +commands.scoreboard.players.set.success=Set score of %s for player %s to %d +commands.scoreboard.players.set.tagMismatch=The dataTag does not match for %s +commands.scoreboard.players.set.tagError=Could not parse dataTag, reason: %s +commands.scoreboard.players.set.usage=/scoreboard players set [dataTag] +commands.scoreboard.players.add.usage=/scoreboard players add [dataTag] +commands.scoreboard.players.remove.usage=/scoreboard players remove [dataTag] +commands.scoreboard.players.reset.usage=/scoreboard players reset [objective] +commands.scoreboard.players.reset.success=Reset scores of player %s +commands.scoreboard.players.resetscore.success=Reset score %s of player %s +commands.scoreboard.players.list.usage=/scoreboard players list [name] +commands.scoreboard.players.list.count=Showing %d tracked players on the scoreboard: +commands.scoreboard.players.list.empty=There are no tracked players on the scoreboard +commands.scoreboard.players.list.player.count=Showing %d tracked objective(s) for %s: +commands.scoreboard.players.list.player.entry=- %2$s: %1$d (%3$s) +commands.scoreboard.players.list.player.empty=Player %s has no scores recorded +commands.scoreboard.players.enable.usage=/scoreboard players enable +commands.scoreboard.players.enable.success=Enabled trigger %s for %s +commands.scoreboard.players.enable.noTrigger=Objective %s is not a trigger +commands.scoreboard.players.test.usage=/scoreboard players test +commands.scoreboard.players.test.notFound=No %s score for %s found +commands.scoreboard.players.test.failed=Score %d is NOT in range %d to %d +commands.scoreboard.players.test.success=Score %d is in range %d to %d +commands.scoreboard.players.operation.usage=/scoreboard players operation +commands.scoreboard.players.operation.notFound=No %s score for %s found +commands.scoreboard.players.operation.invalidOperation=Invalid operation %s +commands.scoreboard.players.operation.success=Operation applied successfully +commands.scoreboard.teams.usage=/scoreboard teams ... +commands.scoreboard.teams.add.usage=/scoreboard teams add [display name ...] +commands.scoreboard.teams.add.alreadyExists=A team with the name '%s' already exists +commands.scoreboard.teams.add.tooLong=The name '%s' is too long for a team, it can be at most %d characters long +commands.scoreboard.teams.add.displayTooLong=The display name '%s' is too long for a team, it can be at most %d characters long +commands.scoreboard.teams.add.success=Added new team '%s' successfully +commands.scoreboard.teams.list.usage=/scoreboard teams list [name] +commands.scoreboard.teams.list.count=Showing %d teams on the scoreboard: +commands.scoreboard.teams.list.entry=- %1$s: '%2$s' has %3$d players +commands.scoreboard.teams.list.empty=There are no teams registered on the scoreboard +commands.scoreboard.teams.list.player.count=Showing %d player(s) in team %s: +commands.scoreboard.teams.list.player.entry=- %2$s: %1$d (%3$s) +commands.scoreboard.teams.list.player.empty=Team %s has no players +commands.scoreboard.teams.empty.usage=/scoreboard teams empty +commands.scoreboard.teams.empty.alreadyEmpty=Team %s is already empty, cannot remove nonexistant players +commands.scoreboard.teams.empty.success=Removed all %d player(s) from team %s +commands.scoreboard.teams.remove.usage=/scoreboard teams remove +commands.scoreboard.teams.remove.success=Removed team %s +commands.scoreboard.teams.join.usage=/scoreboard teams join [player] +commands.scoreboard.teams.join.success=Added %d player(s) to team %s: %s +commands.scoreboard.teams.join.failure=Could not add %d player(s) to team %s: %s +commands.scoreboard.teams.leave.usage=/scoreboard teams leave [player] +commands.scoreboard.teams.leave.success=Removed %d player(s) from their teams: %s +commands.scoreboard.teams.leave.failure=Could not remove %d player(s) from their teams: %s +commands.scoreboard.teams.leave.noTeam=You are not in a team +commands.scoreboard.teams.option.usage=/scoreboard teams option +commands.scoreboard.teams.option.noValue=Valid values for option %s are: %s +commands.scoreboard.teams.option.success=Set option %s for team %s to %s +commands.execute.usage=/execute OR /execute detect +commands.execute.allInvocationsFailed=All invocations failed: '%s' +commands.execute.failed=Failed to execute '%s' as %s +commands.gamemode.success.self=Set own game mode to %s +commands.gamemode.success.other=Set %s's game mode to %s +commands.gamemode.usage=/gamemode [player] +commands.defaultgamemode.usage=/defaultgamemode +commands.defaultgamemode.success=The world's default game mode is now %s +commands.me.usage=/me +commands.help.header=--- Showing help page %d of %d (/help ) --- +commands.help.footer=Tip: Use the key while typing a command to auto-complete the command or its arguments +commands.help.usage=/help [page|command name] +commands.trigger.usage=/trigger +commands.trigger.invalidObjective=Invalid trigger name %s +commands.trigger.invalidMode=Invalid trigger mode %s +commands.trigger.disabled=Trigger %s is not enabled +commands.trigger.invalidPlayer=Only players can use the /trigger command +commands.trigger.success=Trigger %s changed with %s %s +commands.publish.usage=/publish +commands.publish.started=Local game hosted on port %s +commands.publish.failed=Unable to host local game +commands.debug.start=Started debug profiling +commands.debug.stop=Stopped debug profiling after %.2f seconds (%d ticks) +commands.debug.notStarted=Can't stop profiling when we haven't started yet! +commands.debug.usage=/debug +commands.chunkinfo.usage=/chunkinfo [ ] +commands.chunkinfo.location=Chunk location: (%d, %d, %d) +commands.chunkinfo.noChunk=No chunk found at chunk position %d, %d, %d +commands.chunkinfo.notEmpty=Chunk is not empty. +commands.chunkinfo.empty=Chunk is empty. +commands.chunkinfo.notCompiled=Chunk is not compiled. +commands.chunkinfo.compiled=Chunk is compiled. +commands.chunkinfo.hasNoRenderableLayers=Chunk has no renderable layers. +commands.chunkinfo.hasLayers=Chunk has layers: %s +commands.chunkinfo.isEmpty=Chunk has empty layers: %s +commands.chunkinfo.vertices=%s layer's buffer contains %d vertices +commands.chunkinfo.data=First 64 vertices are: %s +commands.tellraw.usage=/tellraw +commands.tellraw.jsonException=Invalid json: %s +commands.message.usage=/tell +commands.message.sameTarget=You can't send a private message to yourself! +commands.message.display.outgoing=You whisper to %s: %s +commands.message.display.incoming=%s whispers to you: %s +commands.difficulty.usage=/difficulty +commands.difficulty.success=Set game difficulty to %s +commands.spawnpoint.usage=/spawnpoint [player] [ ] +commands.spawnpoint.success=Set %s's spawn point to (%d, %d, %d) +commands.setworldspawn.usage=/setworldspawn [ ] +commands.setworldspawn.success=Set the world spawn point to (%d, %d, %d) +commands.gamerule.usage=/gamerule [value] +commands.gamerule.success=Game rule has been updated +commands.gamerule.norule=No game rule called '%s' is available +commands.gamerule.nopermission=Only server owners can change '%s' +commands.weather.usage=/weather [duration in seconds] +commands.weather.clear=Changing to clear weather +commands.weather.rain=Changing to rainy weather +commands.weather.thunder=Changing to rain and thunder +commands.testfor.usage=/testfor [dataTag] +commands.testfor.failure=%s did not match the required data structure +commands.testfor.success=Found %s +commands.testfor.tagError=Data tag parsing failed: %s +commands.seed.usage=/seed +commands.seed.success=Seed: %s +commands.spreadplayers.usage=/spreadplayers +commands.spreadplayers.spreading.teams=Spreading %s teams %s blocks around %s,%s (min %s blocks apart) +commands.spreadplayers.spreading.players=Spreading %s players %s blocks around %s,%s (min %s blocks apart) +commands.spreadplayers.success.teams=Successfully spread %s teams around %s,%s +commands.spreadplayers.success.players=Successfully spread %s players around %s,%s +commands.spreadplayers.info.teams=(Average distance between teams is %s blocks apart after %s iterations) +commands.spreadplayers.info.players=(Average distance between players is %s blocks apart after %s iterations) +commands.spreadplayers.failure.teams=Could not spread %s teams around %s,%s (too many players for space - try using spread of at most %s) +commands.spreadplayers.failure.players=Could not spread %s players around %s,%s (too many players for space - try using spread of at most %s) +commands.achievement.usage=/achievement [player] +commands.achievement.unknownAchievement=Unknown achievement or statistic '%s' +commands.achievement.alreadyHave=Player %s already has achievement %s +commands.achievement.dontHave=Player %s doesn't have achievement %s +commands.achievement.give.success.all=Successfully given all achievements to %s +commands.achievement.give.success.one=Successfully given %s the stat %s +commands.achievement.take.success.all=Successfully taken all achievements from %s +commands.achievement.take.success.one=Successfully taken the stat %s from %s +commands.achievement.statTooLow=Player %s does not have the stat %s +commands.worldborder.usage=/worldborder ... +commands.worldborder.add.usage=/worldborder add [timeInSeconds] +commands.worldborder.set.usage=/worldborder set [timeInSeconds] +commands.worldborder.set.success=Set world border to %s blocks wide (from %s blocks) +commands.worldborder.get.success=World border is currently %s blocks wide +commands.worldborder.setSlowly.shrink.success=Shrinking world border to %s blocks wide (down from %s blocks) over %s seconds +commands.worldborder.setSlowly.grow.success=Growing world border to %s blocks wide (up from %s blocks) over %s seconds +commands.worldborder.center.usage=/worldborder center +commands.worldborder.center.success=Set world border center to %s,%s +commands.worldborder.damage.usage=/worldborder damage +commands.worldborder.damage.buffer.usage=/worldborder damage buffer +commands.worldborder.damage.buffer.success=Set world border damage buffer to %s blocks (from %s blocks) +commands.worldborder.damage.amount.usage=/worldborder damage amount +commands.worldborder.damage.amount.success=Set world border damage amount to %s per block (from %s per block) +commands.worldborder.warning.usage=/worldborder warning +commands.worldborder.warning.time.usage=/worldborder warning time +commands.worldborder.warning.time.success=Set world border warning to %s seconds away (from %s seconds) +commands.worldborder.warning.distance.usage=/worldborder warning distance +commands.worldborder.warning.distance.success=Set world border warning to %s blocks away (from %s blocks) +commands.title.usage=/title ... +commands.title.usage.title=/title title|subtitle +commands.title.usage.clear=/title clear|reset +commands.title.usage.times=/title times +commands.title.success=Title command successfully executed + +itemGroup.buildingBlocks=Building Blocks +itemGroup.decorations=Decoration Blocks +itemGroup.redstone=Redstone +itemGroup.transportation=Transportation +itemGroup.misc=Miscellaneous +itemGroup.search=Search Items +itemGroup.food=Foodstuffs +itemGroup.tools=Tools +itemGroup.combat=Combat +itemGroup.brewing=Brewing +itemGroup.materials=Materials +itemGroup.inventory=Survival Inventory + +inventory.binSlot=Destroy Item + +advMode.setCommand=Set Console Command for Block +advMode.setCommand.success=Command set: %s +advMode.command=Console Command +advMode.nearestPlayer=Use "@p" to target nearest player +advMode.randomPlayer=Use "@r" to target random player +advMode.allPlayers=Use "@a" to target all players +advMode.allEntities=Use "@e" to target all entities +advMode.previousOutput=Previous Output + +advMode.notEnabled=Command blocks are not enabled on this server +advMode.notAllowed=Must be an opped player in creative mode + +mount.onboard=Press %1$s to dismount + +build.tooHigh=Height limit for building is %s blocks + +item.modifiers.mainhand=When in main hand: +item.modifiers.offhand=When in off hand: +item.modifiers.feet=When on feet: +item.modifiers.legs=When on legs: +item.modifiers.chest=When on body: +item.modifiers.head=When on head: + +attribute.modifier.plus.0=+%d %s +attribute.modifier.plus.1=+%d%% %s +attribute.modifier.plus.2=+%d%% %s +attribute.modifier.take.0=-%d %s +attribute.modifier.take.1=-%d%% %s +attribute.modifier.take.2=-%d%% %s +attribute.modifier.equals.0=%d %s +attribute.modifier.equals.1=%d%% %s +attribute.modifier.equals.2=%d%% %s + +attribute.name.horse.jumpStrength=Horse Jump Strength +attribute.name.zombie.spawnReinforcements=Zombie Reinforcements +attribute.name.generic.maxHealth=Max Health +attribute.name.generic.followRange=Mob Follow Range +attribute.name.generic.knockbackResistance=Knockback Resistance +attribute.name.generic.movementSpeed=Speed +attribute.name.generic.attackDamage=Attack Damage +attribute.name.generic.attackSpeed=Attack Speed +attribute.name.generic.luck=Luck +attribute.name.generic.armor=Armor +attribute.name.generic.armorToughness=Armor Toughness + +screenshot.success=Saved screenshot as %s +screenshot.failure=Couldn't save screenshot: %s + +stream.user.mode.moderator=Moderator +stream.user.mode.moderator.self=Moderator on your channel +stream.user.mode.moderator.other=Moderator on %s's channel +stream.user.mode.broadcaster=Broadcaster +stream.user.mode.broadcaster.self=Broadcaster (You!) +stream.user.mode.broadcaster.other=Broadcaster +stream.user.mode.administrator=Twitch Administrator +stream.user.mode.staff=Twitch Staff +stream.user.mode.banned=Banned +stream.user.mode.banned.self=Banned on your channel +stream.user.mode.banned.other=Banned on %s's channel +stream.user.subscription.subscriber=Subscriber +stream.user.subscription.subscriber.self=Subscriber to your channel +stream.user.subscription.subscriber.other=Subscriber to %s's channel +stream.user.subscription.turbo=Twitch Turbo + +stream.unavailable.title=Twitch Broadcasting Unavailable +stream.unavailable.report_to_mojang=Report to Mojang + +stream.confirm_start=Are you sure you want to start broadcasting? + +stream.unavailable.account_not_bound=Before you can broadcast Minecraft through Twitch, you will need to link your Twitch account on mojang.com. Would you like to do that now? +stream.unavailable.account_not_bound.okay=Link Accounts +stream.unavailable.account_not_migrated=Before you can broadcast Minecraft through Twitch, you will need to migrate your Minecraft account to a Mojang account. Would you like to do that now? +stream.unavailable.account_not_migrated.okay=Migrate Account +stream.unavailable.failed_auth=Authentication to Twitch failed. Please go to mojang.com and rebind your Twitch account. +stream.unavailable.failed_auth.okay=Rebind Accounts +stream.unavailable.failed_auth_error=Unable to authenticate to Twitch. Please try again later. +stream.unavailable.initialization_failure=Unable to initialize the Twitch SDK. +stream.unavailable.initialization_failure.extra=(Reason: %s) +stream.unavailable.library_arch_mismatch=The custom java version used to launch Minecraft has a different architecture than the one used to run the launcher. Please make sure these are the same, either 32-bit or 64-bit for both. +stream.unavailable.library_failure=Unable to load the libraries needed for the integrated Twitch broadcasting service. +stream.unavailable.no_fbo=Your video card needs to support at least OpenGL version 3.0 or support Framebuffer Objects via an extension to use the integrated Twitch broadcasting. +stream.unavailable.no_fbo.version=You are currently using: %s +stream.unavailable.no_fbo.blend=Separate blending support via EXT is: %s +stream.unavailable.no_fbo.arb=Framebuffer object support via ARB is: %s +stream.unavailable.no_fbo.ext=Framebuffer object support via EXT is: %s +stream.unavailable.not_supported.windows=Unfortunately the integrated Twitch broadcasting requires a newer version of Windows than you are on. You must have at least Windows Vista or newer. +stream.unavailable.not_supported.mac=Unfortunately the integrated Twitch broadcasting on Mac requires a version of OSX newer than the one you are on. You must use 10.7 (Mac OS X Lion) or newer to be able to use this service. Would you like to visit apple.com to learn about upgrading? +stream.unavailable.not_supported.mac.okay=Upgrade +stream.unavailable.not_supported.other=Unfortunately the integrated Twitch broadcasting service requires Windows (Vista or newer) or Mac OS X (10.7/Lion or newer) +stream.unavailable.unknown=Unfortunately you cannot broadcast to Twitch at this time. And we don't know why :'( +stream.unavailable.unknown.chat=Could not start stream: %s + +stream.unavailable.soundflower.chat=Soundflower is required to be able to stream on Mac. %s +stream.unavailable.soundflower.chat.link=Please click here to install it. + +stream.userinfo.chatTooltip=Click to manage user +stream.userinfo.timeout=Timeout +stream.userinfo.ban=Ban +stream.userinfo.unban=Unban +stream.userinfo.mod=Promote to Moderator +stream.userinfo.unmod=Demote from Moderator + +item.banner.black.name=Black Banner +item.banner.red.name=Red Banner +item.banner.green.name=Green Banner +item.banner.brown.name=Brown Banner +item.banner.blue.name=Blue Banner +item.banner.purple.name=Purple Banner +item.banner.cyan.name=Cyan Banner +item.banner.silver.name=Light Gray Banner +item.banner.gray.name=Gray Banner +item.banner.pink.name=Pink Banner +item.banner.lime.name=Lime Banner +item.banner.yellow.name=Yellow Banner +item.banner.lightBlue.name=Light Blue Banner +item.banner.magenta.name=Magenta Banner +item.banner.orange.name=Orange Banner +item.banner.white.name=White Banner + +item.banner.square_bottom_left.black=Black Base Dexter Canton +item.banner.square_bottom_left.red=Red Base Dexter Canton +item.banner.square_bottom_left.green=Green Base Dexter Canton +item.banner.square_bottom_left.brown=Brown Base Dexter Canton +item.banner.square_bottom_left.blue=Blue Base Dexter Canton +item.banner.square_bottom_left.purple=Purple Base Dexter Canton +item.banner.square_bottom_left.cyan=Cyan Base Dexter Canton +item.banner.square_bottom_left.silver=Light Gray Base Dexter Canton +item.banner.square_bottom_left.gray=Gray Base Dexter Canton +item.banner.square_bottom_left.pink=Pink Base Dexter Canton +item.banner.square_bottom_left.lime=Lime Base Dexter Canton +item.banner.square_bottom_left.yellow=Yellow Base Dexter Canton +item.banner.square_bottom_left.lightBlue=Light Blue Base Dexter Canton +item.banner.square_bottom_left.magenta=Magenta Base Dexter Canton +item.banner.square_bottom_left.orange=Orange Base Dexter Canton +item.banner.square_bottom_left.white=White Base Dexter Canton + +item.banner.square_bottom_right.black=Black Base Sinister Canton +item.banner.square_bottom_right.red=Red Base Sinister Canton +item.banner.square_bottom_right.green=Green Base Sinister Canton +item.banner.square_bottom_right.brown=Brown Base Sinister Canton +item.banner.square_bottom_right.blue=Blue Base Sinister Canton +item.banner.square_bottom_right.purple=Purple Base Sinister Canton +item.banner.square_bottom_right.cyan=Cyan Base Sinister Canton +item.banner.square_bottom_right.silver=Light Gray Base Sinister Canton +item.banner.square_bottom_right.gray=Gray Base Sinister Canton +item.banner.square_bottom_right.pink=Pink Base Sinister Canton +item.banner.square_bottom_right.lime=Lime Base Sinister Canton +item.banner.square_bottom_right.yellow=Yellow Base Sinister Canton +item.banner.square_bottom_right.lightBlue=Light Blue Base Sinister Canton +item.banner.square_bottom_right.magenta=Magenta Base Sinister Canton +item.banner.square_bottom_right.orange=Orange Base Sinister Canton +item.banner.square_bottom_right.white=White Base Sinister Canton + +item.banner.square_top_left.black=Black Chief Dexter Canton +item.banner.square_top_left.red=Red Chief Dexter Canton +item.banner.square_top_left.green=Green Chief Dexter Canton +item.banner.square_top_left.brown=Brown Chief Dexter Canton +item.banner.square_top_left.blue=Blue Chief Dexter Canton +item.banner.square_top_left.purple=Purple Chief Dexter Canton +item.banner.square_top_left.cyan=Cyan Chief Dexter Canton +item.banner.square_top_left.silver=Light Gray Chief Dexter Canton +item.banner.square_top_left.gray=Gray Chief Dexter Canton +item.banner.square_top_left.pink=Pink Chief Dexter Canton +item.banner.square_top_left.lime=Lime Chief Dexter Canton +item.banner.square_top_left.yellow=Yellow Chief Dexter Canton +item.banner.square_top_left.lightBlue=Light Blue Chief Dexter Canton +item.banner.square_top_left.magenta=Magenta Chief Dexter Canton +item.banner.square_top_left.orange=Orange Chief Dexter Canton +item.banner.square_top_left.white=White Chief Dexter Canton + +item.banner.square_top_right.black=Black Chief Sinister Canton +item.banner.square_top_right.red=Red Chief Sinister Canton +item.banner.square_top_right.green=Green Chief Sinister Canton +item.banner.square_top_right.brown=Brown Chief Sinister Canton +item.banner.square_top_right.blue=Blue Chief Sinister Canton +item.banner.square_top_right.purple=Purple Chief Sinister Canton +item.banner.square_top_right.cyan=Cyan Chief Sinister Canton +item.banner.square_top_right.silver=Light Gray Chief Sinister Canton +item.banner.square_top_right.gray=Gray Chief Sinister Canton +item.banner.square_top_right.pink=Pink Chief Sinister Canton +item.banner.square_top_right.lime=Lime Chief Sinister Canton +item.banner.square_top_right.yellow=Yellow Chief Sinister Canton +item.banner.square_top_right.lightBlue=Light Blue Chief Sinister Canton +item.banner.square_top_right.magenta=Magenta Chief Sinister Canton +item.banner.square_top_right.orange=Orange Chief Sinister Canton +item.banner.square_top_right.white=White Chief Sinister Canton + +item.banner.stripe_bottom.black=Black Base Fess +item.banner.stripe_bottom.red=Red Base Fess +item.banner.stripe_bottom.green=Green Base Fess +item.banner.stripe_bottom.brown=Brown Base Fess +item.banner.stripe_bottom.blue=Blue Base Fess +item.banner.stripe_bottom.purple=Purple Base Fess +item.banner.stripe_bottom.cyan=Cyan Base Fess +item.banner.stripe_bottom.silver=Light Gray Base Fess +item.banner.stripe_bottom.gray=Gray Base Fess +item.banner.stripe_bottom.pink=Pink Base Fess +item.banner.stripe_bottom.lime=Lime Base Fess +item.banner.stripe_bottom.yellow=Yellow Base Fess +item.banner.stripe_bottom.lightBlue=Light Blue Base Fess +item.banner.stripe_bottom.magenta=Magenta Base Fess +item.banner.stripe_bottom.orange=Orange Base Fess +item.banner.stripe_bottom.white=White Base Fess + +item.banner.stripe_top.black=Black Chief Fess +item.banner.stripe_top.red=Red Chief Fess +item.banner.stripe_top.green=Green Chief Fess +item.banner.stripe_top.brown=Brown Chief Fess +item.banner.stripe_top.blue=Blue Chief Fess +item.banner.stripe_top.purple=Purple Chief Fess +item.banner.stripe_top.cyan=Cyan Chief Fess +item.banner.stripe_top.silver=Light Gray Chief Fess +item.banner.stripe_top.gray=Gray Chief Fess +item.banner.stripe_top.pink=Pink Chief Fess +item.banner.stripe_top.lime=Lime Chief Fess +item.banner.stripe_top.yellow=Yellow Chief Fess +item.banner.stripe_top.lightBlue=Light Blue Chief Fess +item.banner.stripe_top.magenta=Magenta Chief Fess +item.banner.stripe_top.orange=Orange Chief Fess +item.banner.stripe_top.white=White Chief Fess + +item.banner.stripe_left.black=Black Pale Dexter +item.banner.stripe_left.red=Red Pale Dexter +item.banner.stripe_left.green=Green Pale Dexter +item.banner.stripe_left.brown=Brown Pale Dexter +item.banner.stripe_left.blue=Blue Pale Dexter +item.banner.stripe_left.purple=Purple Pale Dexter +item.banner.stripe_left.cyan=Cyan Pale Dexter +item.banner.stripe_left.silver=Light Gray Pale Dexter +item.banner.stripe_left.gray=Gray Pale Dexter +item.banner.stripe_left.pink=Pink Pale Dexter +item.banner.stripe_left.lime=Lime Pale Dexter +item.banner.stripe_left.yellow=Yellow Pale Dexter +item.banner.stripe_left.lightBlue=Light Blue Pale Dexter +item.banner.stripe_left.magenta=Magenta Pale Dexter +item.banner.stripe_left.orange=Orange Pale Dexter +item.banner.stripe_left.white=White Pale Dexter + +item.banner.stripe_right.black=Black Pale Sinister +item.banner.stripe_right.red=Red Pale Sinister +item.banner.stripe_right.green=Green Pale Sinister +item.banner.stripe_right.brown=Brown Pale Sinister +item.banner.stripe_right.blue=Blue Pale Sinister +item.banner.stripe_right.purple=Purple Pale Sinister +item.banner.stripe_right.cyan=Cyan Pale Sinister +item.banner.stripe_right.silver=Light Gray Pale Sinister +item.banner.stripe_right.gray=Gray Pale Sinister +item.banner.stripe_right.pink=Pink Pale Sinister +item.banner.stripe_right.lime=Lime Pale Sinister +item.banner.stripe_right.yellow=Yellow Pale Sinister +item.banner.stripe_right.lightBlue=Light Blue Pale Sinister +item.banner.stripe_right.magenta=Magenta Pale Sinister +item.banner.stripe_right.orange=Orange Pale Sinister +item.banner.stripe_right.white=White Pale Sinister + +item.banner.stripe_center.black=Black Pale +item.banner.stripe_center.red=Red Pale +item.banner.stripe_center.green=Green Pale +item.banner.stripe_center.brown=Brown Pale +item.banner.stripe_center.blue=Blue Pale +item.banner.stripe_center.purple=Purple Pale +item.banner.stripe_center.cyan=Cyan Pale +item.banner.stripe_center.silver=Light Gray Pale +item.banner.stripe_center.gray=Gray Pale +item.banner.stripe_center.pink=Pink Pale +item.banner.stripe_center.lime=Lime Pale +item.banner.stripe_center.yellow=Yellow Pale +item.banner.stripe_center.lightBlue=Light Blue Pale +item.banner.stripe_center.magenta=Magenta Pale +item.banner.stripe_center.orange=Orange Pale +item.banner.stripe_center.white=White Pale + +item.banner.stripe_middle.black=Black Fess +item.banner.stripe_middle.red=Red Fess +item.banner.stripe_middle.green=Green Fess +item.banner.stripe_middle.brown=Brown Fess +item.banner.stripe_middle.blue=Blue Fess +item.banner.stripe_middle.purple=Purple Fess +item.banner.stripe_middle.cyan=Cyan Fess +item.banner.stripe_middle.silver=Light Gray Fess +item.banner.stripe_middle.gray=Gray Fess +item.banner.stripe_middle.pink=Pink Fess +item.banner.stripe_middle.lime=Lime Fess +item.banner.stripe_middle.yellow=Yellow Fess +item.banner.stripe_middle.lightBlue=Light Blue Fess +item.banner.stripe_middle.magenta=Magenta Fess +item.banner.stripe_middle.orange=Orange Fess +item.banner.stripe_middle.white=White Fess + +item.banner.stripe_downright.black=Black Bend +item.banner.stripe_downright.red=Red Bend +item.banner.stripe_downright.green=Green Bend +item.banner.stripe_downright.brown=Brown Bend +item.banner.stripe_downright.blue=Blue Bend +item.banner.stripe_downright.purple=Purple Bend +item.banner.stripe_downright.cyan=Cyan Bend +item.banner.stripe_downright.silver=Light Gray Bend +item.banner.stripe_downright.gray=Gray Bend +item.banner.stripe_downright.pink=Pink Bend +item.banner.stripe_downright.lime=Lime Bend +item.banner.stripe_downright.yellow=Yellow Bend +item.banner.stripe_downright.lightBlue=Light Blue Bend +item.banner.stripe_downright.magenta=Magenta Bend +item.banner.stripe_downright.orange=Orange Bend +item.banner.stripe_downright.white=White Bend + +item.banner.stripe_downleft.black=Black Bend Sinister +item.banner.stripe_downleft.red=Red Bend Sinister +item.banner.stripe_downleft.green=Green Bend Sinister +item.banner.stripe_downleft.brown=Brown Bend Sinister +item.banner.stripe_downleft.blue=Blue Bend Sinister +item.banner.stripe_downleft.purple=Purple Bend Sinister +item.banner.stripe_downleft.cyan=Cyan Bend Sinister +item.banner.stripe_downleft.silver=Light Gray Bend Sinister +item.banner.stripe_downleft.gray=Gray Bend Sinister +item.banner.stripe_downleft.pink=Pink Bend Sinister +item.banner.stripe_downleft.lime=Lime Bend Sinister +item.banner.stripe_downleft.yellow=Yellow Bend Sinister +item.banner.stripe_downleft.lightBlue=Light Blue Bend Sinister +item.banner.stripe_downleft.magenta=Magenta Bend Sinister +item.banner.stripe_downleft.orange=Orange Bend Sinister +item.banner.stripe_downleft.white=White Bend Sinister + +item.banner.small_stripes.black=Black Paly +item.banner.small_stripes.red=Red Paly +item.banner.small_stripes.green=Green Paly +item.banner.small_stripes.brown=Brown Paly +item.banner.small_stripes.blue=Blue Paly +item.banner.small_stripes.purple=Purple Paly +item.banner.small_stripes.cyan=Cyan Paly +item.banner.small_stripes.silver=Light Gray Paly +item.banner.small_stripes.gray=Gray Paly +item.banner.small_stripes.pink=Pink Paly +item.banner.small_stripes.lime=Lime Paly +item.banner.small_stripes.yellow=Yellow Paly +item.banner.small_stripes.lightBlue=Light Blue Paly +item.banner.small_stripes.magenta=Magenta Paly +item.banner.small_stripes.orange=Orange Paly +item.banner.small_stripes.white=White Paly + +item.banner.cross.black=Black Saltire +item.banner.cross.red=Red Saltire +item.banner.cross.green=Green Saltire +item.banner.cross.brown=Brown Saltire +item.banner.cross.blue=Blue Saltire +item.banner.cross.purple=Purple Saltire +item.banner.cross.cyan=Cyan Saltire +item.banner.cross.silver=Light Gray Saltire +item.banner.cross.gray=Gray Saltire +item.banner.cross.pink=Pink Saltire +item.banner.cross.lime=Lime Saltire +item.banner.cross.yellow=Yellow Saltire +item.banner.cross.lightBlue=Light Blue Saltire +item.banner.cross.magenta=Magenta Saltire +item.banner.cross.orange=Orange Saltire +item.banner.cross.white=White Saltire + +item.banner.triangle_bottom.black=Black Chevron +item.banner.triangle_bottom.red=Red Chevron +item.banner.triangle_bottom.green=Green Chevron +item.banner.triangle_bottom.brown=Brown Chevron +item.banner.triangle_bottom.blue=Blue Chevron +item.banner.triangle_bottom.purple=Purple Chevron +item.banner.triangle_bottom.cyan=Cyan Chevron +item.banner.triangle_bottom.silver=Light Gray Chevron +item.banner.triangle_bottom.gray=Gray Chevron +item.banner.triangle_bottom.pink=Pink Chevron +item.banner.triangle_bottom.lime=Lime Chevron +item.banner.triangle_bottom.yellow=Yellow Chevron +item.banner.triangle_bottom.lightBlue=Light Blue Chevron +item.banner.triangle_bottom.magenta=Magenta Chevron +item.banner.triangle_bottom.orange=Orange Chevron +item.banner.triangle_bottom.white=White Chevron + +item.banner.triangle_top.black=Black Inverted Chevron +item.banner.triangle_top.red=Red Inverted Chevron +item.banner.triangle_top.green=Green Inverted Chevron +item.banner.triangle_top.brown=Brown Inverted Chevron +item.banner.triangle_top.blue=Blue Inverted Chevron +item.banner.triangle_top.purple=Purple Inverted Chevron +item.banner.triangle_top.cyan=Cyan Inverted Chevron +item.banner.triangle_top.silver=Light Gray Inverted Chevron +item.banner.triangle_top.gray=Gray Inverted Chevron +item.banner.triangle_top.pink=Pink Inverted Chevron +item.banner.triangle_top.lime=Lime Inverted Chevron +item.banner.triangle_top.yellow=Yellow Inverted Chevron +item.banner.triangle_top.lightBlue=Light Blue Inverted Chevron +item.banner.triangle_top.magenta=Magenta Inverted Chevron +item.banner.triangle_top.orange=Orange Inverted Chevron +item.banner.triangle_top.white=White Inverted Chevron + +item.banner.triangles_bottom.black=Black Base Indented +item.banner.triangles_bottom.red=Red Base Indented +item.banner.triangles_bottom.green=Green Base Indented +item.banner.triangles_bottom.brown=Brown Base Indented +item.banner.triangles_bottom.blue=Blue Base Indented +item.banner.triangles_bottom.purple=Purple Base Indented +item.banner.triangles_bottom.cyan=Cyan Base Indented +item.banner.triangles_bottom.silver=Light Gray Base Indented +item.banner.triangles_bottom.gray=Gray Base Indented +item.banner.triangles_bottom.pink=Pink Base Indented +item.banner.triangles_bottom.lime=Lime Base Indented +item.banner.triangles_bottom.yellow=Yellow Base Indented +item.banner.triangles_bottom.lightBlue=Light Blue Base Indented +item.banner.triangles_bottom.magenta=Magenta Base Indented +item.banner.triangles_bottom.orange=Orange Base Indented +item.banner.triangles_bottom.white=White Base Indented + +item.banner.triangles_top.black=Black Chief Indented +item.banner.triangles_top.red=Red Chief Indented +item.banner.triangles_top.green=Green Chief Indented +item.banner.triangles_top.brown=Brown Chief Indented +item.banner.triangles_top.blue=Blue Chief Indented +item.banner.triangles_top.purple=Purple Chief Indented +item.banner.triangles_top.cyan=Cyan Chief Indented +item.banner.triangles_top.silver=Light Gray Chief Indented +item.banner.triangles_top.gray=Gray Chief Indented +item.banner.triangles_top.pink=Pink Chief Indented +item.banner.triangles_top.lime=Lime Chief Indented +item.banner.triangles_top.yellow=Yellow Chief Indented +item.banner.triangles_top.lightBlue=Light Blue Chief Indented +item.banner.triangles_top.magenta=Magenta Chief Indented +item.banner.triangles_top.orange=Orange Chief Indented +item.banner.triangles_top.white=White Chief Indented + +item.banner.diagonal_left.black=Black Per Bend Sinister +item.banner.diagonal_left.red=Red Per Bend Sinister +item.banner.diagonal_left.green=Green Per Bend Sinister +item.banner.diagonal_left.brown=Brown Per Bend Sinister +item.banner.diagonal_left.blue=Blue Per Bend Sinister +item.banner.diagonal_left.purple=Purple Per Bend Sinister +item.banner.diagonal_left.cyan=Cyan Per Bend Sinister +item.banner.diagonal_left.silver=Light Gray Per Bend Sinister +item.banner.diagonal_left.gray=Gray Per Bend Sinister +item.banner.diagonal_left.pink=Pink Per Bend Sinister +item.banner.diagonal_left.lime=Lime Per Bend Sinister +item.banner.diagonal_left.yellow=Yellow Per Bend Sinister +item.banner.diagonal_left.lightBlue=Light Blue Per Bend Sinister +item.banner.diagonal_left.magenta=Magenta Per Bend Sinister +item.banner.diagonal_left.orange=Orange Per Bend Sinister +item.banner.diagonal_left.white=White Per Bend Sinister + +item.banner.diagonal_right.black=Black Per Bend +item.banner.diagonal_right.red=Red Per Bend +item.banner.diagonal_right.green=Green Per Bend +item.banner.diagonal_right.brown=Brown Per Bend +item.banner.diagonal_right.blue=Blue Per Bend +item.banner.diagonal_right.purple=Purple Per Bend +item.banner.diagonal_right.cyan=Cyan Per Bend +item.banner.diagonal_right.silver=Light Gray Per Bend +item.banner.diagonal_right.gray=Gray Per Bend +item.banner.diagonal_right.pink=Pink Per Bend +item.banner.diagonal_right.lime=Lime Per Bend +item.banner.diagonal_right.yellow=Yellow Per Bend +item.banner.diagonal_right.lightBlue=Light Blue Per Bend +item.banner.diagonal_right.magenta=Magenta Per Bend +item.banner.diagonal_right.orange=Orange Per Bend +item.banner.diagonal_right.white=White Per Bend + +item.banner.diagonal_up_left.black=Black Per Bend Inverted +item.banner.diagonal_up_left.red=Red Per Bend Inverted +item.banner.diagonal_up_left.green=Green Per Bend Inverted +item.banner.diagonal_up_left.brown=Brown Per Bend Inverted +item.banner.diagonal_up_left.blue=Blue Per Bend Inverted +item.banner.diagonal_up_left.purple=Purple Per Bend Inverted +item.banner.diagonal_up_left.cyan=Cyan Per Bend Inverted +item.banner.diagonal_up_left.silver=Light Gray Per Bend Inverted +item.banner.diagonal_up_left.gray=Gray Per Bend Inverted +item.banner.diagonal_up_left.pink=Pink Per Bend Inverted +item.banner.diagonal_up_left.lime=Lime Per Bend Inverted +item.banner.diagonal_up_left.yellow=Yellow Per Bend Inverted +item.banner.diagonal_up_left.lightBlue=Light Blue Per Bend Inverted +item.banner.diagonal_up_left.magenta=Magenta Per Bend Inverted +item.banner.diagonal_up_left.orange=Orange Per Bend Inverted +item.banner.diagonal_up_left.white=White Per Bend Inverted + +item.banner.diagonal_up_right.black=Black Per Bend Sinister Inverted +item.banner.diagonal_up_right.red=Red Per Bend Sinister Inverted +item.banner.diagonal_up_right.green=Green Per Bend Sinister Inverted +item.banner.diagonal_up_right.brown=Brown Per Bend Sinister Inverted +item.banner.diagonal_up_right.blue=Blue Per Bend Sinister Inverted +item.banner.diagonal_up_right.purple=Purple Per Bend Sinister Inverted +item.banner.diagonal_up_right.cyan=Cyan Per Bend Sinister Inverted +item.banner.diagonal_up_right.silver=Light Gray Per Bend Sinister Inverted +item.banner.diagonal_up_right.gray=Gray Per Bend Sinister Inverted +item.banner.diagonal_up_right.pink=Pink Per Bend Sinister Inverted +item.banner.diagonal_up_right.lime=Lime Per Bend Sinister Inverted +item.banner.diagonal_up_right.yellow=Yellow Per Bend Sinister Inverted +item.banner.diagonal_up_right.lightBlue=Light Blue Per Bend Sinister Inverted +item.banner.diagonal_up_right.magenta=Magenta Per Bend Sinister Inverted +item.banner.diagonal_up_right.orange=Orange Per Bend Sinister Inverted +item.banner.diagonal_up_right.white=White Per Bend Sinister Inverted + +item.banner.circle.black=Black Roundel +item.banner.circle.red=Red Roundel +item.banner.circle.green=Green Roundel +item.banner.circle.brown=Brown Roundel +item.banner.circle.blue=Blue Roundel +item.banner.circle.purple=Purple Roundel +item.banner.circle.cyan=Cyan Roundel +item.banner.circle.silver=Light Gray Roundel +item.banner.circle.gray=Gray Roundel +item.banner.circle.pink=Pink Roundel +item.banner.circle.lime=Lime Roundel +item.banner.circle.yellow=Yellow Roundel +item.banner.circle.lightBlue=Light Blue Roundel +item.banner.circle.magenta=Magenta Roundel +item.banner.circle.orange=Orange Roundel +item.banner.circle.white=White Roundel + +item.banner.rhombus.black=Black Lozenge +item.banner.rhombus.red=Red Lozenge +item.banner.rhombus.green=Green Lozenge +item.banner.rhombus.brown=Brown Lozenge +item.banner.rhombus.blue=Blue Lozenge +item.banner.rhombus.purple=Purple Lozenge +item.banner.rhombus.cyan=Cyan Lozenge +item.banner.rhombus.silver=Light Gray Lozenge +item.banner.rhombus.gray=Gray Lozenge +item.banner.rhombus.pink=Pink Lozenge +item.banner.rhombus.lime=Lime Lozenge +item.banner.rhombus.yellow=Yellow Lozenge +item.banner.rhombus.lightBlue=Light Blue Lozenge +item.banner.rhombus.magenta=Magenta Lozenge +item.banner.rhombus.orange=Orange Lozenge +item.banner.rhombus.white=White Lozenge + +item.banner.half_vertical.black=Black Per Pale +item.banner.half_vertical.red=Red Per Pale +item.banner.half_vertical.green=Green Per Pale +item.banner.half_vertical.brown=Brown Per Pale +item.banner.half_vertical.blue=Blue Per Pale +item.banner.half_vertical.purple=Purple Per Pale +item.banner.half_vertical.cyan=Cyan Per Pale +item.banner.half_vertical.silver=Light Gray Per Pale +item.banner.half_vertical.gray=Gray Per Pale +item.banner.half_vertical.pink=Pink Per Pale +item.banner.half_vertical.lime=Lime Per Pale +item.banner.half_vertical.yellow=Yellow Per Pale +item.banner.half_vertical.lightBlue=Light Blue Per Pale +item.banner.half_vertical.magenta=Magenta Per Pale +item.banner.half_vertical.orange=Orange Per Pale +item.banner.half_vertical.white=White Per Pale + +item.banner.half_horizontal.black=Black Per Fess +item.banner.half_horizontal.red=Red Per Fess +item.banner.half_horizontal.green=Green Per Fess +item.banner.half_horizontal.brown=Brown Per Fess +item.banner.half_horizontal.blue=Blue Per Fess +item.banner.half_horizontal.purple=Purple Per Fess +item.banner.half_horizontal.cyan=Cyan Per Fess +item.banner.half_horizontal.silver=Light Gray Per Fess +item.banner.half_horizontal.gray=Gray Per Fess +item.banner.half_horizontal.pink=Pink Per Fess +item.banner.half_horizontal.lime=Lime Per Fess +item.banner.half_horizontal.yellow=Yellow Per Fess +item.banner.half_horizontal.lightBlue=Light Blue Per Fess +item.banner.half_horizontal.magenta=Magenta Per Fess +item.banner.half_horizontal.orange=Orange Per Fess +item.banner.half_horizontal.white=White Per Fess + +item.banner.half_vertical_right.black=Black Per Pale Inverted +item.banner.half_vertical_right.red=Red Per Pale Inverted +item.banner.half_vertical_right.green=Green Per Pale Inverted +item.banner.half_vertical_right.brown=Brown Per Pale Inverted +item.banner.half_vertical_right.blue=Blue Per Pale Inverted +item.banner.half_vertical_right.purple=Purple Per Pale Inverted +item.banner.half_vertical_right.cyan=Cyan Per Pale Inverted +item.banner.half_vertical_right.silver=Light Gray Per Pale Inverted +item.banner.half_vertical_right.gray=Gray Per Pale Inverted +item.banner.half_vertical_right.pink=Pink Per Pale Inverted +item.banner.half_vertical_right.lime=Lime Per Pale Inverted +item.banner.half_vertical_right.yellow=Yellow Per Pale Inverted +item.banner.half_vertical_right.lightBlue=Light Blue Per Pale Inverted +item.banner.half_vertical_right.magenta=Magenta Per Pale Inverted +item.banner.half_vertical_right.orange=Orange Per Pale Inverted +item.banner.half_vertical_right.white=White Per Pale Inverted + +item.banner.half_horizontal_bottom.black=Black Per Fess Inverted +item.banner.half_horizontal_bottom.red=Red Per Fess Inverted +item.banner.half_horizontal_bottom.green=Green Per Fess Inverted +item.banner.half_horizontal_bottom.brown=Brown Per Fess Inverted +item.banner.half_horizontal_bottom.blue=Blue Per Fess Inverted +item.banner.half_horizontal_bottom.purple=Purple Per Fess Inverted +item.banner.half_horizontal_bottom.cyan=Cyan Per Fess Inverted +item.banner.half_horizontal_bottom.silver=Light Gray Per Fess Inverted +item.banner.half_horizontal_bottom.gray=Gray Per Fess Inverted +item.banner.half_horizontal_bottom.pink=Pink Per Fess Inverted +item.banner.half_horizontal_bottom.lime=Lime Per Fess Inverted +item.banner.half_horizontal_bottom.yellow=Yellow Per Fess Inverted +item.banner.half_horizontal_bottom.lightBlue=Light Blue Per Fess Inverted +item.banner.half_horizontal_bottom.magenta=Magenta Per Fess Inverted +item.banner.half_horizontal_bottom.orange=Orange Per Fess Inverted +item.banner.half_horizontal_bottom.white=White Per Fess Inverted + +item.banner.creeper.black=Black Creeper Charge +item.banner.creeper.red=Red Creeper Charge +item.banner.creeper.green=Green Creeper Charge +item.banner.creeper.brown=Brown Creeper Charge +item.banner.creeper.blue=Blue Creeper Charge +item.banner.creeper.purple=Purple Creeper Charge +item.banner.creeper.cyan=Cyan Creeper Charge +item.banner.creeper.silver=Light Gray Creeper Charge +item.banner.creeper.gray=Gray Creeper Charge +item.banner.creeper.pink=Pink Creeper Charge +item.banner.creeper.lime=Lime Creeper Charge +item.banner.creeper.yellow=Yellow Creeper Charge +item.banner.creeper.lightBlue=Light Blue Creeper Charge +item.banner.creeper.magenta=Magenta Creeper Charge +item.banner.creeper.orange=Orange Creeper Charge +item.banner.creeper.white=White Creeper Charge + +item.banner.bricks.black=Black Field Masoned +item.banner.bricks.red=Red Field Masoned +item.banner.bricks.green=Green Field Masoned +item.banner.bricks.brown=Brown Field Masoned +item.banner.bricks.blue=Blue Field Masoned +item.banner.bricks.purple=Purple Field Masoned +item.banner.bricks.cyan=Cyan Field Masoned +item.banner.bricks.silver=Light Gray Field Masoned +item.banner.bricks.gray=Gray Field Masoned +item.banner.bricks.pink=Pink Field Masoned +item.banner.bricks.lime=Lime Field Masoned +item.banner.bricks.yellow=Yellow Field Masoned +item.banner.bricks.lightBlue=Light Blue Field Masoned +item.banner.bricks.magenta=Magenta Field Masoned +item.banner.bricks.orange=Orange Field Masoned +item.banner.bricks.white=White Field Masoned + +item.banner.gradient.black=Black Gradient +item.banner.gradient.red=Red Gradient +item.banner.gradient.green=Green Gradient +item.banner.gradient.brown=Brown Gradient +item.banner.gradient.blue=Blue Gradient +item.banner.gradient.purple=Purple Gradient +item.banner.gradient.cyan=Cyan Gradient +item.banner.gradient.silver=Light Gray Gradient +item.banner.gradient.gray=Gray Gradient +item.banner.gradient.pink=Pink Gradient +item.banner.gradient.lime=Lime Gradient +item.banner.gradient.yellow=Yellow Gradient +item.banner.gradient.lightBlue=Light Blue Gradient +item.banner.gradient.magenta=Magenta Gradient +item.banner.gradient.orange=Orange Gradient +item.banner.gradient.white=White Gradient + +item.banner.gradient_up.black=Black Base Gradient +item.banner.gradient_up.red=Red Base Gradient +item.banner.gradient_up.green=Green Base Gradient +item.banner.gradient_up.brown=Brown Base Gradient +item.banner.gradient_up.blue=Blue Base Gradient +item.banner.gradient_up.purple=Purple Base Gradient +item.banner.gradient_up.cyan=Cyan Base Gradient +item.banner.gradient_up.silver=Light Gray Base Gradient +item.banner.gradient_up.gray=Gray Base Gradient +item.banner.gradient_up.pink=Pink Base Gradient +item.banner.gradient_up.lime=Lime Base Gradient +item.banner.gradient_up.yellow=Yellow Base Gradient +item.banner.gradient_up.lightBlue=Light Blue Base Gradient +item.banner.gradient_up.magenta=Magenta Base Gradient +item.banner.gradient_up.orange=Orange Base Gradient +item.banner.gradient_up.white=White Base Gradient + +item.banner.skull.black=Black Skull Charge +item.banner.skull.red=Red Skull Charge +item.banner.skull.green=Green Skull Charge +item.banner.skull.brown=Brown Skull Charge +item.banner.skull.blue=Blue Skull Charge +item.banner.skull.purple=Purple Skull Charge +item.banner.skull.cyan=Cyan Skull Charge +item.banner.skull.silver=Light Gray Skull Charge +item.banner.skull.gray=Gray Skull Charge +item.banner.skull.pink=Pink Skull Charge +item.banner.skull.lime=Lime Skull Charge +item.banner.skull.yellow=Yellow Skull Charge +item.banner.skull.lightBlue=Light Blue Skull Charge +item.banner.skull.magenta=Magenta Skull Charge +item.banner.skull.orange=Orange Skull Charge +item.banner.skull.white=White Skull Charge + +item.banner.flower.black=Black Flower Charge +item.banner.flower.red=Red Flower Charge +item.banner.flower.green=Green Flower Charge +item.banner.flower.brown=Brown Flower Charge +item.banner.flower.blue=Blue Flower Charge +item.banner.flower.purple=Purple Flower Charge +item.banner.flower.cyan=Cyan Flower Charge +item.banner.flower.silver=Light Gray Flower Charge +item.banner.flower.gray=Gray Flower Charge +item.banner.flower.pink=Pink Flower Charge +item.banner.flower.lime=Lime Flower Charge +item.banner.flower.yellow=Yellow Flower Charge +item.banner.flower.lightBlue=Light Blue Flower Charge +item.banner.flower.magenta=Magenta Flower Charge +item.banner.flower.orange=Orange Flower Charge +item.banner.flower.white=White Flower Charge + +item.banner.border.black=Black Bordure +item.banner.border.red=Red Bordure +item.banner.border.green=Green Bordure +item.banner.border.brown=Brown Bordure +item.banner.border.blue=Blue Bordure +item.banner.border.purple=Purple Bordure +item.banner.border.cyan=Cyan Bordure +item.banner.border.silver=Light Gray Bordure +item.banner.border.gray=Gray Bordure +item.banner.border.pink=Pink Bordure +item.banner.border.lime=Lime Bordure +item.banner.border.yellow=Yellow Bordure +item.banner.border.lightBlue=Light Blue Bordure +item.banner.border.magenta=Magenta Bordure +item.banner.border.orange=Orange Bordure +item.banner.border.white=White Bordure + +item.banner.curly_border.black=Black Bordure Indented +item.banner.curly_border.red=Red Bordure Indented +item.banner.curly_border.green=Green Bordure Indented +item.banner.curly_border.brown=Brown Bordure Indented +item.banner.curly_border.blue=Blue Bordure Indented +item.banner.curly_border.purple=Purple Bordure Indented +item.banner.curly_border.cyan=Cyan Bordure Indented +item.banner.curly_border.silver=Light Gray Bordure Indented +item.banner.curly_border.gray=Gray Bordure Indented +item.banner.curly_border.pink=Pink Bordure Indented +item.banner.curly_border.lime=Lime Bordure Indented +item.banner.curly_border.yellow=Yellow Bordure Indented +item.banner.curly_border.lightBlue=Light Blue Bordure Indented +item.banner.curly_border.magenta=Magenta Bordure Indented +item.banner.curly_border.orange=Orange Bordure Indented +item.banner.curly_border.white=White Bordure Indented + +item.banner.mojang.black=Black Thing +item.banner.mojang.red=Red Thing +item.banner.mojang.green=Green Thing +item.banner.mojang.brown=Brown Thing +item.banner.mojang.blue=Blue Thing +item.banner.mojang.purple=Purple Thing +item.banner.mojang.cyan=Cyan Thing +item.banner.mojang.silver=Light Gray Thing +item.banner.mojang.gray=Gray Thing +item.banner.mojang.pink=Pink Thing +item.banner.mojang.lime=Lime Thing +item.banner.mojang.yellow=Yellow Thing +item.banner.mojang.lightBlue=Light Blue Thing +item.banner.mojang.magenta=Magenta Thing +item.banner.mojang.orange=Orange Thing +item.banner.mojang.white=White Thing + +item.banner.straight_cross.black=Black Cross +item.banner.straight_cross.red=Red Cross +item.banner.straight_cross.green=Green Cross +item.banner.straight_cross.brown=Brown Cross +item.banner.straight_cross.blue=Blue Cross +item.banner.straight_cross.purple=Purple Cross +item.banner.straight_cross.cyan=Cyan Cross +item.banner.straight_cross.silver=Light Gray Cross +item.banner.straight_cross.gray=Gray Cross +item.banner.straight_cross.pink=Pink Cross +item.banner.straight_cross.lime=Lime Cross +item.banner.straight_cross.yellow=Yellow Cross +item.banner.straight_cross.lightBlue=Light Blue Cross +item.banner.straight_cross.magenta=Magenta Cross +item.banner.straight_cross.orange=Orange Cross +item.banner.straight_cross.white=White Cross + +tile.purpurBlock.name=Purpur Block +tile.purpurPillar.name=Purpur Pillar +tile.purpurStairs.name=Purpur Stairs +tile.purpurSlab.name=Purpur Slab +tile.purpurSlab.default.name=Purpur Slab +tile.purpurStairs.name=Purpur Stairs +tile.endRod.name=End Rod +tile.chorusPlant.name=Chorus Plant +tile.chorusFlower.name=Chorus Flower +tile.grassPath.name=Grass Path +tile.endBricks.name=End Stone Bricks + +item.chorusFruit.name=Chorus Fruit +item.chorusFruitPopped.name=Popped Chorus Fruit +item.beetroot.name=Beetroot +item.beetroot_seeds.name=Beetroot Seeds +item.beetroot_soup.name=Beetroot Soup item.elytra.name=Elytra \ No newline at end of file diff --git a/resources/resources/profanity_filter.wlist b/resources/resources/profanity_filter.wlist new file mode 100755 index 0000000..86f3162 --- /dev/null +++ b/resources/resources/profanity_filter.wlist @@ -0,0 +1,740 @@ +1488 +a55hole +ahole +ainujin +ainuzin +akimekura +anal +anus +anuses +anushead +anuslick +anuss +aokan +arsch +arschloch +arse +arsed +arsehole +arseholed +arseholes +arseholing +arselicker +arses +ass +asshat +asshole +assholed +assholes +assholing +asslick +asslicker +asses +auschwitz +b00bs +b00bz +b1tc +baise +bakachon +bakatyon +ballsack +ballzack +bamf +beaner +beeatch +beeeyotch +beefwhistle +beeotch +beetch +beeyotch +bellend +bestiality +beyitch +beyotch +biach +biotch +bitch +bitches +bitching +blad +bladt +blowjob +blow job +blowme +blow me +blyad +blyadt +bon3r +boner +boobs +boobz +btch +bukakke +bullshit +bung +butagorosi +butthead +butthole +buttplug +c0ck +cabron +cacca +cadela +cagada +cameljockey +caralho +castrate +cazzo +ceemen +ch1nk +chankoro +chieokure +chikusatsu +ching chong +chinga +chingada madre +chingado +chingate +chink +chinpo +chlamydia +choad +chode +chonga +chonko +chonkoro +chourimbo +chourinbo +chourippo +chuurembo +chuurenbo +circlejerk +cl1t +cli7 +clit +clitoris +cocain +cocaine +cock +cocksucker +coglione +coglioni +coitus +coituss +cojelon +cojones +condom +coon +coon hunt +coon kill +coonhunt +coonkill +cooter +cotton pic +cotton pik +cottonpic +cottonpik +culear +culero +culo +cum +cumming +cun7 +cunt +cvn7 +cvnt +cyka +d1kc +d4go +dago +darkie +dickhead +dick +dicks +dikc +dildo +dio bestia +dumass +dumbass +durka durka +dyke +ejaculate +encule +enjokousai +enzyokousai +etahinin +etambo +etanbo +f0ck +f0kc +f3lch +facking +fag +faggot +fags +faggots +fanculo +fatass +fcuk +fcuuk +felch +fellatio +fetish +fickdich +figlio di puttana +fku +fock +fokc +foreskin +fotze +foutre +fucc +fuck +fucks +fuckd +fucked +fucker +fuckers +fucking +fuckr +fuct +fujinoyamai +fukashokumin +fupa +fuuck +fuuckd +fuucked +fuucker +fuucking +fuuckr +fuuuck +fuuuckd +fuuucked +fuuucker +fuuucking +fuuuckr +fuuuuck +fuuuuckd +fuuuucked +fuuuucker +fuuuucking +fuuuuckr +fuuuuuck +fuuuuuckd +fuuuuucked +fuuuuucker +fuuuuucking +fuuuuuckr +fuuuuuuck +fuuuuuuckd +fuuuuuucked +fuuuuuucker +fuuuuuucking +fuuuuuuckr +fuuuuuuuck +fuuuuuuuckd +fuuuuuuucked +fuuuuuuucker +fuuuuuuucking +fuuuuuuuckr +fuuuuuuuuck +fuuuuuuuuckd +fuuuuuuuucked +fuuuuuuuucker +fuuuuuuuucking +fuuuuuuuuckr +fuuuuuuuuuck +fuuuuuuuuuckd +fuuuuuuuuucked +fuuuuuuuuucker +fuuuuuuuuucking +fuuuuuuuuuckr +fuuuuuuuuuu +fvck +fxck +fxuxcxk +g000k +g00k +g0ok +gestapo +go0k +god damn +goddamn +goldenshowers +golliwogg +gollywog +gooch +gook +goook +gyp +h0m0 +h0mo +h1tl3 +h1tle +hairpie +hakujakusha +hakuroubyo +hakuzyakusya +hantoujin +hantouzin +herpes +hitl3r +hitler +hitlr +holocaust +hom0 +homo +honky +hooker +hor3 +hore +hukasyokumin +hure +hurensohn +huzinoyamai +hymen +inc3st +incest +inculato +injun +intercourse +inugoroshi +inugorosi +j1g4b0 +j1g4bo +j1gab0 +j1gabo +jack off +jackass +jap +jerkoff +jig4b0 +jig4bo +jigabo +jigaboo +jiggaboo +jizz +joder +joto +jungle bunny +junglebunny +k k k +k1k3 +kichigai +kik3 +kike +kikeiji +kikeizi +kilurself +kitigai +kkk +klu klux +klu klux klan +kluklux +knobhead +koon hunt +koon kill +koonhunt +koonkill +koroshiteyaru +koumoujin +koumouzin +ku klux klan +kun7 +kurombo +kurva +kurwa +kxkxk +l3sb0 +lesbo +lezbo +lezzie +m07th3rfukr +m0th3rfvk3r +m0th3rfvker +madonna puttana +manberries +manko +manshaft +maricon +masterbat +masterbate +masturbacion +masturbait +masturbare +masturbate +masturbazione +merda +merde +meth +mierda +milf +minge +miststück +mitsukuchi +mitukuti +molest +molester +molestor +mong +moon cricket +moth3rfucer +moth3rfvk3r +moth3rfvker +motherfucker +mulatto +n1663r +n1664 +n166a +n166er +n1g3r +n1german +n1gg3r +n1ggerman +n3gro +n4g3r +n4gg3r +n4ggerman +n4z1 +nag3r +nagg3r +naggerman +natzi +naz1 +nazi +nazl +negerman +nggerman +nggr +nhiggerman +ni666 +ni66a +ni66er +ni66g +ni6g +ni6g6 +ni6gg +nig +nig66 +nig6g +nigar +nigerman +nigg3 +nigg6 +nigga +niggaz +niggerman +nigger +nigglet +niggr +nigguh +niggur +niggy +niglet +nignog +nimpinin +ninpinin +nipples +niqqa +niqqer +nonce +nugga +nutsack +nutted +nyggerman +omeko +orgy +p3n15 +p3n1s +p3ni5 +p3nis +p3nl5 +p3nls +paki +panties +pedo +pedoph +pedophile +pen15 +pen1s +pendejo +peni5 +penile +penis +penis +penl5 +penls +penus +perra +phag +phaggot +phagot +phuck +pikey +pinche +pizda +polla +porca madonna +porch monkey +porn +pornhub +porra +pu555y +pu55y +pub1c +pube +pubic +pun4ni +pun4nl +punal +punan1 +punani +punanl +puss1 +puss3 +puss5 +pusse +pussi +pussy +pussys +pussies +pusss1 +pussse +pusssi +pusssl +pusssy +pussy +puta +putain +pute +puto +puttana +puttane +puttaniere +puzzy +pvssy +queef +r3c7um +r4p15t +r4p1st +r4p3 +r4pi5t +r4pist +raape +raghead +raibyo +raip +rap15t +rap1st +rapage +rape +raped +rapi5t +raping +rapist +red tube +reggin +reipu +retard +ricchione +rimjob +rimming +rizzape +rompari +salaud +salope +sangokujin +sangokuzin +santorum +scheiße +schlampe +schlampe +schlong +schwuchtel +scrote +secks +seishinhakujaku +seishinijo +seisinhakuzyaku +seisinizyo +semen +semushiotoko +semusiotoko +sh|t +sh|thead +sh|tstain +sh17 +sh17head +sh17stain +sh1t +sh1thead +sh1tstain +shat +shemale +shi7 +shi7head +shi7stain +shinajin +shinheimin +shirakko +shit +shithead +shitstain +shitting +shitty +shokubutsuningen +sinazin +sinheimin +skank +slut +smd +sodom +sofa king +sofaking +spanishick +spanishook +spanishunk +succhia cazzi +syokubutuningen +taint +tapatte +tapette +tarlouse +tea bag +teabag +teebag +teensex +teino +testa di cazzo +testicles +thot +tieokure +tinpo +tits +titz +titties +tittiez +tokushugakkyu +tokusyugakkyu +torukoburo +torukojo +torukozyo +tosatsu +tosatu +towelhead +trannie +tranny +tunbo +tw47 +tw4t +twat +tyankoro +tyonga +tyonko +tyonkoro +tyourinbo +tyourippo +tyurenbo +ushigoroshi +usigorosi +v461n4 +v461na +v46in4 +v46ina +v4g1n4 +v4g1na +v4gin4 +v4gina +va61n4 +va61na +va6in4 +va6ina +vaccagare +vaffanculo +vag +vag1n4 +vag1na +vagin4 +vagina +vatefaire +vvhitepower +w3tb4ck +w3tback +wank +wanker +wetb4ck +wetback +wh0r3 +wh0re +white power +whitepower +whor3 +whore +whores +wog +wop +x8lp3t +xbl pet +xblpet +xblrewards +xl3lpet +yabunirami +zipperhead +блядь +сука +アオカン +あおかん +イヌゴロシ +いぬごろし +インバイ +いんばい +オナニー +おなにー +オメコ +カワラコジキ +かわらこじき +カワラモノ +かわらもの +キケイジ +きけいじ +キチガイ +きちがい +キンタマ +きんたま +クロンボ +くろんぼ +コロシテヤル +ころしてやる +シナジン +しなじん +タチンボ +たちんぼ +チョンコウ +ちょんこう +チョンコロ +ちょんころ +ちょん公 +チンポ +ちんぽ +ツンボ +つんぼ +とるこじょう +とるこぶろ +トルコ嬢 +トルコ風呂 +ニガー +ニグロ +にんぴにん +はんとうじん +マンコ +まんこ +レイプ +れいぷ +低能 +屠殺 +強姦 +援交 +支那人 +精薄 +精薄者 +輪姦 \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/Block.java b/src/game/java/net/minecraft/block/Block.java similarity index 100% rename from src/main/java/net/minecraft/block/Block.java rename to src/game/java/net/minecraft/block/Block.java diff --git a/src/main/java/net/minecraft/block/BlockAir.java b/src/game/java/net/minecraft/block/BlockAir.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockAir.java rename to src/game/java/net/minecraft/block/BlockAir.java diff --git a/src/main/java/net/minecraft/block/BlockAnvil.java b/src/game/java/net/minecraft/block/BlockAnvil.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockAnvil.java rename to src/game/java/net/minecraft/block/BlockAnvil.java diff --git a/src/main/java/net/minecraft/block/BlockBanner.java b/src/game/java/net/minecraft/block/BlockBanner.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBanner.java rename to src/game/java/net/minecraft/block/BlockBanner.java diff --git a/src/main/java/net/minecraft/block/BlockBarrier.java b/src/game/java/net/minecraft/block/BlockBarrier.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBarrier.java rename to src/game/java/net/minecraft/block/BlockBarrier.java diff --git a/src/main/java/net/minecraft/block/BlockBasePressurePlate.java b/src/game/java/net/minecraft/block/BlockBasePressurePlate.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBasePressurePlate.java rename to src/game/java/net/minecraft/block/BlockBasePressurePlate.java diff --git a/src/main/java/net/minecraft/block/BlockBeacon.java b/src/game/java/net/minecraft/block/BlockBeacon.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBeacon.java rename to src/game/java/net/minecraft/block/BlockBeacon.java diff --git a/src/main/java/net/minecraft/block/BlockBed.java b/src/game/java/net/minecraft/block/BlockBed.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBed.java rename to src/game/java/net/minecraft/block/BlockBed.java diff --git a/src/main/java/net/minecraft/block/BlockBeetroot.java b/src/game/java/net/minecraft/block/BlockBeetroot.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBeetroot.java rename to src/game/java/net/minecraft/block/BlockBeetroot.java diff --git a/src/main/java/net/minecraft/block/BlockBookshelf.java b/src/game/java/net/minecraft/block/BlockBookshelf.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBookshelf.java rename to src/game/java/net/minecraft/block/BlockBookshelf.java diff --git a/src/main/java/net/minecraft/block/BlockBreakable.java b/src/game/java/net/minecraft/block/BlockBreakable.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBreakable.java rename to src/game/java/net/minecraft/block/BlockBreakable.java diff --git a/src/main/java/net/minecraft/block/BlockBrewingStand.java b/src/game/java/net/minecraft/block/BlockBrewingStand.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBrewingStand.java rename to src/game/java/net/minecraft/block/BlockBrewingStand.java diff --git a/src/main/java/net/minecraft/block/BlockBush.java b/src/game/java/net/minecraft/block/BlockBush.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockBush.java rename to src/game/java/net/minecraft/block/BlockBush.java diff --git a/src/main/java/net/minecraft/block/BlockButton.java b/src/game/java/net/minecraft/block/BlockButton.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockButton.java rename to src/game/java/net/minecraft/block/BlockButton.java diff --git a/src/main/java/net/minecraft/block/BlockButtonStone.java b/src/game/java/net/minecraft/block/BlockButtonStone.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockButtonStone.java rename to src/game/java/net/minecraft/block/BlockButtonStone.java diff --git a/src/main/java/net/minecraft/block/BlockButtonWood.java b/src/game/java/net/minecraft/block/BlockButtonWood.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockButtonWood.java rename to src/game/java/net/minecraft/block/BlockButtonWood.java diff --git a/src/main/java/net/minecraft/block/BlockCactus.java b/src/game/java/net/minecraft/block/BlockCactus.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCactus.java rename to src/game/java/net/minecraft/block/BlockCactus.java diff --git a/src/main/java/net/minecraft/block/BlockCake.java b/src/game/java/net/minecraft/block/BlockCake.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCake.java rename to src/game/java/net/minecraft/block/BlockCake.java diff --git a/src/main/java/net/minecraft/block/BlockCarpet.java b/src/game/java/net/minecraft/block/BlockCarpet.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCarpet.java rename to src/game/java/net/minecraft/block/BlockCarpet.java diff --git a/src/main/java/net/minecraft/block/BlockCarrot.java b/src/game/java/net/minecraft/block/BlockCarrot.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCarrot.java rename to src/game/java/net/minecraft/block/BlockCarrot.java diff --git a/src/main/java/net/minecraft/block/BlockCauldron.java b/src/game/java/net/minecraft/block/BlockCauldron.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCauldron.java rename to src/game/java/net/minecraft/block/BlockCauldron.java diff --git a/src/main/java/net/minecraft/block/BlockChest.java b/src/game/java/net/minecraft/block/BlockChest.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockChest.java rename to src/game/java/net/minecraft/block/BlockChest.java diff --git a/src/main/java/net/minecraft/block/BlockChorusFlower.java b/src/game/java/net/minecraft/block/BlockChorusFlower.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockChorusFlower.java rename to src/game/java/net/minecraft/block/BlockChorusFlower.java diff --git a/src/main/java/net/minecraft/block/BlockChorusPlant.java b/src/game/java/net/minecraft/block/BlockChorusPlant.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockChorusPlant.java rename to src/game/java/net/minecraft/block/BlockChorusPlant.java diff --git a/src/main/java/net/minecraft/block/BlockClay.java b/src/game/java/net/minecraft/block/BlockClay.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockClay.java rename to src/game/java/net/minecraft/block/BlockClay.java diff --git a/src/main/java/net/minecraft/block/BlockCocoa.java b/src/game/java/net/minecraft/block/BlockCocoa.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCocoa.java rename to src/game/java/net/minecraft/block/BlockCocoa.java diff --git a/src/main/java/net/minecraft/block/BlockColored.java b/src/game/java/net/minecraft/block/BlockColored.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockColored.java rename to src/game/java/net/minecraft/block/BlockColored.java diff --git a/src/main/java/net/minecraft/block/BlockCommandBlock.java b/src/game/java/net/minecraft/block/BlockCommandBlock.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCommandBlock.java rename to src/game/java/net/minecraft/block/BlockCommandBlock.java diff --git a/src/main/java/net/minecraft/block/BlockCompressedPowered.java b/src/game/java/net/minecraft/block/BlockCompressedPowered.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCompressedPowered.java rename to src/game/java/net/minecraft/block/BlockCompressedPowered.java diff --git a/src/main/java/net/minecraft/block/BlockContainer.java b/src/game/java/net/minecraft/block/BlockContainer.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockContainer.java rename to src/game/java/net/minecraft/block/BlockContainer.java diff --git a/src/main/java/net/minecraft/block/BlockCrops.java b/src/game/java/net/minecraft/block/BlockCrops.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockCrops.java rename to src/game/java/net/minecraft/block/BlockCrops.java diff --git a/src/main/java/net/minecraft/block/BlockDaylightDetector.java b/src/game/java/net/minecraft/block/BlockDaylightDetector.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDaylightDetector.java rename to src/game/java/net/minecraft/block/BlockDaylightDetector.java diff --git a/src/main/java/net/minecraft/block/BlockDeadBush.java b/src/game/java/net/minecraft/block/BlockDeadBush.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDeadBush.java rename to src/game/java/net/minecraft/block/BlockDeadBush.java diff --git a/src/main/java/net/minecraft/block/BlockDirectional.java b/src/game/java/net/minecraft/block/BlockDirectional.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDirectional.java rename to src/game/java/net/minecraft/block/BlockDirectional.java diff --git a/src/main/java/net/minecraft/block/BlockDirt.java b/src/game/java/net/minecraft/block/BlockDirt.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDirt.java rename to src/game/java/net/minecraft/block/BlockDirt.java diff --git a/src/main/java/net/minecraft/block/BlockDispenser.java b/src/game/java/net/minecraft/block/BlockDispenser.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDispenser.java rename to src/game/java/net/minecraft/block/BlockDispenser.java diff --git a/src/main/java/net/minecraft/block/BlockDoor.java b/src/game/java/net/minecraft/block/BlockDoor.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDoor.java rename to src/game/java/net/minecraft/block/BlockDoor.java diff --git a/src/main/java/net/minecraft/block/BlockDoublePlant.java b/src/game/java/net/minecraft/block/BlockDoublePlant.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDoublePlant.java rename to src/game/java/net/minecraft/block/BlockDoublePlant.java diff --git a/src/main/java/net/minecraft/block/BlockDoubleStoneSlab.java b/src/game/java/net/minecraft/block/BlockDoubleStoneSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDoubleStoneSlab.java rename to src/game/java/net/minecraft/block/BlockDoubleStoneSlab.java diff --git a/src/main/java/net/minecraft/block/BlockDoubleStoneSlabNew.java b/src/game/java/net/minecraft/block/BlockDoubleStoneSlabNew.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDoubleStoneSlabNew.java rename to src/game/java/net/minecraft/block/BlockDoubleStoneSlabNew.java diff --git a/src/main/java/net/minecraft/block/BlockDoubleWoodSlab.java b/src/game/java/net/minecraft/block/BlockDoubleWoodSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDoubleWoodSlab.java rename to src/game/java/net/minecraft/block/BlockDoubleWoodSlab.java diff --git a/src/main/java/net/minecraft/block/BlockDragonEgg.java b/src/game/java/net/minecraft/block/BlockDragonEgg.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDragonEgg.java rename to src/game/java/net/minecraft/block/BlockDragonEgg.java diff --git a/src/main/java/net/minecraft/block/BlockDropper.java b/src/game/java/net/minecraft/block/BlockDropper.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDropper.java rename to src/game/java/net/minecraft/block/BlockDropper.java diff --git a/src/main/java/net/minecraft/block/BlockDynamicLiquid.java b/src/game/java/net/minecraft/block/BlockDynamicLiquid.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockDynamicLiquid.java rename to src/game/java/net/minecraft/block/BlockDynamicLiquid.java diff --git a/src/main/java/net/minecraft/block/BlockEmptyDrops.java b/src/game/java/net/minecraft/block/BlockEmptyDrops.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEmptyDrops.java rename to src/game/java/net/minecraft/block/BlockEmptyDrops.java diff --git a/src/main/java/net/minecraft/block/BlockEnchantmentTable.java b/src/game/java/net/minecraft/block/BlockEnchantmentTable.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEnchantmentTable.java rename to src/game/java/net/minecraft/block/BlockEnchantmentTable.java diff --git a/src/main/java/net/minecraft/block/BlockEndPortal.java b/src/game/java/net/minecraft/block/BlockEndPortal.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEndPortal.java rename to src/game/java/net/minecraft/block/BlockEndPortal.java diff --git a/src/main/java/net/minecraft/block/BlockEndPortalFrame.java b/src/game/java/net/minecraft/block/BlockEndPortalFrame.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEndPortalFrame.java rename to src/game/java/net/minecraft/block/BlockEndPortalFrame.java diff --git a/src/main/java/net/minecraft/block/BlockEndRod.java b/src/game/java/net/minecraft/block/BlockEndRod.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEndRod.java rename to src/game/java/net/minecraft/block/BlockEndRod.java diff --git a/src/main/java/net/minecraft/block/BlockEnderChest.java b/src/game/java/net/minecraft/block/BlockEnderChest.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEnderChest.java rename to src/game/java/net/minecraft/block/BlockEnderChest.java diff --git a/src/main/java/net/minecraft/block/BlockEventData.java b/src/game/java/net/minecraft/block/BlockEventData.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockEventData.java rename to src/game/java/net/minecraft/block/BlockEventData.java diff --git a/src/main/java/net/minecraft/block/BlockFalling.java b/src/game/java/net/minecraft/block/BlockFalling.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFalling.java rename to src/game/java/net/minecraft/block/BlockFalling.java diff --git a/src/main/java/net/minecraft/block/BlockFarmland.java b/src/game/java/net/minecraft/block/BlockFarmland.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFarmland.java rename to src/game/java/net/minecraft/block/BlockFarmland.java diff --git a/src/main/java/net/minecraft/block/BlockFence.java b/src/game/java/net/minecraft/block/BlockFence.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFence.java rename to src/game/java/net/minecraft/block/BlockFence.java diff --git a/src/main/java/net/minecraft/block/BlockFenceGate.java b/src/game/java/net/minecraft/block/BlockFenceGate.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFenceGate.java rename to src/game/java/net/minecraft/block/BlockFenceGate.java diff --git a/src/main/java/net/minecraft/block/BlockFire.java b/src/game/java/net/minecraft/block/BlockFire.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFire.java rename to src/game/java/net/minecraft/block/BlockFire.java diff --git a/src/main/java/net/minecraft/block/BlockFlower.java b/src/game/java/net/minecraft/block/BlockFlower.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFlower.java rename to src/game/java/net/minecraft/block/BlockFlower.java diff --git a/src/main/java/net/minecraft/block/BlockFlowerPot.java b/src/game/java/net/minecraft/block/BlockFlowerPot.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFlowerPot.java rename to src/game/java/net/minecraft/block/BlockFlowerPot.java diff --git a/src/main/java/net/minecraft/block/BlockFrostedIce.java b/src/game/java/net/minecraft/block/BlockFrostedIce.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFrostedIce.java rename to src/game/java/net/minecraft/block/BlockFrostedIce.java diff --git a/src/main/java/net/minecraft/block/BlockFurnace.java b/src/game/java/net/minecraft/block/BlockFurnace.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockFurnace.java rename to src/game/java/net/minecraft/block/BlockFurnace.java diff --git a/src/main/java/net/minecraft/block/BlockGlass.java b/src/game/java/net/minecraft/block/BlockGlass.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockGlass.java rename to src/game/java/net/minecraft/block/BlockGlass.java diff --git a/src/main/java/net/minecraft/block/BlockGlowstone.java b/src/game/java/net/minecraft/block/BlockGlowstone.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockGlowstone.java rename to src/game/java/net/minecraft/block/BlockGlowstone.java diff --git a/src/main/java/net/minecraft/block/BlockGrass.java b/src/game/java/net/minecraft/block/BlockGrass.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockGrass.java rename to src/game/java/net/minecraft/block/BlockGrass.java diff --git a/src/main/java/net/minecraft/block/BlockGrassPath.java b/src/game/java/net/minecraft/block/BlockGrassPath.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockGrassPath.java rename to src/game/java/net/minecraft/block/BlockGrassPath.java diff --git a/src/main/java/net/minecraft/block/BlockGravel.java b/src/game/java/net/minecraft/block/BlockGravel.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockGravel.java rename to src/game/java/net/minecraft/block/BlockGravel.java diff --git a/src/main/java/net/minecraft/block/BlockHalfStoneSlab.java b/src/game/java/net/minecraft/block/BlockHalfStoneSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHalfStoneSlab.java rename to src/game/java/net/minecraft/block/BlockHalfStoneSlab.java diff --git a/src/main/java/net/minecraft/block/BlockHalfStoneSlabNew.java b/src/game/java/net/minecraft/block/BlockHalfStoneSlabNew.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHalfStoneSlabNew.java rename to src/game/java/net/minecraft/block/BlockHalfStoneSlabNew.java diff --git a/src/main/java/net/minecraft/block/BlockHalfWoodSlab.java b/src/game/java/net/minecraft/block/BlockHalfWoodSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHalfWoodSlab.java rename to src/game/java/net/minecraft/block/BlockHalfWoodSlab.java diff --git a/src/main/java/net/minecraft/block/BlockHardenedClay.java b/src/game/java/net/minecraft/block/BlockHardenedClay.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHardenedClay.java rename to src/game/java/net/minecraft/block/BlockHardenedClay.java diff --git a/src/main/java/net/minecraft/block/BlockHay.java b/src/game/java/net/minecraft/block/BlockHay.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHay.java rename to src/game/java/net/minecraft/block/BlockHay.java diff --git a/src/main/java/net/minecraft/block/BlockHopper.java b/src/game/java/net/minecraft/block/BlockHopper.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHopper.java rename to src/game/java/net/minecraft/block/BlockHopper.java diff --git a/src/main/java/net/minecraft/block/BlockHugeMushroom.java b/src/game/java/net/minecraft/block/BlockHugeMushroom.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockHugeMushroom.java rename to src/game/java/net/minecraft/block/BlockHugeMushroom.java diff --git a/src/main/java/net/minecraft/block/BlockIce.java b/src/game/java/net/minecraft/block/BlockIce.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockIce.java rename to src/game/java/net/minecraft/block/BlockIce.java diff --git a/src/main/java/net/minecraft/block/BlockJukebox.java b/src/game/java/net/minecraft/block/BlockJukebox.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockJukebox.java rename to src/game/java/net/minecraft/block/BlockJukebox.java diff --git a/src/main/java/net/minecraft/block/BlockLadder.java b/src/game/java/net/minecraft/block/BlockLadder.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLadder.java rename to src/game/java/net/minecraft/block/BlockLadder.java diff --git a/src/main/java/net/minecraft/block/BlockLeaves.java b/src/game/java/net/minecraft/block/BlockLeaves.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLeaves.java rename to src/game/java/net/minecraft/block/BlockLeaves.java diff --git a/src/main/java/net/minecraft/block/BlockLeavesBase.java b/src/game/java/net/minecraft/block/BlockLeavesBase.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLeavesBase.java rename to src/game/java/net/minecraft/block/BlockLeavesBase.java diff --git a/src/main/java/net/minecraft/block/BlockLever.java b/src/game/java/net/minecraft/block/BlockLever.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLever.java rename to src/game/java/net/minecraft/block/BlockLever.java diff --git a/src/main/java/net/minecraft/block/BlockLilyPad.java b/src/game/java/net/minecraft/block/BlockLilyPad.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLilyPad.java rename to src/game/java/net/minecraft/block/BlockLilyPad.java diff --git a/src/main/java/net/minecraft/block/BlockLiquid.java b/src/game/java/net/minecraft/block/BlockLiquid.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLiquid.java rename to src/game/java/net/minecraft/block/BlockLiquid.java diff --git a/src/main/java/net/minecraft/block/BlockLog.java b/src/game/java/net/minecraft/block/BlockLog.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockLog.java rename to src/game/java/net/minecraft/block/BlockLog.java diff --git a/src/main/java/net/minecraft/block/BlockMelon.java b/src/game/java/net/minecraft/block/BlockMelon.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockMelon.java rename to src/game/java/net/minecraft/block/BlockMelon.java diff --git a/src/main/java/net/minecraft/block/BlockMobSpawner.java b/src/game/java/net/minecraft/block/BlockMobSpawner.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockMobSpawner.java rename to src/game/java/net/minecraft/block/BlockMobSpawner.java diff --git a/src/main/java/net/minecraft/block/BlockMushroom.java b/src/game/java/net/minecraft/block/BlockMushroom.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockMushroom.java rename to src/game/java/net/minecraft/block/BlockMushroom.java diff --git a/src/main/java/net/minecraft/block/BlockMycelium.java b/src/game/java/net/minecraft/block/BlockMycelium.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockMycelium.java rename to src/game/java/net/minecraft/block/BlockMycelium.java diff --git a/src/main/java/net/minecraft/block/BlockNetherBrick.java b/src/game/java/net/minecraft/block/BlockNetherBrick.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockNetherBrick.java rename to src/game/java/net/minecraft/block/BlockNetherBrick.java diff --git a/src/main/java/net/minecraft/block/BlockNetherWart.java b/src/game/java/net/minecraft/block/BlockNetherWart.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockNetherWart.java rename to src/game/java/net/minecraft/block/BlockNetherWart.java diff --git a/src/main/java/net/minecraft/block/BlockNetherrack.java b/src/game/java/net/minecraft/block/BlockNetherrack.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockNetherrack.java rename to src/game/java/net/minecraft/block/BlockNetherrack.java diff --git a/src/main/java/net/minecraft/block/BlockNewLeaf.java b/src/game/java/net/minecraft/block/BlockNewLeaf.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockNewLeaf.java rename to src/game/java/net/minecraft/block/BlockNewLeaf.java diff --git a/src/main/java/net/minecraft/block/BlockNewLog.java b/src/game/java/net/minecraft/block/BlockNewLog.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockNewLog.java rename to src/game/java/net/minecraft/block/BlockNewLog.java diff --git a/src/main/java/net/minecraft/block/BlockNote.java b/src/game/java/net/minecraft/block/BlockNote.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockNote.java rename to src/game/java/net/minecraft/block/BlockNote.java diff --git a/src/main/java/net/minecraft/block/BlockObsidian.java b/src/game/java/net/minecraft/block/BlockObsidian.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockObsidian.java rename to src/game/java/net/minecraft/block/BlockObsidian.java diff --git a/src/main/java/net/minecraft/block/BlockOldLeaf.java b/src/game/java/net/minecraft/block/BlockOldLeaf.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockOldLeaf.java rename to src/game/java/net/minecraft/block/BlockOldLeaf.java diff --git a/src/main/java/net/minecraft/block/BlockOldLog.java b/src/game/java/net/minecraft/block/BlockOldLog.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockOldLog.java rename to src/game/java/net/minecraft/block/BlockOldLog.java diff --git a/src/main/java/net/minecraft/block/BlockOre.java b/src/game/java/net/minecraft/block/BlockOre.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockOre.java rename to src/game/java/net/minecraft/block/BlockOre.java diff --git a/src/main/java/net/minecraft/block/BlockPackedIce.java b/src/game/java/net/minecraft/block/BlockPackedIce.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPackedIce.java rename to src/game/java/net/minecraft/block/BlockPackedIce.java diff --git a/src/main/java/net/minecraft/block/BlockPane.java b/src/game/java/net/minecraft/block/BlockPane.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPane.java rename to src/game/java/net/minecraft/block/BlockPane.java diff --git a/src/main/java/net/minecraft/block/BlockPistonBase.java b/src/game/java/net/minecraft/block/BlockPistonBase.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPistonBase.java rename to src/game/java/net/minecraft/block/BlockPistonBase.java diff --git a/src/main/java/net/minecraft/block/BlockPistonExtension.java b/src/game/java/net/minecraft/block/BlockPistonExtension.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPistonExtension.java rename to src/game/java/net/minecraft/block/BlockPistonExtension.java diff --git a/src/main/java/net/minecraft/block/BlockPistonMoving.java b/src/game/java/net/minecraft/block/BlockPistonMoving.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPistonMoving.java rename to src/game/java/net/minecraft/block/BlockPistonMoving.java diff --git a/src/main/java/net/minecraft/block/BlockPlanks.java b/src/game/java/net/minecraft/block/BlockPlanks.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPlanks.java rename to src/game/java/net/minecraft/block/BlockPlanks.java diff --git a/src/main/java/net/minecraft/block/BlockPortal.java b/src/game/java/net/minecraft/block/BlockPortal.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPortal.java rename to src/game/java/net/minecraft/block/BlockPortal.java diff --git a/src/main/java/net/minecraft/block/BlockPotato.java b/src/game/java/net/minecraft/block/BlockPotato.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPotato.java rename to src/game/java/net/minecraft/block/BlockPotato.java diff --git a/src/main/java/net/minecraft/block/BlockPressurePlate.java b/src/game/java/net/minecraft/block/BlockPressurePlate.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPressurePlate.java rename to src/game/java/net/minecraft/block/BlockPressurePlate.java diff --git a/src/main/java/net/minecraft/block/BlockPressurePlateWeighted.java b/src/game/java/net/minecraft/block/BlockPressurePlateWeighted.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPressurePlateWeighted.java rename to src/game/java/net/minecraft/block/BlockPressurePlateWeighted.java diff --git a/src/main/java/net/minecraft/block/BlockPrismarine.java b/src/game/java/net/minecraft/block/BlockPrismarine.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPrismarine.java rename to src/game/java/net/minecraft/block/BlockPrismarine.java diff --git a/src/main/java/net/minecraft/block/BlockPumpkin.java b/src/game/java/net/minecraft/block/BlockPumpkin.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPumpkin.java rename to src/game/java/net/minecraft/block/BlockPumpkin.java diff --git a/src/main/java/net/minecraft/block/BlockPurpurPillar.java b/src/game/java/net/minecraft/block/BlockPurpurPillar.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPurpurPillar.java rename to src/game/java/net/minecraft/block/BlockPurpurPillar.java diff --git a/src/main/java/net/minecraft/block/BlockPurpurSlab.java b/src/game/java/net/minecraft/block/BlockPurpurSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockPurpurSlab.java rename to src/game/java/net/minecraft/block/BlockPurpurSlab.java diff --git a/src/main/java/net/minecraft/block/BlockQuartz.java b/src/game/java/net/minecraft/block/BlockQuartz.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockQuartz.java rename to src/game/java/net/minecraft/block/BlockQuartz.java diff --git a/src/main/java/net/minecraft/block/BlockRail.java b/src/game/java/net/minecraft/block/BlockRail.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRail.java rename to src/game/java/net/minecraft/block/BlockRail.java diff --git a/src/main/java/net/minecraft/block/BlockRailBase.java b/src/game/java/net/minecraft/block/BlockRailBase.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRailBase.java rename to src/game/java/net/minecraft/block/BlockRailBase.java diff --git a/src/main/java/net/minecraft/block/BlockRailDetector.java b/src/game/java/net/minecraft/block/BlockRailDetector.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRailDetector.java rename to src/game/java/net/minecraft/block/BlockRailDetector.java diff --git a/src/main/java/net/minecraft/block/BlockRailPowered.java b/src/game/java/net/minecraft/block/BlockRailPowered.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRailPowered.java rename to src/game/java/net/minecraft/block/BlockRailPowered.java diff --git a/src/main/java/net/minecraft/block/BlockRedFlower.java b/src/game/java/net/minecraft/block/BlockRedFlower.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedFlower.java rename to src/game/java/net/minecraft/block/BlockRedFlower.java diff --git a/src/main/java/net/minecraft/block/BlockRedSandstone.java b/src/game/java/net/minecraft/block/BlockRedSandstone.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedSandstone.java rename to src/game/java/net/minecraft/block/BlockRedSandstone.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneComparator.java b/src/game/java/net/minecraft/block/BlockRedstoneComparator.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneComparator.java rename to src/game/java/net/minecraft/block/BlockRedstoneComparator.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneDiode.java b/src/game/java/net/minecraft/block/BlockRedstoneDiode.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneDiode.java rename to src/game/java/net/minecraft/block/BlockRedstoneDiode.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneLight.java b/src/game/java/net/minecraft/block/BlockRedstoneLight.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneLight.java rename to src/game/java/net/minecraft/block/BlockRedstoneLight.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneOre.java b/src/game/java/net/minecraft/block/BlockRedstoneOre.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneOre.java rename to src/game/java/net/minecraft/block/BlockRedstoneOre.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneRepeater.java b/src/game/java/net/minecraft/block/BlockRedstoneRepeater.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneRepeater.java rename to src/game/java/net/minecraft/block/BlockRedstoneRepeater.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneTorch.java b/src/game/java/net/minecraft/block/BlockRedstoneTorch.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneTorch.java rename to src/game/java/net/minecraft/block/BlockRedstoneTorch.java diff --git a/src/main/java/net/minecraft/block/BlockRedstoneWire.java b/src/game/java/net/minecraft/block/BlockRedstoneWire.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRedstoneWire.java rename to src/game/java/net/minecraft/block/BlockRedstoneWire.java diff --git a/src/main/java/net/minecraft/block/BlockReed.java b/src/game/java/net/minecraft/block/BlockReed.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockReed.java rename to src/game/java/net/minecraft/block/BlockReed.java diff --git a/src/main/java/net/minecraft/block/BlockRotatedPillar.java b/src/game/java/net/minecraft/block/BlockRotatedPillar.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockRotatedPillar.java rename to src/game/java/net/minecraft/block/BlockRotatedPillar.java diff --git a/src/main/java/net/minecraft/block/BlockSand.java b/src/game/java/net/minecraft/block/BlockSand.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSand.java rename to src/game/java/net/minecraft/block/BlockSand.java diff --git a/src/main/java/net/minecraft/block/BlockSandStone.java b/src/game/java/net/minecraft/block/BlockSandStone.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSandStone.java rename to src/game/java/net/minecraft/block/BlockSandStone.java diff --git a/src/main/java/net/minecraft/block/BlockSapling.java b/src/game/java/net/minecraft/block/BlockSapling.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSapling.java rename to src/game/java/net/minecraft/block/BlockSapling.java diff --git a/src/main/java/net/minecraft/block/BlockSeaLantern.java b/src/game/java/net/minecraft/block/BlockSeaLantern.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSeaLantern.java rename to src/game/java/net/minecraft/block/BlockSeaLantern.java diff --git a/src/main/java/net/minecraft/block/BlockSign.java b/src/game/java/net/minecraft/block/BlockSign.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSign.java rename to src/game/java/net/minecraft/block/BlockSign.java diff --git a/src/main/java/net/minecraft/block/BlockSilverfish.java b/src/game/java/net/minecraft/block/BlockSilverfish.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSilverfish.java rename to src/game/java/net/minecraft/block/BlockSilverfish.java diff --git a/src/main/java/net/minecraft/block/BlockSkull.java b/src/game/java/net/minecraft/block/BlockSkull.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSkull.java rename to src/game/java/net/minecraft/block/BlockSkull.java diff --git a/src/main/java/net/minecraft/block/BlockSlab.java b/src/game/java/net/minecraft/block/BlockSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSlab.java rename to src/game/java/net/minecraft/block/BlockSlab.java diff --git a/src/main/java/net/minecraft/block/BlockSlime.java b/src/game/java/net/minecraft/block/BlockSlime.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSlime.java rename to src/game/java/net/minecraft/block/BlockSlime.java diff --git a/src/main/java/net/minecraft/block/BlockSnow.java b/src/game/java/net/minecraft/block/BlockSnow.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSnow.java rename to src/game/java/net/minecraft/block/BlockSnow.java diff --git a/src/main/java/net/minecraft/block/BlockSnowBlock.java b/src/game/java/net/minecraft/block/BlockSnowBlock.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSnowBlock.java rename to src/game/java/net/minecraft/block/BlockSnowBlock.java diff --git a/src/main/java/net/minecraft/block/BlockSoulSand.java b/src/game/java/net/minecraft/block/BlockSoulSand.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSoulSand.java rename to src/game/java/net/minecraft/block/BlockSoulSand.java diff --git a/src/main/java/net/minecraft/block/BlockSourceImpl.java b/src/game/java/net/minecraft/block/BlockSourceImpl.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSourceImpl.java rename to src/game/java/net/minecraft/block/BlockSourceImpl.java diff --git a/src/main/java/net/minecraft/block/BlockSponge.java b/src/game/java/net/minecraft/block/BlockSponge.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockSponge.java rename to src/game/java/net/minecraft/block/BlockSponge.java diff --git a/src/main/java/net/minecraft/block/BlockStainedGlass.java b/src/game/java/net/minecraft/block/BlockStainedGlass.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStainedGlass.java rename to src/game/java/net/minecraft/block/BlockStainedGlass.java diff --git a/src/main/java/net/minecraft/block/BlockStainedGlassPane.java b/src/game/java/net/minecraft/block/BlockStainedGlassPane.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStainedGlassPane.java rename to src/game/java/net/minecraft/block/BlockStainedGlassPane.java diff --git a/src/main/java/net/minecraft/block/BlockStairs.java b/src/game/java/net/minecraft/block/BlockStairs.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStairs.java rename to src/game/java/net/minecraft/block/BlockStairs.java diff --git a/src/main/java/net/minecraft/block/BlockStandingSign.java b/src/game/java/net/minecraft/block/BlockStandingSign.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStandingSign.java rename to src/game/java/net/minecraft/block/BlockStandingSign.java diff --git a/src/main/java/net/minecraft/block/BlockStaticLiquid.java b/src/game/java/net/minecraft/block/BlockStaticLiquid.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStaticLiquid.java rename to src/game/java/net/minecraft/block/BlockStaticLiquid.java diff --git a/src/main/java/net/minecraft/block/BlockStem.java b/src/game/java/net/minecraft/block/BlockStem.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStem.java rename to src/game/java/net/minecraft/block/BlockStem.java diff --git a/src/main/java/net/minecraft/block/BlockStone.java b/src/game/java/net/minecraft/block/BlockStone.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStone.java rename to src/game/java/net/minecraft/block/BlockStone.java diff --git a/src/main/java/net/minecraft/block/BlockStoneBrick.java b/src/game/java/net/minecraft/block/BlockStoneBrick.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStoneBrick.java rename to src/game/java/net/minecraft/block/BlockStoneBrick.java diff --git a/src/main/java/net/minecraft/block/BlockStoneSlab.java b/src/game/java/net/minecraft/block/BlockStoneSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStoneSlab.java rename to src/game/java/net/minecraft/block/BlockStoneSlab.java diff --git a/src/main/java/net/minecraft/block/BlockStoneSlabNew.java b/src/game/java/net/minecraft/block/BlockStoneSlabNew.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockStoneSlabNew.java rename to src/game/java/net/minecraft/block/BlockStoneSlabNew.java diff --git a/src/main/java/net/minecraft/block/BlockTNT.java b/src/game/java/net/minecraft/block/BlockTNT.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockTNT.java rename to src/game/java/net/minecraft/block/BlockTNT.java diff --git a/src/main/java/net/minecraft/block/BlockTallGrass.java b/src/game/java/net/minecraft/block/BlockTallGrass.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockTallGrass.java rename to src/game/java/net/minecraft/block/BlockTallGrass.java diff --git a/src/main/java/net/minecraft/block/BlockTorch.java b/src/game/java/net/minecraft/block/BlockTorch.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockTorch.java rename to src/game/java/net/minecraft/block/BlockTorch.java diff --git a/src/main/java/net/minecraft/block/BlockTrapDoor.java b/src/game/java/net/minecraft/block/BlockTrapDoor.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockTrapDoor.java rename to src/game/java/net/minecraft/block/BlockTrapDoor.java diff --git a/src/main/java/net/minecraft/block/BlockTripWire.java b/src/game/java/net/minecraft/block/BlockTripWire.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockTripWire.java rename to src/game/java/net/minecraft/block/BlockTripWire.java diff --git a/src/main/java/net/minecraft/block/BlockTripWireHook.java b/src/game/java/net/minecraft/block/BlockTripWireHook.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockTripWireHook.java rename to src/game/java/net/minecraft/block/BlockTripWireHook.java diff --git a/src/main/java/net/minecraft/block/BlockVine.java b/src/game/java/net/minecraft/block/BlockVine.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockVine.java rename to src/game/java/net/minecraft/block/BlockVine.java diff --git a/src/main/java/net/minecraft/block/BlockWall.java b/src/game/java/net/minecraft/block/BlockWall.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockWall.java rename to src/game/java/net/minecraft/block/BlockWall.java diff --git a/src/main/java/net/minecraft/block/BlockWallSign.java b/src/game/java/net/minecraft/block/BlockWallSign.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockWallSign.java rename to src/game/java/net/minecraft/block/BlockWallSign.java diff --git a/src/main/java/net/minecraft/block/BlockWeb.java b/src/game/java/net/minecraft/block/BlockWeb.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockWeb.java rename to src/game/java/net/minecraft/block/BlockWeb.java diff --git a/src/main/java/net/minecraft/block/BlockWoodSlab.java b/src/game/java/net/minecraft/block/BlockWoodSlab.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockWoodSlab.java rename to src/game/java/net/minecraft/block/BlockWoodSlab.java diff --git a/src/main/java/net/minecraft/block/BlockWorkbench.java b/src/game/java/net/minecraft/block/BlockWorkbench.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockWorkbench.java rename to src/game/java/net/minecraft/block/BlockWorkbench.java diff --git a/src/main/java/net/minecraft/block/BlockYellowFlower.java b/src/game/java/net/minecraft/block/BlockYellowFlower.java similarity index 100% rename from src/main/java/net/minecraft/block/BlockYellowFlower.java rename to src/game/java/net/minecraft/block/BlockYellowFlower.java diff --git a/src/main/java/net/minecraft/block/IGrowable.java b/src/game/java/net/minecraft/block/IGrowable.java similarity index 100% rename from src/main/java/net/minecraft/block/IGrowable.java rename to src/game/java/net/minecraft/block/IGrowable.java diff --git a/src/main/java/net/minecraft/block/ITileEntityProvider.java b/src/game/java/net/minecraft/block/ITileEntityProvider.java similarity index 100% rename from src/main/java/net/minecraft/block/ITileEntityProvider.java rename to src/game/java/net/minecraft/block/ITileEntityProvider.java diff --git a/src/main/java/net/minecraft/block/material/MapColor.java b/src/game/java/net/minecraft/block/material/MapColor.java similarity index 100% rename from src/main/java/net/minecraft/block/material/MapColor.java rename to src/game/java/net/minecraft/block/material/MapColor.java diff --git a/src/main/java/net/minecraft/block/material/Material.java b/src/game/java/net/minecraft/block/material/Material.java similarity index 100% rename from src/main/java/net/minecraft/block/material/Material.java rename to src/game/java/net/minecraft/block/material/Material.java diff --git a/src/main/java/net/minecraft/block/material/MaterialLiquid.java b/src/game/java/net/minecraft/block/material/MaterialLiquid.java similarity index 100% rename from src/main/java/net/minecraft/block/material/MaterialLiquid.java rename to src/game/java/net/minecraft/block/material/MaterialLiquid.java diff --git a/src/main/java/net/minecraft/block/material/MaterialLogic.java b/src/game/java/net/minecraft/block/material/MaterialLogic.java similarity index 100% rename from src/main/java/net/minecraft/block/material/MaterialLogic.java rename to src/game/java/net/minecraft/block/material/MaterialLogic.java diff --git a/src/main/java/net/minecraft/block/material/MaterialPortal.java b/src/game/java/net/minecraft/block/material/MaterialPortal.java similarity index 100% rename from src/main/java/net/minecraft/block/material/MaterialPortal.java rename to src/game/java/net/minecraft/block/material/MaterialPortal.java diff --git a/src/main/java/net/minecraft/block/material/MaterialTransparent.java b/src/game/java/net/minecraft/block/material/MaterialTransparent.java similarity index 100% rename from src/main/java/net/minecraft/block/material/MaterialTransparent.java rename to src/game/java/net/minecraft/block/material/MaterialTransparent.java diff --git a/src/main/java/net/minecraft/block/properties/IProperty.java b/src/game/java/net/minecraft/block/properties/IProperty.java similarity index 100% rename from src/main/java/net/minecraft/block/properties/IProperty.java rename to src/game/java/net/minecraft/block/properties/IProperty.java diff --git a/src/main/java/net/minecraft/block/properties/PropertyBool.java b/src/game/java/net/minecraft/block/properties/PropertyBool.java similarity index 100% rename from src/main/java/net/minecraft/block/properties/PropertyBool.java rename to src/game/java/net/minecraft/block/properties/PropertyBool.java diff --git a/src/main/java/net/minecraft/block/properties/PropertyDirection.java b/src/game/java/net/minecraft/block/properties/PropertyDirection.java similarity index 100% rename from src/main/java/net/minecraft/block/properties/PropertyDirection.java rename to src/game/java/net/minecraft/block/properties/PropertyDirection.java diff --git a/src/main/java/net/minecraft/block/properties/PropertyEnum.java b/src/game/java/net/minecraft/block/properties/PropertyEnum.java similarity index 100% rename from src/main/java/net/minecraft/block/properties/PropertyEnum.java rename to src/game/java/net/minecraft/block/properties/PropertyEnum.java diff --git a/src/main/java/net/minecraft/block/properties/PropertyHelper.java b/src/game/java/net/minecraft/block/properties/PropertyHelper.java similarity index 100% rename from src/main/java/net/minecraft/block/properties/PropertyHelper.java rename to src/game/java/net/minecraft/block/properties/PropertyHelper.java diff --git a/src/main/java/net/minecraft/block/properties/PropertyInteger.java b/src/game/java/net/minecraft/block/properties/PropertyInteger.java similarity index 100% rename from src/main/java/net/minecraft/block/properties/PropertyInteger.java rename to src/game/java/net/minecraft/block/properties/PropertyInteger.java diff --git a/src/main/java/net/minecraft/block/state/BlockPistonStructureHelper.java b/src/game/java/net/minecraft/block/state/BlockPistonStructureHelper.java similarity index 100% rename from src/main/java/net/minecraft/block/state/BlockPistonStructureHelper.java rename to src/game/java/net/minecraft/block/state/BlockPistonStructureHelper.java diff --git a/src/main/java/net/minecraft/block/state/BlockState.java b/src/game/java/net/minecraft/block/state/BlockState.java similarity index 100% rename from src/main/java/net/minecraft/block/state/BlockState.java rename to src/game/java/net/minecraft/block/state/BlockState.java diff --git a/src/main/java/net/minecraft/block/state/BlockStateBase.java b/src/game/java/net/minecraft/block/state/BlockStateBase.java similarity index 100% rename from src/main/java/net/minecraft/block/state/BlockStateBase.java rename to src/game/java/net/minecraft/block/state/BlockStateBase.java diff --git a/src/main/java/net/minecraft/block/state/BlockWorldState.java b/src/game/java/net/minecraft/block/state/BlockWorldState.java similarity index 100% rename from src/main/java/net/minecraft/block/state/BlockWorldState.java rename to src/game/java/net/minecraft/block/state/BlockWorldState.java diff --git a/src/main/java/net/minecraft/block/state/IBlockState.java b/src/game/java/net/minecraft/block/state/IBlockState.java similarity index 100% rename from src/main/java/net/minecraft/block/state/IBlockState.java rename to src/game/java/net/minecraft/block/state/IBlockState.java diff --git a/src/main/java/net/minecraft/block/state/pattern/BlockHelper.java b/src/game/java/net/minecraft/block/state/pattern/BlockHelper.java similarity index 100% rename from src/main/java/net/minecraft/block/state/pattern/BlockHelper.java rename to src/game/java/net/minecraft/block/state/pattern/BlockHelper.java diff --git a/src/main/java/net/minecraft/block/state/pattern/BlockPattern.java b/src/game/java/net/minecraft/block/state/pattern/BlockPattern.java similarity index 100% rename from src/main/java/net/minecraft/block/state/pattern/BlockPattern.java rename to src/game/java/net/minecraft/block/state/pattern/BlockPattern.java diff --git a/src/main/java/net/minecraft/block/state/pattern/BlockStateHelper.java b/src/game/java/net/minecraft/block/state/pattern/BlockStateHelper.java similarity index 100% rename from src/main/java/net/minecraft/block/state/pattern/BlockStateHelper.java rename to src/game/java/net/minecraft/block/state/pattern/BlockStateHelper.java diff --git a/src/main/java/net/minecraft/block/state/pattern/FactoryBlockPattern.java b/src/game/java/net/minecraft/block/state/pattern/FactoryBlockPattern.java similarity index 100% rename from src/main/java/net/minecraft/block/state/pattern/FactoryBlockPattern.java rename to src/game/java/net/minecraft/block/state/pattern/FactoryBlockPattern.java diff --git a/src/main/java/net/minecraft/client/ClientBrandRetriever.java b/src/game/java/net/minecraft/client/ClientBrandRetriever.java similarity index 100% rename from src/main/java/net/minecraft/client/ClientBrandRetriever.java rename to src/game/java/net/minecraft/client/ClientBrandRetriever.java diff --git a/src/main/java/net/minecraft/client/LoadingScreenRenderer.java b/src/game/java/net/minecraft/client/LoadingScreenRenderer.java similarity index 92% rename from src/main/java/net/minecraft/client/LoadingScreenRenderer.java rename to src/game/java/net/minecraft/client/LoadingScreenRenderer.java index e00524c..3a31d93 100644 --- a/src/main/java/net/minecraft/client/LoadingScreenRenderer.java +++ b/src/game/java/net/minecraft/client/LoadingScreenRenderer.java @@ -56,11 +56,9 @@ public class LoadingScreenRenderer implements IProgressUpdate { */ private long systemTime = Minecraft.getSystemTime(); private boolean field_73724_e; - private ScaledResolution scaledResolution; public LoadingScreenRenderer(Minecraft mcIn) { this.mc = mcIn; - this.scaledResolution = new ScaledResolution(mcIn); } /** @@ -93,9 +91,8 @@ public class LoadingScreenRenderer implements IProgressUpdate { GlStateManager.clear(GL_DEPTH_BUFFER_BIT); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - GlStateManager.ortho(0.0D, scaledresolution.getScaledWidth_double(), - scaledresolution.getScaledHeight_double(), 0.0D, 100.0D, 300.0D); + GlStateManager.ortho(0.0D, mc.scaledResolution.getScaledWidth_double(), + mc.scaledResolution.getScaledHeight_double(), 0.0D, 100.0D, 300.0D); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.loadIdentity(); GlStateManager.translate(0.0F, 0.0F, -200.0F); @@ -152,7 +149,7 @@ public class LoadingScreenRenderer implements IProgressUpdate { long i = Minecraft.getSystemTime(); if (i - this.systemTime >= 100L) { this.systemTime = i; - ScaledResolution scaledresolution = new ScaledResolution(this.mc); + ScaledResolution scaledresolution = mc.scaledResolution; int j = scaledresolution.getScaleFactor(); int k = scaledresolution.getScaledWidth(); int l = scaledresolution.getScaledHeight(); @@ -207,9 +204,11 @@ public class LoadingScreenRenderer implements IProgressUpdate { this.mc.fontRendererObj.drawStringWithShadow(this.currentlyDisplayedText, (float) ((k - this.mc.fontRendererObj.getStringWidth(this.currentlyDisplayedText)) / 2), (float) (l / 2 - 4 - 16), 16777215); - this.mc.fontRendererObj.drawStringWithShadow(this.message, - (float) ((k - this.mc.fontRendererObj.getStringWidth(this.message)) / 2), - (float) (l / 2 - 4 + 8), 16777215); + if (this.message != null) { + this.mc.fontRendererObj.drawStringWithShadow(this.message, + (float) ((k - this.mc.fontRendererObj.getStringWidth(this.message)) / 2), + (float) (l / 2 - 4 + 8), 16777215); + } this.mc.updateDisplay(); try { diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/game/java/net/minecraft/client/Minecraft.java similarity index 80% rename from src/main/java/net/minecraft/client/Minecraft.java rename to src/game/java/net/minecraft/client/Minecraft.java index ff9b9e4..7b6c6de 100644 --- a/src/main/java/net/minecraft/client/Minecraft.java +++ b/src/game/java/net/minecraft/client/Minecraft.java @@ -1,2418 +1,2527 @@ -package net.minecraft.client; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL._wglBindFramebuffer; - -import java.io.IOException; -import java.io.InputStream; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Callable; - -import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; - -import org.apache.commons.lang3.Validate; - -import com.google.common.collect.Lists; - -import net.hoosiertransfer.CullingMod; -import net.hoosiertransfer.Alfheim.ILightUpdatesProcessor; -import net.hoosiertransfer.Config; - -import net.lax1dude.eaglercraft.v1_8.Display; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; -import net.lax1dude.eaglercraft.v1_8.HString; -import net.lax1dude.eaglercraft.v1_8.IOUtils; -import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.lax1dude.eaglercraft.v1_8.Mouse; -import net.lax1dude.eaglercraft.v1_8.futures.Executors; -import net.lax1dude.eaglercraft.v1_8.futures.FutureTask; -import net.lax1dude.eaglercraft.v1_8.futures.ListenableFuture; -import net.lax1dude.eaglercraft.v1_8.futures.ListenableFutureTask; -import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFontRenderer; -import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; -import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; -import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.BlockVertexIDs; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DebugFramebufferView; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.EaglerDeferredPipeline; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ShaderPackInfoReloadListener; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.EmissiveItems; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.MetalsLUT; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.PBRTextureMapUtils; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.TemperaturesLUT; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.profile.GuiScreenEditProfile; -import net.lax1dude.eaglercraft.v1_8.profile.SkinPreviewRenderer; -import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; -import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; -import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; -import net.lax1dude.eaglercraft.v1_8.sp.IntegratedServerState; -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.lax1dude.eaglercraft.v1_8.sp.SkullCommand; -import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenDemoIntegratedServerStartup; -import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenIntegratedServerBusy; -import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenSingleplayerConnecting; -import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; -import net.lax1dude.eaglercraft.v1_8.update.RelayUpdateChecker; -import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceOverlay; -import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; -import net.minecraft.block.Block; -import net.minecraft.block.material.Material; -import net.minecraft.client.audio.MusicTicker; -import net.minecraft.client.audio.SoundHandler; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.GuiChat; -import net.minecraft.client.gui.GuiControls; -import net.minecraft.client.gui.GuiGameOver; -import net.minecraft.client.gui.GuiIngame; -import net.minecraft.client.gui.GuiIngameMenu; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.GuiSleepMP; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.gui.achievement.GuiAchievement; -import net.minecraft.client.gui.inventory.GuiInventory; -import net.minecraft.client.main.GameConfiguration; -import net.minecraft.client.multiplayer.GuiConnecting; -import net.minecraft.client.multiplayer.PlayerControllerMP; -import net.minecraft.client.multiplayer.ServerAddress; -import net.minecraft.client.multiplayer.ServerData; -import net.minecraft.client.multiplayer.ServerList; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.client.particle.EffectRenderer; -import net.minecraft.client.renderer.BlockRendererDispatcher; -import net.minecraft.client.renderer.EntityRenderer; -import net.minecraft.client.renderer.ItemRenderer; -import net.minecraft.client.renderer.RenderGlobal; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.chunk.RenderChunk; -import net.minecraft.client.renderer.entity.RenderItem; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.resources.DefaultResourcePack; -import net.minecraft.client.resources.FoliageColorReloadListener; -import net.minecraft.client.resources.GrassColorReloadListener; -import net.minecraft.client.resources.I18n; -import net.minecraft.client.resources.IReloadableResourceManager; -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.client.resources.IResourcePack; -import net.minecraft.client.resources.LanguageManager; -import net.minecraft.client.resources.ResourcePackRepository; -import net.minecraft.client.resources.SimpleReloadableResourceManager; -import net.minecraft.client.resources.data.AnimationMetadataSection; -import net.minecraft.client.resources.data.AnimationMetadataSectionSerializer; -import net.minecraft.client.resources.data.FontMetadataSection; -import net.minecraft.client.resources.data.FontMetadataSectionSerializer; -import net.minecraft.client.resources.data.IMetadataSerializer; -import net.minecraft.client.resources.data.LanguageMetadataSection; -import net.minecraft.client.resources.data.LanguageMetadataSectionSerializer; -import net.minecraft.client.resources.data.PackMetadataSection; -import net.minecraft.client.resources.data.PackMetadataSectionSerializer; -import net.minecraft.client.resources.data.TextureMetadataSection; -import net.minecraft.client.resources.data.TextureMetadataSectionSerializer; -import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.client.settings.GameSettings; -import net.minecraft.client.settings.KeyBinding; -import net.minecraft.crash.CrashReport; -import net.minecraft.crash.CrashReportCategory; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLeashKnot; -import net.minecraft.entity.EntityList; -import net.minecraft.entity.boss.BossStatus; -import net.minecraft.entity.item.EntityArmorStand; -import net.minecraft.entity.item.EntityBoat; -import net.minecraft.entity.item.EntityItemFrame; -import net.minecraft.entity.item.EntityMinecart; -import net.minecraft.entity.item.EntityPainting; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.event.ClickEvent; -import net.minecraft.init.Bootstrap; -import net.minecraft.init.Items; -import net.minecraft.item.Item; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.nbt.NBTTagString; -import net.minecraft.network.play.client.C16PacketClientStatus; -import net.minecraft.profiler.Profiler; -import net.minecraft.stats.AchievementList; -import net.minecraft.stats.IStatStringFormat; -import net.minecraft.stats.StatFileWriter; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.BlockPos; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.ChatComponentTranslation; -import net.minecraft.util.ChatStyle; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.FrameTimer; -import net.minecraft.util.IThreadListener; -import net.minecraft.util.MathHelper; -import net.minecraft.util.MinecraftError; -import net.minecraft.util.MouseHelper; -import net.minecraft.util.MovementInputFromOptions; -import net.minecraft.util.MovingObjectPosition; -import net.minecraft.util.ReportedException; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.ScreenShotHelper; -import net.minecraft.util.Session; -import net.minecraft.util.StringTranslate; -import net.minecraft.util.Timer; -import net.minecraft.util.Util; -import net.minecraft.world.EnumDifficulty; -import net.minecraft.world.WorldProviderEnd; -import net.minecraft.world.WorldProviderHell; -import net.minecraft.world.WorldSettings; -import net.minecraft.world.storage.ISaveFormat; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class Minecraft implements IThreadListener { - private static final Logger logger = LogManager.getLogger(); - private static final ResourceLocation locationMojangPng = new ResourceLocation("textures/gui/title/mojang.png"); - public static final boolean isRunningOnMac = false; - private ServerData currentServerData; - private TextureManager renderEngine; - private static Minecraft theMinecraft; - public PlayerControllerMP playerController; - private boolean fullscreen; - private boolean enableGLErrorChecking = true; - private boolean hasCrashed; - private CrashReport crashReporter; - public int displayWidth; - public int displayHeight; - private boolean field_181541_X = false; - private Timer timer = new Timer(20.0F); - public WorldClient theWorld; - public RenderGlobal renderGlobal; - private RenderManager renderManager; - private RenderItem renderItem; - private ItemRenderer itemRenderer; - public EntityPlayerSP thePlayer; - private Entity renderViewEntity; - public Entity pointedEntity; - public EffectRenderer effectRenderer; - private final Session session; - private boolean isGamePaused; - private boolean wasPaused; - public FontRenderer fontRendererObj; - public FontRenderer standardGalacticFontRenderer; - public GuiScreen currentScreen; - public LoadingScreenRenderer loadingScreen; - public EntityRenderer entityRenderer; - private int leftClickCounter; - private int tempDisplayWidth; - private int tempDisplayHeight; - public GuiAchievement guiAchievement; - public GuiIngame ingameGUI; - public boolean skipRenderWorld; - public MovingObjectPosition objectMouseOver; - public GameSettings gameSettings; - public MouseHelper mouseHelper; - private final String launchedVersion; - private static int debugFPS; - private int rightClickDelayTimer; - private String serverName; - private int serverPort; - public boolean inGameHasFocus; - long systemTime = getSystemTime(); - private int joinPlayerCounter; - public final FrameTimer field_181542_y = new FrameTimer(); - long field_181543_z = System.nanoTime(); - private final boolean jvm64bit; - private EaglercraftNetworkManager myNetworkManager; - private boolean integratedServerIsRunning; - /** - * + - * The profiler instance - */ - public final Profiler mcProfiler = new Profiler(); - /** - * + - * Keeps track of how long the debug crash keycombo (F3+C) has - * been pressed for, in order to crash after 10 seconds. - */ - private long debugCrashKeyPressTime = -1L; - private IReloadableResourceManager mcResourceManager; - private final IMetadataSerializer metadataSerializer_ = new IMetadataSerializer(); - private final List defaultResourcePacks = Lists.newArrayList(); - private final DefaultResourcePack mcDefaultResourcePack; - private ResourcePackRepository mcResourcePackRepository; - private LanguageManager mcLanguageManager; - private TextureMap textureMapBlocks; - private SoundHandler mcSoundHandler; - private MusicTicker mcMusicTicker; - private ResourceLocation mojangLogo; - private final List> scheduledTasks = new LinkedList(); - private long field_175615_aJ = 0L; - private final Thread mcThread = Thread.currentThread(); - private ModelManager modelManager; - private BlockRendererDispatcher blockRenderDispatcher; - /** - * + - * Set to true to keep the game loop running. Set to false by - * shutdown() to allow the game loop to exit cleanly. - */ - volatile boolean running = true; - /** - * + - * String that shows the debug information - */ - public String debug = ""; - public boolean field_175613_B = false; - public boolean field_175614_C = false; - public boolean field_175611_D = false; - public boolean renderChunksMany = true; - long debugUpdateTime = getSystemTime(); - int fpsCounter; - long prevFrameTime = -1L; - /** - * + - * Profiler currently displayed in the debug screen pie chart - */ - private String debugProfilerName = "root"; - public int joinWorldTickCounter = 0; - private int dontPauseTimer = 0; - public int bungeeOutdatedMsgTimer = 0; - private boolean isLANOpen = false; - - public SkullCommand eagskullCommand; - - public GuiVoiceOverlay voiceOverlay; - - public float startZoomValue = 18.0f; - public float adjustedZoomValue = 18.0f; - public boolean isZoomKey = false; - - public Minecraft(GameConfiguration gameConfig) { - theMinecraft = this; - StringTranslate.initClient(); - this.launchedVersion = gameConfig.gameInfo.version; - this.mcDefaultResourcePack = new DefaultResourcePack(); - this.session = gameConfig.userInfo.session; - logger.info("Setting user: " + this.session.getProfile().getName()); - this.displayWidth = gameConfig.displayInfo.width > 0 ? gameConfig.displayInfo.width : 1; - this.displayHeight = gameConfig.displayInfo.height > 0 ? gameConfig.displayInfo.height : 1; - this.tempDisplayWidth = gameConfig.displayInfo.width; - this.tempDisplayHeight = gameConfig.displayInfo.height; - this.fullscreen = gameConfig.displayInfo.fullscreen; - this.jvm64bit = isJvm64bit(); - String serverToJoin = EagRuntime.getConfiguration().getServerToJoin(); - if (serverToJoin != null) { - ServerAddress addr = AddressResolver.resolveAddressFromURI(serverToJoin); - this.serverName = addr.getIP(); - this.serverPort = addr.getPort(); - } - - Bootstrap.register(); - } - - public boolean isCallingFromMinecraftThread() { - return Thread.currentThread() == this.mcThread; - } - - public void run() { - CullingMod.intialize(); - this.running = true; - - try { - this.startGame(); - } catch (Throwable throwable) { - CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Initializing game"); - crashreport.makeCategory("Initialization"); - this.displayCrashReport(this.addGraphicsAndWorldToCrashReport(crashreport)); - return; - } - - try { - while (true) { - if (!this.running) { - break; - } - - if (!this.hasCrashed || this.crashReporter == null) { - this.runGameLoop(); - continue; - } - - this.displayCrashReport(this.crashReporter); - } - } catch (MinecraftError var12) { - // ?? - } catch (ReportedException reportedexception) { - this.addGraphicsAndWorldToCrashReport(reportedexception.getCrashReport()); - logger.fatal("Reported exception thrown!", reportedexception); - this.displayCrashReport(reportedexception.getCrashReport()); - } catch (Throwable throwable1) { - CrashReport crashreport1 = this - .addGraphicsAndWorldToCrashReport(new CrashReport("Unexpected error", throwable1)); - logger.fatal("Unreported exception thrown!", throwable1); - this.displayCrashReport(crashreport1); - } finally { - this.shutdownMinecraftApplet(); - } - - } - - /** - * + - * Starts the game: initializes the canvas, the title, the - * settings, etcetera. - */ - private void startGame() throws IOException { - this.gameSettings = new GameSettings(this); - this.defaultResourcePacks.add(this.mcDefaultResourcePack); - if (this.gameSettings.overrideHeight > 0 && this.gameSettings.overrideWidth > 0) { - this.displayWidth = this.gameSettings.overrideWidth; - this.displayHeight = this.gameSettings.overrideHeight; - } - - logger.info("EagRuntime Version: " + EagRuntime.getVersion()); - this.createDisplay(); - this.registerMetadataSerializers(); - EaglerFolderResourcePack.deleteOldResourcePacks(EaglerFolderResourcePack.SERVER_RESOURCE_PACKS, 604800000L); - this.mcResourcePackRepository = new ResourcePackRepository(this.mcDefaultResourcePack, this.metadataSerializer_, - this.gameSettings); - this.mcResourceManager = new SimpleReloadableResourceManager(this.metadataSerializer_); - this.mcLanguageManager = new LanguageManager(this.metadataSerializer_, this.gameSettings.language); - this.mcResourceManager.registerReloadListener(this.mcLanguageManager); - this.refreshResources(); - this.renderEngine = new TextureManager(this.mcResourceManager); - this.mcResourceManager.registerReloadListener(this.renderEngine); - this.drawSplashScreen(this.renderEngine); - this.mcSoundHandler = new SoundHandler(this.mcResourceManager, this.gameSettings); - this.mcResourceManager.registerReloadListener(this.mcSoundHandler); - this.mcMusicTicker = new MusicTicker(this); - this.fontRendererObj = new EaglerFontRenderer(this.gameSettings, - new ResourceLocation("textures/font/ascii.png"), this.renderEngine, false); - if (this.gameSettings.language != null) { - this.fontRendererObj.setUnicodeFlag(this.isUnicode()); - this.fontRendererObj.setBidiFlag(this.mcLanguageManager.isCurrentLanguageBidirectional()); - } - - this.standardGalacticFontRenderer = new EaglerFontRenderer(this.gameSettings, - new ResourceLocation("textures/font/ascii_sga.png"), this.renderEngine, false); - this.mcResourceManager.registerReloadListener(this.fontRendererObj); - this.mcResourceManager.registerReloadListener(this.standardGalacticFontRenderer); - this.mcResourceManager.registerReloadListener(new GrassColorReloadListener()); - this.mcResourceManager.registerReloadListener(new FoliageColorReloadListener()); - this.mcResourceManager.registerReloadListener(new ShaderPackInfoReloadListener()); - this.mcResourceManager.registerReloadListener(PBRTextureMapUtils.blockMaterialConstants); - this.mcResourceManager.registerReloadListener(new TemperaturesLUT()); - this.mcResourceManager.registerReloadListener(new MetalsLUT()); - this.mcResourceManager.registerReloadListener(new EmissiveItems()); - this.mcResourceManager.registerReloadListener(new BlockVertexIDs()); - this.mcResourceManager.registerReloadListener(new EaglerMeshLoader()); - AchievementList.openInventory.setStatStringFormatter(new IStatStringFormat() { - public String formatString(String parString1) { - try { - return HString.format(parString1, new Object[] { GameSettings - .getKeyDisplayString(Minecraft.this.gameSettings.keyBindInventory.getKeyCode()) }); - } catch (Exception exception) { - return "Error: " + exception.getLocalizedMessage(); - } - } - }); - this.mouseHelper = new MouseHelper(); - this.checkGLError("Pre startup"); - GlStateManager.enableTexture2D(); - GlStateManager.shadeModel(GL_SMOOTH); - GlStateManager.clearDepth(1.0f); - GlStateManager.enableDepth(); - GlStateManager.depthFunc(GL_LEQUAL); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(GL_GREATER, 0.1F); - GlStateManager.cullFace(GL_BACK); - GlStateManager.matrixMode(GL_PROJECTION); - GlStateManager.loadIdentity(); - GlStateManager.matrixMode(GL_MODELVIEW); - this.checkGLError("Startup"); - this.textureMapBlocks = new TextureMap("textures"); - this.textureMapBlocks.setEnablePBREagler(gameSettings.shaders); - this.textureMapBlocks.setMipmapLevels(this.gameSettings.mipmapLevels); - this.renderEngine.loadTickableTexture(TextureMap.locationBlocksTexture, this.textureMapBlocks); - this.renderEngine.bindTexture(TextureMap.locationBlocksTexture); - this.textureMapBlocks.setBlurMipmapDirect(false, this.gameSettings.mipmapLevels > 0); - this.modelManager = new ModelManager(this.textureMapBlocks); - this.mcResourceManager.registerReloadListener(this.modelManager); - this.renderItem = new RenderItem(this.renderEngine, this.modelManager); - this.renderManager = new RenderManager(this.renderEngine, this.renderItem); - this.itemRenderer = new ItemRenderer(this); - this.mcResourceManager.registerReloadListener(this.renderItem); - this.entityRenderer = new EntityRenderer(this, this.mcResourceManager); - this.mcResourceManager.registerReloadListener(this.entityRenderer); - this.blockRenderDispatcher = new BlockRendererDispatcher(this.modelManager.getBlockModelShapes(), - this.gameSettings); - this.mcResourceManager.registerReloadListener(this.blockRenderDispatcher); - this.renderGlobal = new RenderGlobal(this); - this.mcResourceManager.registerReloadListener(this.renderGlobal); - this.guiAchievement = new GuiAchievement(this); - GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); - this.effectRenderer = new EffectRenderer(this.theWorld, this.renderEngine); - SkinPreviewRenderer.initialize(); - this.checkGLError("Post startup"); - this.ingameGUI = new GuiIngame(this); - this.eagskullCommand = new SkullCommand(this); - this.voiceOverlay = new GuiVoiceOverlay(this); - ScaledResolution voiceRes = new ScaledResolution(this); - this.voiceOverlay.setResolution(voiceRes.getScaledWidth(), voiceRes.getScaledHeight()); - - ServerList.initServerList(this); - EaglerProfile.read(); - - GuiScreen mainMenu = new GuiMainMenu(); - if (isDemo()) { - mainMenu = new GuiScreenDemoIntegratedServerStartup(mainMenu); - } - if (this.serverName != null) { - mainMenu = new GuiConnecting(mainMenu, this, this.serverName, this.serverPort); - } - - this.displayGuiScreen(new GuiScreenEditProfile(mainMenu)); - - this.renderEngine.deleteTexture(this.mojangLogo); - this.mojangLogo = null; - this.loadingScreen = new LoadingScreenRenderer(this); - this.enableGLErrorChecking = false; // Hoosiertransfer mod - } - - private void registerMetadataSerializers() { - this.metadataSerializer_.registerMetadataSectionType(new TextureMetadataSectionSerializer(), - TextureMetadataSection.class); - this.metadataSerializer_.registerMetadataSectionType(new FontMetadataSectionSerializer(), - FontMetadataSection.class); - this.metadataSerializer_.registerMetadataSectionType(new AnimationMetadataSectionSerializer(), - AnimationMetadataSection.class); - this.metadataSerializer_.registerMetadataSectionType(new PackMetadataSectionSerializer(), - PackMetadataSection.class); - this.metadataSerializer_.registerMetadataSectionType(new LanguageMetadataSectionSerializer(), - LanguageMetadataSection.class); - } - - private void initStream() { - throw new UnsupportedOperationException("wtf u trying to twitch stream in a browser game?"); - } - - private void createDisplay() { - Display.create(); - Display.setTitle("Eaglercraft 1.9.4"); - } - - private static boolean isJvm64bit() { - return true; - } - - public String getVersion() { - return this.launchedVersion; - } - - public void crashed(CrashReport crash) { - this.hasCrashed = true; - this.crashReporter = crash; - } - - /** - * + - * Wrapper around displayCrashReportInternal - */ - public void displayCrashReport(CrashReport crashReportIn) { - String report = crashReportIn.getCompleteReport(); - Bootstrap.printToSYSOUT(report); - PlatformRuntime.writeCrashReport(report); - if (PlatformRuntime.getPlatformType() == EnumPlatformType.JAVASCRIPT) { - System.err.println( - "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); - System.err.println("NATIVE BROWSER EXCEPTION:"); - if (!PlatformRuntime.printJSExceptionIfBrowser(crashReportIn.getCrashCause())) { - System.err.println(""); - } - System.err.println( - "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); - } - } - - public boolean isUnicode() { - return this.mcLanguageManager.isCurrentLocaleUnicode() || this.gameSettings.forceUnicodeFont; - } - - public void refreshResources() { - GlStateManager.recompileShaders(); - - ArrayList arraylist = Lists.newArrayList(this.defaultResourcePacks); - - for (ResourcePackRepository.Entry resourcepackrepository$entry : this.mcResourcePackRepository - .getRepositoryEntries()) { - arraylist.add(resourcepackrepository$entry.getResourcePack()); - } - - if (this.mcResourcePackRepository.getResourcePackInstance() != null) { - arraylist.add(this.mcResourcePackRepository.getResourcePackInstance()); - } - - try { - this.mcResourceManager.reloadResources(arraylist); - } catch (RuntimeException runtimeexception) { - logger.info("Caught error stitching, removing all assigned resourcepacks"); - logger.info(runtimeexception); - arraylist.clear(); - arraylist.addAll(this.defaultResourcePacks); - this.mcResourcePackRepository.setRepositories(Collections.emptyList()); - this.mcResourceManager.reloadResources(arraylist); - this.gameSettings.resourcePacks.clear(); - this.gameSettings.field_183018_l.clear(); - this.gameSettings.saveOptions(); - } - - ShaderSource.clearCache(); - GuiMainMenu.doResourceReloadHack(); - - this.mcLanguageManager.parseLanguageMetadata(arraylist); - if (this.renderGlobal != null) { - this.renderGlobal.loadRenderers(); - } - - } - - private void updateDisplayMode() { - this.displayWidth = Display.getWidth(); - this.displayHeight = Display.getHeight(); - } - - private void drawSplashScreen(TextureManager textureManagerInstance) { - Display.update(); - updateDisplayMode(); - GlStateManager.viewport(0, 0, displayWidth, displayHeight); - ScaledResolution scaledresolution = new ScaledResolution(this); - int i = scaledresolution.getScaleFactor(); - GlStateManager.matrixMode(GL_PROJECTION); - GlStateManager.loadIdentity(); - GlStateManager.ortho(0.0D, (double) scaledresolution.getScaledWidth(), - (double) scaledresolution.getScaledHeight(), 0.0D, 1000.0D, 3000.0D); - GlStateManager.matrixMode(GL_MODELVIEW); - GlStateManager.loadIdentity(); - GlStateManager.translate(0.0F, 0.0F, -2000.0F); - GlStateManager.disableLighting(); - GlStateManager.disableFog(); - GlStateManager.disableDepth(); - GlStateManager.enableTexture2D(); - InputStream inputstream = null; - - try { - inputstream = this.mcDefaultResourcePack.getInputStream(locationMojangPng); - this.mojangLogo = textureManagerInstance.getDynamicTextureLocation("logo", - new DynamicTexture(ImageData.loadImageFile(inputstream))); - textureManagerInstance.bindTexture(this.mojangLogo); - } catch (IOException ioexception) { - logger.error("Unable to load logo: " + locationMojangPng, ioexception); - } finally { - IOUtils.closeQuietly(inputstream); - } - - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); - worldrenderer.pos(0.0D, (double) this.displayHeight, 0.0D).tex(0.0D, 0.0D).color(255, 255, 255, 255) - .endVertex(); - worldrenderer.pos((double) this.displayWidth, (double) this.displayHeight, 0.0D).tex(0.0D, 0.0D) - .color(255, 255, 255, 255).endVertex(); - worldrenderer.pos((double) this.displayWidth, 0.0D, 0.0D).tex(0.0D, 0.0D).color(255, 255, 255, 255).endVertex(); - worldrenderer.pos(0.0D, 0.0D, 0.0D).tex(0.0D, 0.0D).color(255, 255, 255, 255).endVertex(); - tessellator.draw(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - short short1 = 256; - short short2 = 256; - this.func_181536_a((scaledresolution.getScaledWidth() - short1) / 2, - (scaledresolution.getScaledHeight() - short2) / 2, 0, 0, short1, short2, 255, 255, 255, 255); - GlStateManager.disableLighting(); - GlStateManager.disableFog(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(GL_GREATER, 0.1F); - this.updateDisplay(); - } - - public void func_181536_a(int parInt1, int parInt2, int parInt3, int parInt4, int parInt5, int parInt6, int parInt7, - int parInt8, int parInt9, int parInt10) { - float f = 0.00390625F; - float f1 = 0.00390625F; - WorldRenderer worldrenderer = Tessellator.getInstance().getWorldRenderer(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); - worldrenderer.pos((double) parInt1, (double) (parInt2 + parInt6), 0.0D) - .tex((double) ((float) parInt3 * f), (double) ((float) (parInt4 + parInt6) * f1)) - .color(parInt7, parInt8, parInt9, parInt10).endVertex(); - worldrenderer.pos((double) (parInt1 + parInt5), (double) (parInt2 + parInt6), 0.0D) - .tex((double) ((float) (parInt3 + parInt5) * f), (double) ((float) (parInt4 + parInt6) * f1)) - .color(parInt7, parInt8, parInt9, parInt10).endVertex(); - worldrenderer.pos((double) (parInt1 + parInt5), (double) parInt2, 0.0D) - .tex((double) ((float) (parInt3 + parInt5) * f), (double) ((float) parInt4 * f1)) - .color(parInt7, parInt8, parInt9, parInt10).endVertex(); - worldrenderer.pos((double) parInt1, (double) parInt2, 0.0D) - .tex((double) ((float) parInt3 * f), (double) ((float) parInt4 * f1)) - .color(parInt7, parInt8, parInt9, parInt10).endVertex(); - Tessellator.getInstance().draw(); - } - - /** - * + - * Sets the argument GuiScreen as the main (topmost visible) - * screen. - */ - public void displayGuiScreen(GuiScreen guiScreenIn) { - if (this.currentScreen != null) { - this.currentScreen.onGuiClosed(); - } - - if (guiScreenIn == null && this.theWorld == null) { - guiScreenIn = new GuiMainMenu(); - } else if (guiScreenIn == null && this.thePlayer.getHealth() <= 0.0F) { - guiScreenIn = new GuiGameOver(); - } - - if (guiScreenIn instanceof GuiMainMenu) { - this.gameSettings.showDebugInfo = false; - this.ingameGUI.getChatGUI().clearChatMessages(); - } - - this.currentScreen = (GuiScreen) guiScreenIn; - if (guiScreenIn != null) { - this.setIngameNotInFocus(); - ScaledResolution scaledresolution = new ScaledResolution(this); - int i = scaledresolution.getScaledWidth(); - int j = scaledresolution.getScaledHeight(); - ((GuiScreen) guiScreenIn).setWorldAndResolution(this, i, j); - this.skipRenderWorld = false; - } else { - this.mcSoundHandler.resumeSounds(); - this.setIngameFocus(); - } - - } - - public void shutdownIntegratedServer(GuiScreen cont) { - if (SingleplayerServerController.shutdownEaglercraftServer() - || SingleplayerServerController.getStatusState() == IntegratedServerState.WORLD_UNLOADING) { - displayGuiScreen(new GuiScreenIntegratedServerBusy(cont, "singleplayer.busy.stoppingIntegratedServer", - "singleplayer.failed.stoppingIntegratedServer", SingleplayerServerController::isReady)); - } else { - displayGuiScreen(cont); - } - } - - /** - * + - * Checks for an OpenGL error. If there is one, prints the error - * ID and error string. - */ - public void checkGLError(String message) { - if (this.enableGLErrorChecking) { - int i = EaglercraftGPU.glGetError(); - if (i != 0) { - String s = EaglercraftGPU.gluErrorString(i); - logger.error("########## GL ERROR ##########"); - logger.error("@ " + message); - logger.error(i + ": " + s); - } - - } - } - - /** - * + - * Shuts down the minecraft applet by stopping the resource - * downloads, and clearing up GL stuff; called when the - * application (or web page) is exited. - */ - public void shutdownMinecraftApplet() { - try { - logger.info("Stopping!"); - - try { - this.loadWorld((WorldClient) null); - } catch (Throwable var5) { - ; - } - - this.mcSoundHandler.unloadSounds(); - if (SingleplayerServerController.isWorldRunning()) { - SingleplayerServerController.shutdownEaglercraftServer(); - while (SingleplayerServerController.getStatusState() == IntegratedServerState.WORLD_UNLOADING) { - EagUtils.sleep(50l); - SingleplayerServerController.runTick(); - } - } - if (SingleplayerServerController.isIntegratedServerWorkerAlive() - && SingleplayerServerController.canKillWorker()) { - SingleplayerServerController.killWorker(); - EagUtils.sleep(50l); - } - } finally { - EagRuntime.destroy(); - if (!this.hasCrashed) { - EagRuntime.exit(); - } - - } - } - - /** - * + - * Called repeatedly from run() - */ - private void runGameLoop() throws IOException { - long i = System.nanoTime(); - this.mcProfiler.startSection("root"); - if (Display.isCloseRequested()) { - this.shutdown(); - } - - if (this.isGamePaused && this.theWorld != null) { - float f = this.timer.renderPartialTicks; - this.timer.updateTimer(); - this.timer.renderPartialTicks = f; - } else { - this.timer.updateTimer(); - } - - this.mcProfiler.startSection("scheduledExecutables"); - synchronized (this.scheduledTasks) { - while (!this.scheduledTasks.isEmpty()) { - Util.func_181617_a((FutureTask) this.scheduledTasks.remove(0), logger); - } - } - - this.mcProfiler.endSection(); - long l = System.nanoTime(); - this.mcProfiler.startSection("tick"); - - if (this.timer.elapsedTicks > 1) { - long watchdog = System.currentTimeMillis(); - for (int j = 0; j < this.timer.elapsedTicks; ++j) { - this.runTick(); - long millis = System.currentTimeMillis(); - if (millis - watchdog > 50l) { - watchdog = millis; - EagUtils.sleep(0l); - } - } - } else if (this.timer.elapsedTicks == 1) { - this.runTick(); - } - - this.mcProfiler.endStartSection("preRenderErrors"); - long i1 = System.nanoTime() - l; - this.checkGLError("Pre render"); - this.mcProfiler.endStartSection("sound"); - this.mcSoundHandler.setListener(this.thePlayer, this.timer.renderPartialTicks); - this.mcProfiler.endSection(); - this.mcProfiler.startSection("render"); - - if (!Display.contextLost()) { - this.mcProfiler.startSection("EaglercraftGPU_optimize"); - EaglercraftGPU.optimize(); - this.mcProfiler.endSection(); - _wglBindFramebuffer(0x8D40, null); - GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); - GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); - GlStateManager.pushMatrix(); - GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - this.mcProfiler.startSection("display"); - GlStateManager.enableTexture2D(); - if (this.thePlayer != null && this.thePlayer.isEntityInsideOpaqueBlock()) { - this.gameSettings.thirdPersonView = 0; - } - - this.mcProfiler.endSection(); - if (!this.skipRenderWorld) { - this.mcProfiler.endStartSection("gameRenderer"); - this.entityRenderer.func_181560_a(this.timer.renderPartialTicks, i); - this.mcProfiler.endSection(); - } - - this.mcProfiler.endSection(); - if (this.gameSettings.showDebugInfo && this.gameSettings.showDebugProfilerChart - && !this.gameSettings.hideGUI) { - if (!this.mcProfiler.profilingEnabled) { - this.mcProfiler.clearProfiling(); - } - - this.mcProfiler.profilingEnabled = true; - this.displayDebugInfo(i1); - } else { - this.mcProfiler.profilingEnabled = false; - this.prevFrameTime = System.nanoTime(); - } - - this.guiAchievement.updateAchievementWindow(); - GlStateManager.popMatrix(); - } - - this.mcProfiler.startSection("root"); - this.updateDisplay(); - this.checkGLError("Post render"); - - ++this.fpsCounter; - long k = System.nanoTime(); - this.field_181542_y.func_181747_a(k - this.field_181543_z); - this.field_181543_z = k; - - while (getSystemTime() >= this.debugUpdateTime + 1000L) { - debugFPS = this.fpsCounter; - this.debug = HString.format("%d fps (%d chunk update%s) T: %s%s%s%s", - new Object[] { Integer.valueOf(debugFPS), Integer.valueOf(RenderChunk.renderChunksUpdated), - RenderChunk.renderChunksUpdated != 1 ? "s" : "", - (float) this.gameSettings.limitFramerate == GameSettings.Options.FRAMERATE_LIMIT - .getValueMax() ? "inf" : Integer.valueOf(this.gameSettings.limitFramerate), - this.gameSettings.enableVsync ? " vsync" : "", - this.gameSettings.fancyGraphics ? "" : " fast", this.gameSettings.clouds == 0 ? "" - : (this.gameSettings.clouds == 1 ? " fast-clouds" : " fancy-clouds") }); - RenderChunk.renderChunksUpdated = 0; - this.debugUpdateTime += 1000L; - this.fpsCounter = 0; - } - - if (this.isFramerateLimitBelowMax()) { - this.mcProfiler.startSection("fpslimit_wait"); - Display.sync(this.getLimitFramerate()); - this.mcProfiler.endSection(); - } - - Mouse.tickCursorShape(); - this.mcProfiler.endSection(); - } - - public void updateDisplay() { - this.mcProfiler.startSection("display_update"); - if (Display.isVSyncSupported()) { - Display.setVSync(this.gameSettings.enableVsync); - } else { - this.gameSettings.enableVsync = false; - } - Display.update(); - this.mcProfiler.endSection(); - this.checkWindowResize(); - } - - protected void checkWindowResize() { - if (!this.fullscreen && Display.wasResized()) { - int i = this.displayWidth; - int j = this.displayHeight; - this.displayWidth = Display.getWidth(); - this.displayHeight = Display.getHeight(); - if (this.displayWidth != i || this.displayHeight != j) { - if (this.displayWidth <= 0) { - this.displayWidth = 1; - } - - if (this.displayHeight <= 0) { - this.displayHeight = 1; - } - - this.resize(this.displayWidth, this.displayHeight); - } - } - - } - - public int getLimitFramerate() { - return this.theWorld == null && this.currentScreen != null ? 30 : this.gameSettings.limitFramerate; - } - - public boolean isFramerateLimitBelowMax() { - return (float) this.getLimitFramerate() < GameSettings.Options.FRAMERATE_LIMIT.getValueMax(); - } - - /** - * + - * Update debugProfilerName in response to number keys in debug - * screen - */ - private void updateDebugProfilerName(int keyCount) { - List list = this.mcProfiler.getProfilingData(this.debugProfilerName); - if (list != null && !list.isEmpty()) { - Profiler.Result profiler$result = (Profiler.Result) list.remove(0); - if (keyCount == 0) { - if (profiler$result.field_76331_c.length() > 0) { - int i = this.debugProfilerName.lastIndexOf("."); - if (i >= 0) { - this.debugProfilerName = this.debugProfilerName.substring(0, i); - } - } - } else { - --keyCount; - if (keyCount < list.size() - && !((Profiler.Result) list.get(keyCount)).field_76331_c.equals("unspecified")) { - if (this.debugProfilerName.length() > 0) { - this.debugProfilerName = this.debugProfilerName + "."; - } - - this.debugProfilerName = this.debugProfilerName - + ((Profiler.Result) list.get(keyCount)).field_76331_c; - } - } - - } - } - - /** - * + - * Parameter appears to be unused - */ - private void displayDebugInfo(long elapsedTicksTime) { - if (this.mcProfiler.profilingEnabled) { - List list = this.mcProfiler.getProfilingData(this.debugProfilerName); - Profiler.Result profiler$result = (Profiler.Result) list.remove(0); - GlStateManager.clear(GL_DEPTH_BUFFER_BIT); - GlStateManager.matrixMode(GL_PROJECTION); - GlStateManager.enableColorMaterial(); - GlStateManager.loadIdentity(); - GlStateManager.ortho(0.0D, (double) this.displayWidth, (double) this.displayHeight, 0.0D, 1000.0D, 3000.0D); - GlStateManager.matrixMode(GL_MODELVIEW); - GlStateManager.loadIdentity(); - GlStateManager.translate(0.0F, 0.0F, -2000.0F); - EaglercraftGPU.glLineWidth(1.0F); - GlStateManager.disableTexture2D(); - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - short short1 = 160; - int i = this.displayWidth - short1 - 10; - int j = this.displayHeight - short1 * 2; - GlStateManager.enableBlend(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR); - worldrenderer.pos((double) ((float) i - (float) short1 * 1.1F), - (double) ((float) j - (float) short1 * 0.6F - 16.0F), 0.0D).color(0, 0, 0, 100).endVertex(); - worldrenderer.pos((double) ((float) i - (float) short1 * 1.1F), (double) (j + short1 * 2), 0.0D) - .color(0, 0, 0, 100).endVertex(); - worldrenderer.pos((double) ((float) i + (float) short1 * 1.1F), (double) (j + short1 * 2), 0.0D) - .color(0, 0, 0, 100).endVertex(); - worldrenderer.pos((double) ((float) i + (float) short1 * 1.1F), - (double) ((float) j - (float) short1 * 0.6F - 16.0F), 0.0D).color(0, 0, 0, 100).endVertex(); - tessellator.draw(); - GlStateManager.disableBlend(); - double d0 = 0.0D; - - for (int k = 0; k < list.size(); ++k) { - Profiler.Result profiler$result1 = (Profiler.Result) list.get(k); - int l = MathHelper.floor_double(profiler$result1.field_76332_a / 4.0D) + 1; - worldrenderer.begin(6, DefaultVertexFormats.POSITION_COLOR); - int i1 = profiler$result1.func_76329_a(); - int j1 = i1 >> 16 & 255; - int k1 = i1 >> 8 & 255; - int l1 = i1 & 255; - worldrenderer.pos((double) i, (double) j, 0.0D).color(j1, k1, l1, 255).endVertex(); - - for (int i2 = l; i2 >= 0; --i2) { - float f = (float) ((d0 + profiler$result1.field_76332_a * (double) i2 / (double) l) - * 3.1415927410125732D * 2.0D / 100.0D); - float f1 = MathHelper.sin(f) * (float) short1; - float f2 = MathHelper.cos(f) * (float) short1 * 0.5F; - worldrenderer.pos((double) ((float) i + f1), (double) ((float) j - f2), 0.0D).color(j1, k1, l1, 255) - .endVertex(); - } - - tessellator.draw(); - worldrenderer.begin(5, DefaultVertexFormats.POSITION_COLOR); - - for (int l2 = l; l2 >= 0; --l2) { - float f3 = (float) ((d0 + profiler$result1.field_76332_a * (double) l2 / (double) l) - * 3.1415927410125732D * 2.0D / 100.0D); - float f4 = MathHelper.sin(f3) * (float) short1; - float f5 = MathHelper.cos(f3) * (float) short1 * 0.5F; - worldrenderer.pos((double) ((float) i + f4), (double) ((float) j - f5), 0.0D) - .color(j1 >> 1, k1 >> 1, l1 >> 1, 255).endVertex(); - worldrenderer.pos((double) ((float) i + f4), (double) ((float) j - f5 + 10.0F), 0.0D) - .color(j1 >> 1, k1 >> 1, l1 >> 1, 255).endVertex(); - } - - tessellator.draw(); - d0 += profiler$result1.field_76332_a; - } - - DecimalFormat decimalformat = new DecimalFormat("##0.00"); - GlStateManager.enableTexture2D(); - String s = ""; - if (!profiler$result.field_76331_c.equals("unspecified")) { - s = s + "[0] "; - } - - if (profiler$result.field_76331_c.length() == 0) { - s = s + "ROOT "; - } else { - s = s + profiler$result.field_76331_c + " "; - } - - int k2 = 16777215; - this.fontRendererObj.drawStringWithShadow(s, (float) (i - short1), (float) (j - short1 / 2 - 16), k2); - this.fontRendererObj.drawStringWithShadow(s = decimalformat.format(profiler$result.field_76330_b) + "%", - (float) (i + short1 - this.fontRendererObj.getStringWidth(s)), (float) (j - short1 / 2 - 16), k2); - - for (int j2 = 0; j2 < list.size(); ++j2) { - Profiler.Result profiler$result2 = (Profiler.Result) list.get(j2); - String s1 = ""; - if (profiler$result2.field_76331_c.equals("unspecified")) { - s1 = s1 + "[?] "; - } else { - s1 = s1 + "[" + (j2 + 1) + "] "; - } - - s1 = s1 + profiler$result2.field_76331_c; - this.fontRendererObj.drawStringWithShadow(s1, (float) (i - short1), - (float) (j + short1 / 2 + j2 * 8 + 20), profiler$result2.func_76329_a()); - this.fontRendererObj.drawStringWithShadow( - s1 = decimalformat.format(profiler$result2.field_76332_a) + "%", - (float) (i + short1 - 50 - this.fontRendererObj.getStringWidth(s1)), - (float) (j + short1 / 2 + j2 * 8 + 20), profiler$result2.func_76329_a()); - this.fontRendererObj.drawStringWithShadow( - s1 = decimalformat.format(profiler$result2.field_76330_b) + "%", - (float) (i + short1 - this.fontRendererObj.getStringWidth(s1)), - (float) (j + short1 / 2 + j2 * 8 + 20), profiler$result2.func_76329_a()); - } - - } - } - - /** - * + - * Called when the window is closing. Sets 'running' to false - * which allows the game loop to exit cleanly. - */ - public void shutdown() { - this.running = false; - } - - /** - * + - * Will set the focus to ingame if the Minecraft window is the - * active with focus. Also clears any GUI screen currently - * displayed - */ - public void setIngameFocus() { - if (Display.isActive()) { - if (!this.inGameHasFocus) { - this.inGameHasFocus = true; - this.mouseHelper.grabMouseCursor(); - this.displayGuiScreen((GuiScreen) null); - this.leftClickCounter = 10000; - } - } - } - - /** - * + - * Resets the player keystate, disables the ingame focus, and - * ungrabs the mouse cursor. - */ - public void setIngameNotInFocus() { - if (this.inGameHasFocus) { - KeyBinding.unPressAllKeys(); - this.inGameHasFocus = false; - this.mouseHelper.ungrabMouseCursor(); - } - } - - /** - * + - * Displays the ingame menu - */ - public void displayInGameMenu() { - if (this.currentScreen == null) { - this.displayGuiScreen(new GuiIngameMenu()); - } - } - - private void sendClickBlockToController(boolean leftClick) { - if (!leftClick) { - this.leftClickCounter = 0; - } - - if (this.leftClickCounter <= 0 && !this.thePlayer.isUsingItem()) { - if (leftClick && this.objectMouseOver != null - && this.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { - BlockPos blockpos = this.objectMouseOver.getBlockPos(); - if (this.theWorld.getBlockState(blockpos).getBlock().getMaterial() != Material.air - && this.playerController.onPlayerDamageBlock(blockpos, this.objectMouseOver.sideHit)) { - this.effectRenderer.addBlockHitEffects(blockpos, this.objectMouseOver.sideHit); - this.thePlayer.swingItem(); - } - - } else { - this.playerController.resetBlockRemoving(); - } - } - } - - private void clickMouse() { - if (this.leftClickCounter <= 0) { - if (this.objectMouseOver == null) { - logger.error("Null returned as \'hitResult\', this shouldn\'t happen!"); - if (this.playerController.isNotCreative()) { - this.leftClickCounter = 10; - } - - } else { - switch (this.objectMouseOver.typeOfHit) { - case ENTITY: - this.playerController.attackEntity(this.thePlayer, this.objectMouseOver.entityHit); - break; - case BLOCK: - BlockPos blockpos = this.objectMouseOver.getBlockPos(); - if (this.theWorld.getBlockState(blockpos).getBlock().getMaterial() != Material.air) { - this.playerController.clickBlock(blockpos, this.objectMouseOver.sideHit); - break; - } - case MISS: - default: - if (this.playerController.isNotCreative()) { - this.leftClickCounter = 10; - } - - this.thePlayer.resetCooldown(); - } - - } - - this.thePlayer.swingItem(); - } - } - - /** - * + - * Called when user clicked he's mouse right button (place) - */ - private void rightClickMouse() { - if (!this.playerController.func_181040_m()) { - this.rightClickDelayTimer = 4; - boolean flag = true; - ItemStack itemstack = this.thePlayer.inventory.getCurrentItem(); - if (this.objectMouseOver == null) { - logger.warn("Null returned as \'hitResult\', this shouldn\'t happen!"); - } else { - switch (this.objectMouseOver.typeOfHit) { - case ENTITY: - if (this.playerController.func_178894_a(this.thePlayer, this.objectMouseOver.entityHit, - this.objectMouseOver)) { - flag = false; - } else if (this.playerController.interactWithEntitySendPacket(this.thePlayer, - this.objectMouseOver.entityHit)) { - flag = false; - } - break; - case BLOCK: - BlockPos blockpos = this.objectMouseOver.getBlockPos(); - if (this.theWorld.getBlockState(blockpos).getBlock().getMaterial() != Material.air) { - int i = itemstack != null ? itemstack.stackSize : 0; - if (this.playerController.onPlayerRightClick(this.thePlayer, this.theWorld, itemstack, - blockpos, - this.objectMouseOver.sideHit, this.objectMouseOver.hitVec)) { - flag = false; - this.thePlayer.swingItem(); - } - - if (itemstack == null) { - return; - } - - if (itemstack.stackSize == 0) { - this.thePlayer.inventory.mainInventory[this.thePlayer.inventory.currentItem] = null; - } else if (itemstack.stackSize != i || this.playerController.isInCreativeMode()) { - this.entityRenderer.itemRenderer.resetEquippedProgress(); - } - } - } - } - - if (flag) { - ItemStack itemstack1 = this.thePlayer.inventory.getCurrentItem(); - if (itemstack1 != null - && this.playerController.sendUseItem(this.thePlayer, this.theWorld, itemstack1)) { - this.entityRenderer.itemRenderer.resetEquippedProgress2(); - } - } - - } - } - - /** - * + - * Toggles fullscreen mode. - */ - public void toggleFullscreen() { - Display.toggleFullscreen(); - } - - /** - * + - * Called to resize the current screen. - */ - private void resize(int width, int height) { - this.displayWidth = Math.max(1, width); - this.displayHeight = Math.max(1, height); - ScaledResolution scaledresolution = new ScaledResolution(this); - if (this.currentScreen != null) { - this.currentScreen.onResize(this, scaledresolution.getScaledWidth(), scaledresolution.getScaledHeight()); - } - - this.loadingScreen = new LoadingScreenRenderer(this); - - this.voiceOverlay.setResolution(scaledresolution.getScaledWidth(), scaledresolution.getScaledHeight()); - } - - public MusicTicker func_181535_r() { - return this.mcMusicTicker; - } - - /** - * + - * Runs the current tick. - */ - public void runTick() throws IOException { - CullingMod.setRequestCull(true); - if (this.rightClickDelayTimer > 0) { - --this.rightClickDelayTimer; - } - - RateLimitTracker.tick(); - - boolean isHostingLAN = LANServerController.isHostingLAN(); - this.isGamePaused = !isHostingLAN && this.isSingleplayer() && this.theWorld != null && this.thePlayer != null - && this.currentScreen != null && this.currentScreen.doesGuiPauseGame(); - - if (isLANOpen && !isHostingLAN) { - ingameGUI.getChatGUI().printChatMessage(new ChatComponentTranslation("lanServer.relayDisconnected")); - } - isLANOpen = isHostingLAN; - - if (wasPaused != isGamePaused) { - SingleplayerServerController.setPaused(this.isGamePaused); - wasPaused = isGamePaused; - } - - SingleplayerServerController.runTick(); - RelayUpdateChecker.runTick(); - - this.mcProfiler.startSection("gui"); - if (!this.isGamePaused) { - this.ingameGUI.updateTick(); - } - - this.mcProfiler.endStartSection("eaglerVoice"); - VoiceClientController.tickVoiceClient(this); - - this.mcProfiler.endSection(); - this.entityRenderer.getMouseOver(1.0F); - this.mcProfiler.startSection("gameMode"); - if (!this.isGamePaused && this.theWorld != null) { - this.playerController.updateController(); - } - - this.mcProfiler.endStartSection("textures"); - if (!this.isGamePaused) { - this.renderEngine.tick(); - GlStateManager.viewport(0, 0, displayWidth, displayHeight); // to be safe - } - - if (this.currentScreen == null && this.thePlayer != null) { - if (this.thePlayer.getHealth() <= 0.0F) { - this.displayGuiScreen((GuiScreen) null); - } else if (this.thePlayer.isPlayerSleeping() && this.theWorld != null) { - this.displayGuiScreen(new GuiSleepMP()); - } - if (this.currentScreen == null && this.dontPauseTimer <= 0) { - if (!Mouse.isMouseGrabbed()) { - this.setIngameNotInFocus(); - this.displayInGameMenu(); - } - } - } else if (this.currentScreen != null && this.currentScreen instanceof GuiSleepMP - && !this.thePlayer.isPlayerSleeping()) { - this.displayGuiScreen((GuiScreen) null); - } - - if (this.currentScreen != null) { - this.leftClickCounter = 10000; - this.dontPauseTimer = 6; - } else { - if (this.dontPauseTimer > 0) { - --this.dontPauseTimer; - } - } - - if (this.currentScreen != null) { - try { - this.currentScreen.handleInput(); - } catch (Throwable throwable1) { - CrashReport crashreport = CrashReport.makeCrashReport(throwable1, "Updating screen events"); - CrashReportCategory crashreportcategory = crashreport.makeCategory("Affected screen"); - crashreportcategory.addCrashSectionCallable("Screen name", new Callable() { - public String call() throws Exception { - return Minecraft.this.currentScreen.getClass().getName(); - } - }); - throw new ReportedException(crashreport); - } - - if (this.currentScreen != null) { - try { - this.currentScreen.updateScreen(); - } catch (Throwable throwable) { - CrashReport crashreport1 = CrashReport.makeCrashReport(throwable, "Ticking screen"); - CrashReportCategory crashreportcategory1 = crashreport1.makeCategory("Affected screen"); - crashreportcategory1.addCrashSectionCallable("Screen name", new Callable() { - public String call() throws Exception { - return Minecraft.this.currentScreen.getClass().getName(); - } - }); - throw new ReportedException(crashreport1); - } - } - } - - if (this.currentScreen == null || this.currentScreen.allowUserInput) { - this.mcProfiler.endStartSection("mouse"); - - while (Mouse.next()) { - int i = Mouse.getEventButton(); - KeyBinding.setKeyBindState(i - 100, Mouse.getEventButtonState()); - if (Mouse.getEventButtonState()) { - if (this.thePlayer.isSpectator() && i == 2) { - this.ingameGUI.getSpectatorGui().func_175261_b(); - } else { - KeyBinding.onTick(i - 100); - } - } - - long i1 = getSystemTime() - this.systemTime; - if (i1 <= 200L) { - int j = Mouse.getEventDWheel(); - if (j != 0) { - if (this.isZoomKey) { - this.adjustedZoomValue = MathHelper.clamp_float(adjustedZoomValue - j * 4.0f, 5.0f, 32.0f); - } else if (this.thePlayer.isSpectator()) { - j = j < 0 ? -1 : 1; - if (this.ingameGUI.getSpectatorGui().func_175262_a()) { - this.ingameGUI.getSpectatorGui().func_175259_b(-j); - } else { - float f = MathHelper.clamp_float( - this.thePlayer.capabilities.getFlySpeed() + (float) j * 0.005F, 0.0F, 0.2F); - this.thePlayer.capabilities.setFlySpeed(f); - } - } else { - this.thePlayer.inventory.changeCurrentItem(j); - } - } - - if (this.currentScreen == null) { - if ((!this.inGameHasFocus || !Mouse.isActuallyGrabbed()) && Mouse.getEventButtonState()) { - this.inGameHasFocus = false; - this.setIngameFocus(); - } - } else if (this.currentScreen != null) { - this.currentScreen.handleMouseInput(); - } - } - } - - if (this.leftClickCounter > 0) { - --this.leftClickCounter; - } - - this.mcProfiler.endStartSection("keyboard"); - - while (Keyboard.next()) { - int k = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey(); - if (k == 0x1D && (areKeysLocked() || isFullScreen())) { - KeyBinding.setKeyBindState(gameSettings.keyBindSprint.getKeyCode(), Keyboard.getEventKeyState()); - } - KeyBinding.setKeyBindState(k, Keyboard.getEventKeyState()); - if (Keyboard.getEventKeyState()) { - KeyBinding.onTick(k); - } - - if (this.debugCrashKeyPressTime > 0L) { - if (getSystemTime() - this.debugCrashKeyPressTime >= 6000L) { - throw new ReportedException(new CrashReport("Manually triggered debug crash", new Throwable())); - } - - if (!Keyboard.isKeyDown(46) || !Keyboard.isKeyDown(61)) { - this.debugCrashKeyPressTime = -1L; - } - } else if (Keyboard.isKeyDown(46) && Keyboard.isKeyDown(61)) { - this.debugCrashKeyPressTime = getSystemTime(); - } - - this.dispatchKeypresses(); - if (Keyboard.getEventKeyState()) { - if (EaglerDeferredPipeline.instance != null) { - if (k == 62) { - DebugFramebufferView.toggleDebugView(); - } else if (k == 0xCB || k == 0xC8) { - DebugFramebufferView.switchView(-1); - } else if (k == 0xCD || k == 0xD0) { - DebugFramebufferView.switchView(1); - } - } - - if (this.currentScreen != null) { - this.currentScreen.handleKeyboardInput(); - } else { - if (k == 1 || (k > -1 && k == this.gameSettings.keyBindClose.getKeyCode())) { - this.displayInGameMenu(); - } - - if (k == 32 && Keyboard.isKeyDown(61) && this.ingameGUI != null) { - this.ingameGUI.getChatGUI().clearChatMessages(); - } - - if (k == 31 && Keyboard.isKeyDown(61)) { - this.refreshResources(); - } - - if (k == 19 && Keyboard.isKeyDown(61)) { // F3+R - if (gameSettings.shaders) { - ShaderSource.clearCache(); - this.renderGlobal.loadRenderers(); - } - } - - if (k == 17 && Keyboard.isKeyDown(61)) { - ; - } - - if (k == 18 && Keyboard.isKeyDown(61)) { - ; - } - - if (k == 47 && Keyboard.isKeyDown(61)) { - ; - } - - if (k == 38 && Keyboard.isKeyDown(61)) { - ; - } - - if (k == 22 && Keyboard.isKeyDown(61)) { - ; - } - - if (k == 20 && Keyboard.isKeyDown(61)) { - this.refreshResources(); - } - - if (k == 33 && Keyboard.isKeyDown(61)) { - this.gameSettings.setOptionValue(GameSettings.Options.RENDER_DISTANCE, - GuiScreen.isShiftKeyDown() ? -1 : 1); - } - - if (k == 30 && Keyboard.isKeyDown(61)) { - GlStateManager.recompileShaders(); - this.renderGlobal.loadRenderers(); - } - - if (k == 35 && Keyboard.isKeyDown(61)) { - this.gameSettings.advancedItemTooltips = !this.gameSettings.advancedItemTooltips; - this.gameSettings.saveOptions(); - } - - if (k == 48 && Keyboard.isKeyDown(61)) { - this.renderManager.setDebugBoundingBox(!this.renderManager.isDebugBoundingBox()); - } - - if (k == 25 && Keyboard.isKeyDown(61)) { - this.gameSettings.pauseOnLostFocus = !this.gameSettings.pauseOnLostFocus; - this.gameSettings.saveOptions(); - } - - if (k == 59) { - this.gameSettings.hideGUI = !this.gameSettings.hideGUI; - } - - if (k == 61) { - this.gameSettings.showDebugInfo = !this.gameSettings.showDebugInfo; - this.gameSettings.showDebugProfilerChart = GuiScreen.isShiftKeyDown(); - this.gameSettings.field_181657_aC = GuiScreen.isAltKeyDown(); - } - - if (this.gameSettings.keyBindTogglePerspective.isPressed()) { - ++this.gameSettings.thirdPersonView; - if (this.gameSettings.thirdPersonView > 2) { - this.gameSettings.thirdPersonView = 0; - } - - if (this.gameSettings.thirdPersonView == 0) { - this.entityRenderer.loadEntityShader(this.getRenderViewEntity()); - } else if (this.gameSettings.thirdPersonView == 1) { - this.entityRenderer.loadEntityShader((Entity) null); - } - - this.renderGlobal.setDisplayListEntitiesDirty(); - } - - if (this.gameSettings.keyBindSmoothCamera.isPressed()) { - this.gameSettings.smoothCamera = !this.gameSettings.smoothCamera; - } - } - - if (this.gameSettings.showDebugInfo && this.gameSettings.showDebugProfilerChart) { - if (k == 11) { - this.updateDebugProfilerName(0); - } - - for (int j1 = 0; j1 < 9; ++j1) { - if (k == 2 + j1) { - this.updateDebugProfilerName(j1 + 1); - } - } - } - } - } - - for (int l = 0; l < 9; ++l) { - if (this.gameSettings.keyBindsHotbar[l].isPressed()) { - if (this.thePlayer.isSpectator()) { - this.ingameGUI.getSpectatorGui().func_175260_a(l); - } else { - this.thePlayer.inventory.currentItem = l; - } - } - } - - boolean zoomKey = this.gameSettings.keyBindZoomCamera.isKeyDown(); - if (zoomKey != isZoomKey) { - adjustedZoomValue = startZoomValue; - isZoomKey = zoomKey; - } - - boolean flag = this.gameSettings.chatVisibility != EntityPlayer.EnumChatVisibility.HIDDEN; - - while (this.gameSettings.keyBindInventory.isPressed()) { - if (this.playerController.isRidingHorse()) { - this.thePlayer.sendHorseInventory(); - } else { - this.getNetHandler().addToSendQueue( - new C16PacketClientStatus(C16PacketClientStatus.EnumState.OPEN_INVENTORY_ACHIEVEMENT)); - this.displayGuiScreen(new GuiInventory(this.thePlayer)); - } - } - - while (this.gameSettings.keyBindDrop.isPressed()) { - if (!this.thePlayer.isSpectator()) { - this.thePlayer.dropOneItem(GuiScreen.isCtrlKeyDown()); - } - } - - while (this.gameSettings.keyBindChat.isPressed() && flag) { - this.displayGuiScreen(new GuiChat()); - } - - if (this.currentScreen == null && this.gameSettings.keyBindCommand.isPressed() && flag) { - this.displayGuiScreen(new GuiChat("/")); - } - - if (this.thePlayer.isUsingItem()) { - if (!this.gameSettings.keyBindUseItem.isKeyDown()) { - this.playerController.onStoppedUsingItem(this.thePlayer); - } - - while (this.gameSettings.keyBindAttack.isPressed()) { - ; - } - - while (this.gameSettings.keyBindUseItem.isPressed()) { - ; - } - - while (this.gameSettings.keyBindPickBlock.isPressed()) { - ; - } - } else { - while (this.gameSettings.keyBindAttack.isPressed()) { - this.clickMouse(); - } - - while (this.gameSettings.keyBindUseItem.isPressed()) { - this.rightClickMouse(); - } - - while (this.gameSettings.keyBindPickBlock.isPressed()) { - this.middleClickMouse(); - } - } - - if (this.gameSettings.keyBindUseItem.isKeyDown() && this.rightClickDelayTimer == 0 - && !this.thePlayer.isUsingItem()) { - this.rightClickMouse(); - } - - this.sendClickBlockToController( - this.currentScreen == null && this.gameSettings.keyBindAttack.isKeyDown() && this.inGameHasFocus); - } - - if (this.theWorld != null) { - if (this.thePlayer != null) { - ++this.joinPlayerCounter; - if (this.joinPlayerCounter == 30) { - this.joinPlayerCounter = 0; - this.theWorld.joinEntityInSurroundings(this.thePlayer); - } - } - - this.mcProfiler.endStartSection("gameRenderer"); - if (!this.isGamePaused) { - this.entityRenderer.updateRenderer(); - } - - this.mcProfiler.endStartSection("levelRenderer"); - if (!this.isGamePaused) { - this.renderGlobal.updateClouds(); - } - this.mcProfiler.endStartSection("processRenderGlobalLightUpdates"); - if (!isGamePaused) - ((ILightUpdatesProcessor) renderGlobal).alfheim$processLightUpdates(); - - this.mcProfiler.endStartSection("level"); - if (!this.isGamePaused) { - if (this.theWorld.getLastLightningBolt() > 0) { - this.theWorld.setLastLightningBolt(this.theWorld.getLastLightningBolt() - 1); - } - - this.theWorld.updateEntities(); - } - this.eagskullCommand.tick(); - } else if (this.entityRenderer.isShaderActive()) { - this.entityRenderer.func_181022_b(); - } - - if (!this.isGamePaused && Config.audioEnabled()) { - this.mcMusicTicker.update(); - this.mcSoundHandler.update(); - } - - if (this.theWorld != null) { - if (!this.isGamePaused) { - this.theWorld.setAllowedSpawnTypes(this.theWorld.getDifficulty() != EnumDifficulty.PEACEFUL, true); - - try { - this.theWorld.tick(); - } catch (Throwable throwable2) { - CrashReport crashreport2 = CrashReport.makeCrashReport(throwable2, "Exception in world tick"); - if (this.theWorld == null) { - CrashReportCategory crashreportcategory2 = crashreport2.makeCategory("Affected level"); - crashreportcategory2.addCrashSection("Problem", "Level is null!"); - } else { - this.theWorld.addWorldInfoToCrashReport(crashreport2); - } - - throw new ReportedException(crashreport2); - } - } - // just comment this out for now - // this.mcProfiler.endStartSection("animateTick"); - // if (!this.isGamePaused && this.theWorld != null && Config.animateTick()) { - // this.theWorld.doVoidFogParticles(MathHelper.floor_double(this.thePlayer.posX), - // MathHelper.floor_double(this.thePlayer.posY), - // MathHelper.floor_double(this.thePlayer.posZ)); - // } - - this.mcProfiler.endStartSection("particles"); - if (!this.isGamePaused) { - this.effectRenderer.updateEffects(); - } - } else if (this.myNetworkManager != null) { - this.mcProfiler.endStartSection("pendingConnection"); - this.myNetworkManager.processReceivedPackets(); - } - - if (this.theWorld != null) { - ++joinWorldTickCounter; - if (bungeeOutdatedMsgTimer > 0) { - if (--bungeeOutdatedMsgTimer == 0 && this.thePlayer.sendQueue != null) { - String pluginBrand = this.thePlayer.sendQueue.getNetworkManager().getPluginBrand(); - String pluginVersion = this.thePlayer.sendQueue.getNetworkManager().getPluginVersion(); - if (pluginBrand != null && pluginVersion != null - && EaglerXBungeeVersion.isUpdateToPluginAvailable(pluginBrand, pluginVersion)) { - String pfx = EnumChatFormatting.GOLD + "[EagX]" + EnumChatFormatting.AQUA; - ingameGUI.getChatGUI().printChatMessage( - new ChatComponentText(pfx + " ---------------------------------------")); - ingameGUI.getChatGUI().printChatMessage( - new ChatComponentText(pfx + " This server appears to be using version " - + EnumChatFormatting.YELLOW + pluginVersion)); - ingameGUI.getChatGUI().printChatMessage( - new ChatComponentText(pfx + " of the EaglerXBungee plugin which is outdated")); - ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); - ingameGUI.getChatGUI() - .printChatMessage(new ChatComponentText(pfx + " If you are the admin update to " - + EnumChatFormatting.YELLOW + EaglerXBungeeVersion.getPluginVersion() - + EnumChatFormatting.AQUA + " or newer")); - ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); - ingameGUI.getChatGUI().printChatMessage((new ChatComponentText(pfx + " Click: ")) - .appendSibling((new ChatComponentText("" + EnumChatFormatting.GREEN - + EnumChatFormatting.UNDERLINE + EaglerXBungeeVersion.getPluginButton())) - .setChatStyle((new ChatStyle()).setChatClickEvent( - new ClickEvent(ClickEvent.Action.EAGLER_PLUGIN_DOWNLOAD, - "plugin_download.zip"))))); - ingameGUI.getChatGUI().printChatMessage( - new ChatComponentText(pfx + " ---------------------------------------")); - } - } - } - } else { - joinWorldTickCounter = 0; - if (currentScreen != null && currentScreen.shouldHangupIntegratedServer()) { - if (SingleplayerServerController.hangupEaglercraftServer()) { - this.displayGuiScreen(new GuiScreenIntegratedServerBusy(currentScreen, - "singleplayer.busy.stoppingIntegratedServer", - "singleplayer.failed.stoppingIntegratedServer", SingleplayerServerController::isReady)); - } - } - } - - this.mcProfiler.endSection(); - this.systemTime = getSystemTime(); - } - - /** - * + - * Arguments: World foldername, World ingame name, WorldSettings - */ - public void launchIntegratedServer(String folderName, String worldName, WorldSettings worldSettingsIn) { - this.loadWorld((WorldClient) null); - Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(this.gameSettings.enableFNAWSkins); - session.reset(); - SingleplayerServerController.launchEaglercraftServer(folderName, gameSettings.difficulty.getDifficultyId(), - Math.max(gameSettings.renderDistanceChunks, 2), worldSettingsIn); - EagRuntime.setMCServerWindowGlobal("singleplayer"); - this.displayGuiScreen(new GuiScreenIntegratedServerBusy( - new GuiScreenSingleplayerConnecting(new GuiMainMenu(), "Connecting to " + folderName), - "singleplayer.busy.startingIntegratedServer", "singleplayer.failed.startingIntegratedServer", - () -> SingleplayerServerController.isWorldReady(), (t, u) -> { - Minecraft.this.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiMainMenu(), - ((GuiScreenIntegratedServerBusy) t).failMessage, u)); - })); - } - - /** - * + - * unloads the current world first - */ - public void loadWorld(WorldClient worldClientIn) { - this.loadWorld(worldClientIn, ""); - } - - /** - * + - * unloads the current world first - */ - public void loadWorld(WorldClient worldClientIn, String loadingMessage) { - if (worldClientIn != theWorld) { - this.entityRenderer.getMapItemRenderer().clearLoadedMaps(); - } - if (worldClientIn == null) { - NetHandlerPlayClient nethandlerplayclient = this.getNetHandler(); - if (nethandlerplayclient != null) { - nethandlerplayclient.cleanup(); - } - session.reset(); - - this.guiAchievement.clearAchievements(); - this.entityRenderer.getMapItemRenderer().clearLoadedMaps(); - } - - this.renderViewEntity = null; - this.myNetworkManager = null; - if (this.loadingScreen != null) { - this.loadingScreen.resetProgressAndMessage(loadingMessage); - this.loadingScreen.displayLoadingString(""); - } - - if (worldClientIn == null && this.theWorld != null) { - this.mcResourcePackRepository.func_148529_f(); - this.ingameGUI.func_181029_i(); - this.setServerData((ServerData) null); - this.integratedServerIsRunning = false; - } - - this.mcSoundHandler.stopSounds(); - this.theWorld = worldClientIn; - if (worldClientIn != null) { - if (this.renderGlobal != null) { - this.renderGlobal.setWorldAndLoadRenderers(worldClientIn); - } - - if (this.effectRenderer != null) { - this.effectRenderer.clearEffects(worldClientIn); - } - - if (this.thePlayer == null) { - this.thePlayer = this.playerController.func_178892_a(worldClientIn, new StatFileWriter()); - this.playerController.flipPlayer(this.thePlayer); - } - - this.thePlayer.preparePlayerToSpawn(); - worldClientIn.spawnEntityInWorld(this.thePlayer); - this.thePlayer.movementInput = new MovementInputFromOptions(this.gameSettings); - this.playerController.setPlayerCapabilities(this.thePlayer); - this.renderViewEntity = this.thePlayer; - } else { - this.thePlayer = null; - } - - System.gc(); - this.systemTime = 0L; - } - - public void setDimensionAndSpawnPlayer(int dimension) { - this.theWorld.setInitialSpawnLocation(); - this.theWorld.removeAllEntities(); - int i = 0; - String s = null; - if (this.thePlayer != null) { - i = this.thePlayer.getEntityId(); - this.theWorld.removeEntity(this.thePlayer); - s = this.thePlayer.getClientBrand(); - } - - this.renderViewEntity = null; - EntityPlayerSP entityplayersp = this.thePlayer; - this.thePlayer = this.playerController.func_178892_a(this.theWorld, new StatFileWriter()); - this.thePlayer.getDataWatcher().updateWatchedObjectsFromList(entityplayersp.getDataWatcher().getAllWatched()); - this.thePlayer.dimension = dimension; - this.renderViewEntity = this.thePlayer; - this.thePlayer.preparePlayerToSpawn(); - this.thePlayer.setClientBrand(s); - this.theWorld.spawnEntityInWorld(this.thePlayer); - this.playerController.flipPlayer(this.thePlayer); - this.thePlayer.movementInput = new MovementInputFromOptions(this.gameSettings); - this.thePlayer.setEntityId(i); - this.playerController.setPlayerCapabilities(this.thePlayer); - this.thePlayer.setReducedDebug(entityplayersp.hasReducedDebug()); - if (this.currentScreen instanceof GuiGameOver) { - this.displayGuiScreen((GuiScreen) null); - } - - } - - /** - * + - * Gets whether this is a demo or not. - */ - public final boolean isDemo() { - return EagRuntime.getConfiguration().isDemo(); - } - - public NetHandlerPlayClient getNetHandler() { - return this.thePlayer != null ? this.thePlayer.sendQueue : null; - } - - public static boolean isGuiEnabled() { - return theMinecraft == null || !theMinecraft.gameSettings.hideGUI; - } - - public static boolean isFancyGraphicsEnabled() { - return theMinecraft != null && theMinecraft.gameSettings.fancyGraphics; - } - - /** - * + - * Returns if ambient occlusion is enabled - */ - public static boolean isAmbientOcclusionEnabled() { - if (theMinecraft == null) - return false; - GameSettings g = theMinecraft.gameSettings; - return g.ambientOcclusion != 0 && !g.shadersAODisable; - } - - /** - * + - * Called when user clicked he's mouse middle button (pick - * block) - */ - private void middleClickMouse() { - if (this.objectMouseOver != null) { - boolean flag = this.thePlayer.capabilities.isCreativeMode; - int i = 0; - boolean flag1 = false; - TileEntity tileentity = null; - Object object; - if (this.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { - BlockPos blockpos = this.objectMouseOver.getBlockPos(); - Block block = this.theWorld.getBlockState(blockpos).getBlock(); - if (block.getMaterial() == Material.air) { - return; - } - - object = block.getItem(this.theWorld, blockpos); - if (object == null) { - return; - } - - if (flag && GuiScreen.isCtrlKeyDown()) { - tileentity = this.theWorld.getTileEntity(blockpos); - } - - Block block1 = object instanceof ItemBlock && !block.isFlowerPot() - ? Block.getBlockFromItem((Item) object) - : block; - i = block1.getDamageValue(this.theWorld, blockpos); - flag1 = ((Item) object).getHasSubtypes(); - } else { - if (this.objectMouseOver.typeOfHit != MovingObjectPosition.MovingObjectType.ENTITY - || this.objectMouseOver.entityHit == null || !flag) { - return; - } - - if (this.objectMouseOver.entityHit instanceof EntityPainting) { - object = Items.painting; - } else if (this.objectMouseOver.entityHit instanceof EntityLeashKnot) { - object = Items.lead; - } else if (this.objectMouseOver.entityHit instanceof EntityItemFrame) { - EntityItemFrame entityitemframe = (EntityItemFrame) this.objectMouseOver.entityHit; - ItemStack itemstack = entityitemframe.getDisplayedItem(); - if (itemstack == null) { - object = Items.item_frame; - } else { - object = itemstack.getItem(); - i = itemstack.getMetadata(); - flag1 = true; - } - } else if (this.objectMouseOver.entityHit instanceof EntityMinecart) { - EntityMinecart entityminecart = (EntityMinecart) this.objectMouseOver.entityHit; - switch (entityminecart.getMinecartType()) { - case FURNACE: - object = Items.furnace_minecart; - break; - case CHEST: - object = Items.chest_minecart; - break; - case TNT: - object = Items.tnt_minecart; - break; - case HOPPER: - object = Items.hopper_minecart; - break; - case COMMAND_BLOCK: - object = Items.command_block_minecart; - break; - default: - object = Items.minecart; - } - } else if (this.objectMouseOver.entityHit instanceof EntityBoat) { - object = Items.boat; - } else if (this.objectMouseOver.entityHit instanceof EntityArmorStand) { - object = Items.armor_stand; - } else { - object = Items.spawn_egg; - i = EntityList.getEntityID(this.objectMouseOver.entityHit); - flag1 = true; - if (!EntityList.entityEggs.containsKey(Integer.valueOf(i))) { - return; - } - } - } - - InventoryPlayer inventoryplayer = this.thePlayer.inventory; - if (tileentity == null) { - inventoryplayer.setCurrentItem((Item) object, i, flag1, flag); - } else { - ItemStack itemstack1 = this.func_181036_a((Item) object, i, tileentity); - inventoryplayer.setInventorySlotContents(inventoryplayer.currentItem, itemstack1); - } - - if (flag) { - int j = this.thePlayer.inventoryContainer.inventorySlots.size() - 9 + inventoryplayer.currentItem; - this.playerController.sendSlotPacket(inventoryplayer.getStackInSlot(inventoryplayer.currentItem), j); - } - - } - } - - private ItemStack func_181036_a(Item parItem, int parInt1, TileEntity parTileEntity) { - ItemStack itemstack = new ItemStack(parItem, 1, parInt1); - NBTTagCompound nbttagcompound = new NBTTagCompound(); - parTileEntity.writeToNBT(nbttagcompound); - if (parItem == Items.skull && nbttagcompound.hasKey("Owner")) { - NBTTagCompound nbttagcompound2 = nbttagcompound.getCompoundTag("Owner"); - NBTTagCompound nbttagcompound3 = new NBTTagCompound(); - nbttagcompound3.setTag("SkullOwner", nbttagcompound2); - itemstack.setTagCompound(nbttagcompound3); - return itemstack; - } else { - itemstack.setTagInfo("BlockEntityTag", nbttagcompound); - NBTTagCompound nbttagcompound1 = new NBTTagCompound(); - NBTTagList nbttaglist = new NBTTagList(); - nbttaglist.appendTag(new NBTTagString("(+NBT)")); - nbttagcompound1.setTag("Lore", nbttaglist); - itemstack.setTagInfo("display", nbttagcompound1); - return itemstack; - } - } - - /** - * + - * adds core server Info (GL version , Texture pack, isModded, - * type), and the worldInfo to the crash report - */ - public CrashReport addGraphicsAndWorldToCrashReport(CrashReport theCrash) { - theCrash.getCategory().addCrashSectionCallable("Launched Version", new Callable() { - public String call() throws Exception { - return Minecraft.this.launchedVersion; - } - }); - theCrash.getCategory().addCrashSectionCallable("LWJGL", new Callable() { - public String call() { - return EagRuntime.getVersion(); - } - }); - theCrash.getCategory().addCrashSectionCallable("OpenGL", new Callable() { - public String call() { - return EaglercraftGPU.glGetString(7937) + " GL version " + EaglercraftGPU.glGetString(7938) + ", " - + EaglercraftGPU.glGetString(7936); - } - }); - theCrash.getCategory().addCrashSectionCallable("Is Eagler Shaders", new Callable() { - public String call() throws Exception { - return Minecraft.this.gameSettings.shaders ? "Yes" : "No"; - } - }); - theCrash.getCategory().addCrashSectionCallable("Is Dynamic Lights", new Callable() { - public String call() throws Exception { - return !Minecraft.this.gameSettings.shaders && Minecraft.this.gameSettings.enableDynamicLights ? "Yes" - : "No"; - } - }); - theCrash.getCategory().addCrashSectionCallable("In Ext. Pipeline", new Callable() { - public String call() throws Exception { - return GlStateManager.isExtensionPipeline() ? "Yes" : "No"; - } - }); - theCrash.getCategory().addCrashSectionCallable("Is Modded", new Callable() { - public String call() throws Exception { - return "Definitely Not; You're an eagler"; - } - }); - theCrash.getCategory().addCrashSectionCallable("Type", new Callable() { - public String call() throws Exception { - return "Client (map_client.txt)"; - } - }); - theCrash.getCategory().addCrashSectionCallable("Resource Packs", new Callable() { - public String call() throws Exception { - StringBuilder stringbuilder = new StringBuilder(); - - for (String s : Minecraft.this.gameSettings.resourcePacks) { - if (stringbuilder.length() > 0) { - stringbuilder.append(", "); - } - - stringbuilder.append(s); - if (Minecraft.this.gameSettings.field_183018_l.contains(s)) { - stringbuilder.append(" (incompatible)"); - } - } - - return stringbuilder.toString(); - } - }); - theCrash.getCategory().addCrashSectionCallable("Current Language", new Callable() { - public String call() throws Exception { - return Minecraft.this.mcLanguageManager.getCurrentLanguage().toString(); - } - }); - theCrash.getCategory().addCrashSectionCallable("Profiler Position", new Callable() { - public String call() throws Exception { - return Minecraft.this.mcProfiler.profilingEnabled ? Minecraft.this.mcProfiler.getNameOfLastSection() - : "N/A (disabled)"; - } - }); - if (this.theWorld != null) { - this.theWorld.addWorldInfoToCrashReport(theCrash); - } - - return theCrash; - } - - /** - * + - * Return the singleton Minecraft instance for the game - */ - public static Minecraft getMinecraft() { - return theMinecraft; - } - - public ListenableFuture scheduleResourcesRefresh() { - return this.addScheduledTaskFuture(new Runnable() { - public void run() { - Minecraft.this.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), - I18n.format("resourcePack.load.pleaseWait")); - Minecraft.this.refreshResources(); - } - }); - } - - private String func_181538_aA() { - return this.currentServerData != null ? "multiplayer" : "out_of_game"; - } - - /** - * + - * Returns whether snooping is enabled or not. - */ - public boolean isSnooperEnabled() { - return this.gameSettings.snooperEnabled; - } - - /** - * + - * Set the current ServerData instance. - */ - public void setServerData(ServerData serverDataIn) { - this.currentServerData = serverDataIn; - EagRuntime.setMCServerWindowGlobal(serverDataIn != null ? serverDataIn.serverIP : null); - } - - public ServerData getCurrentServerData() { - return this.currentServerData; - } - - public boolean isIntegratedServerRunning() { - return SingleplayerServerController.isWorldRunning(); - } - - /** - * + - * Returns true if there is only one player playing, and the - * current server is the integrated one. - */ - public boolean isSingleplayer() { - return SingleplayerServerController.isWorldRunning(); - } - - public static void stopIntegratedServer() { - - } - - /** - * + - * Gets the system time in milliseconds. - */ - public static long getSystemTime() { - return System.currentTimeMillis(); - } - - /** - * + - * Returns whether we're in full screen or not. - */ - public boolean isFullScreen() { - return Display.isFullscreen(); - } - - public Session getSession() { - return this.session; - } - - public TextureManager getTextureManager() { - return this.renderEngine; - } - - public IResourceManager getResourceManager() { - return this.mcResourceManager; - } - - public ResourcePackRepository getResourcePackRepository() { - return this.mcResourcePackRepository; - } - - public LanguageManager getLanguageManager() { - return this.mcLanguageManager; - } - - public TextureMap getTextureMapBlocks() { - return this.textureMapBlocks; - } - - public boolean isJava64bit() { - return this.jvm64bit; - } - - public boolean isGamePaused() { - return this.isGamePaused; - } - - public SoundHandler getSoundHandler() { - return this.mcSoundHandler; - } - - public MusicTicker.MusicType getAmbientMusicType() { - return this.thePlayer != null ? (this.thePlayer.worldObj.provider instanceof WorldProviderHell - ? MusicTicker.MusicType.NETHER - : (this.thePlayer.worldObj.provider instanceof WorldProviderEnd - ? (BossStatus.bossName != null && BossStatus.statusBarTime > 0 ? MusicTicker.MusicType.END_BOSS - : MusicTicker.MusicType.END) - : (this.thePlayer.capabilities.isCreativeMode && this.thePlayer.capabilities.allowFlying - ? MusicTicker.MusicType.CREATIVE - : MusicTicker.MusicType.GAME))) - : MusicTicker.MusicType.MENU; - } - - public void dispatchKeypresses() { - int i = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() : Keyboard.getEventKey(); - if (i != 0 && !Keyboard.isRepeatEvent()) { - if (!(this.currentScreen instanceof GuiControls) - || ((GuiControls) this.currentScreen).time <= getSystemTime() - 20L) { - if (Keyboard.getEventKeyState()) { - if (i == this.gameSettings.keyBindScreenshot.getKeyCode()) { - this.ingameGUI.getChatGUI().printChatMessage(ScreenShotHelper.saveScreenshot()); - } - } - } - } - } - - public Entity getRenderViewEntity() { - return this.renderViewEntity; - } - - public void setRenderViewEntity(Entity viewingEntity) { - this.renderViewEntity = viewingEntity; - this.entityRenderer.loadEntityShader(viewingEntity); - } - - public ListenableFuture addScheduledTaskFuture(Callable callableToSchedule) { - Validate.notNull(callableToSchedule); - ListenableFutureTask listenablefuturetask = ListenableFutureTask.create(callableToSchedule); - synchronized (this.scheduledTasks) { - this.scheduledTasks.add(listenablefuturetask); - return listenablefuturetask; - } - } - - public ListenableFuture addScheduledTaskFuture(Runnable runnableToSchedule) { - Validate.notNull(runnableToSchedule); - return this.addScheduledTaskFuture(Executors.callable(runnableToSchedule)); - } - - public void addScheduledTask(Runnable runnableToSchedule) { - this.addScheduledTaskFuture(Executors.callable(runnableToSchedule)); - } - - public BlockRendererDispatcher getBlockRendererDispatcher() { - return this.blockRenderDispatcher; - } - - public RenderManager getRenderManager() { - return this.renderManager; - } - - public RenderItem getRenderItem() { - return this.renderItem; - } - - public ItemRenderer getItemRenderer() { - return this.itemRenderer; - } - - public static int getDebugFPS() { - return debugFPS; - } - - public FrameTimer func_181539_aj() { - return this.field_181542_y; - } - - public boolean func_181540_al() { - return this.field_181541_X; - } - - public void func_181537_a(boolean parFlag) { - this.field_181541_X = parFlag; - } - - /** - * + - * Used in the usage snooper. - */ - public static int getGLMaximumTextureSize() { - return EaglercraftGPU.glGetInteger(GL_MAX_TEXTURE_SIZE); - } - - public boolean areKeysLocked() { - return PlatformInput.lockKeys; - } - - public ModelManager getModelManager() { - return modelManager; - } - - public float getRenderPartialTicks() - { - return this.timer.renderPartialTicks; - } - - /** - * + - * Returns the save loader that is currently being used - */ - public ISaveFormat getSaveLoader() { - return SingleplayerServerController.instance; - } - - public void clearTitles() { - ingameGUI.displayTitle(null, null, -1, -1, -1); - } - - public boolean getEnableFNAWSkins() { - boolean ret = this.gameSettings.enableFNAWSkins; - if (this.thePlayer != null) { - ret &= this.thePlayer.sendQueue.currentFNAWSkinAllowedState; - } - return ret; - } +package net.minecraft.client; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL._wglBindFramebuffer; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; + +import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +import org.apache.commons.lang3.Validate; + +import com.google.common.collect.Lists; + +import net.hoosiertransfer.CullingMod; +import net.hoosiertransfer.Alfheim.ILightUpdatesProcessor; +import net.hoosiertransfer.Config; + +import net.lax1dude.eaglercraft.v1_8.Display; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; +import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.IOUtils; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +import org.apache.commons.lang3.Validate; + +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.futures.Executors; +import net.lax1dude.eaglercraft.v1_8.futures.FutureTask; +import net.lax1dude.eaglercraft.v1_8.futures.ListenableFuture; +import net.lax1dude.eaglercraft.v1_8.futures.ListenableFutureTask; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFontRenderer; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.lax1dude.eaglercraft.v1_8.notifications.ServerNotificationRenderer; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.BlockVertexIDs; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DebugFramebufferView; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.EaglerDeferredPipeline; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ShaderPackInfoReloadListener; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.EmissiveItems; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.MetalsLUT; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.PBRTextureMapUtils; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.TemperaturesLUT; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.GuiScreenContentWarning; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.profile.GuiScreenEditProfile; +import net.lax1dude.eaglercraft.v1_8.profile.SkinPreviewRenderer; +import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; +import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; +import net.lax1dude.eaglercraft.v1_8.sp.IntegratedServerState; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.SkullCommand; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenDemoIntegratedServerStartup; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenIntegratedServerBusy; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenSingleplayerConnecting; +import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchControls; +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchOverlayRenderer; +import net.lax1dude.eaglercraft.v1_8.update.GuiUpdateDownloadSuccess; +import net.lax1dude.eaglercraft.v1_8.update.GuiUpdateInstallOptions; +import net.lax1dude.eaglercraft.v1_8.update.RelayUpdateChecker; +import net.lax1dude.eaglercraft.v1_8.update.UpdateDataObj; +import net.lax1dude.eaglercraft.v1_8.update.UpdateResultObj; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceOverlay; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.client.audio.MusicTicker; +import net.minecraft.client.audio.SoundHandler; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.client.gui.GuiControls; +import net.minecraft.client.gui.GuiGameOver; +import net.minecraft.client.gui.GuiIngame; +import net.minecraft.client.gui.GuiIngameMenu; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSleepMP; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.gui.achievement.GuiAchievement; +import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.client.main.GameConfiguration; +import net.minecraft.client.multiplayer.GuiConnecting; +import net.minecraft.client.multiplayer.PlayerControllerMP; +import net.minecraft.client.multiplayer.ServerAddress; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.multiplayer.ServerList; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.particle.EffectRenderer; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.chunk.RenderChunk; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.DefaultResourcePack; +import net.minecraft.client.resources.FoliageColorReloadListener; +import net.minecraft.client.resources.GrassColorReloadListener; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.resources.IReloadableResourceManager; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.IResourcePack; +import net.minecraft.client.resources.LanguageManager; +import net.minecraft.client.resources.ResourcePackRepository; +import net.minecraft.client.resources.SimpleReloadableResourceManager; +import net.minecraft.client.resources.data.AnimationMetadataSection; +import net.minecraft.client.resources.data.AnimationMetadataSectionSerializer; +import net.minecraft.client.resources.data.FontMetadataSection; +import net.minecraft.client.resources.data.FontMetadataSectionSerializer; +import net.minecraft.client.resources.data.IMetadataSerializer; +import net.minecraft.client.resources.data.LanguageMetadataSection; +import net.minecraft.client.resources.data.LanguageMetadataSectionSerializer; +import net.minecraft.client.resources.data.PackMetadataSection; +import net.minecraft.client.resources.data.PackMetadataSectionSerializer; +import net.minecraft.client.resources.data.TextureMetadataSection; +import net.minecraft.client.resources.data.TextureMetadataSectionSerializer; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.client.settings.KeyBinding; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLeashKnot; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.boss.BossStatus; +import net.minecraft.entity.item.EntityArmorStand; +import net.minecraft.entity.item.EntityBoat; +import net.minecraft.entity.item.EntityItemFrame; +import net.minecraft.entity.item.EntityMinecart; +import net.minecraft.entity.item.EntityPainting; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.event.ClickEvent; +import net.minecraft.init.Bootstrap; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraft.network.play.client.C16PacketClientStatus; +import net.minecraft.stats.AchievementList; +import net.minecraft.stats.IStatStringFormat; +import net.minecraft.stats.StatFileWriter; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.FrameTimer; +import net.minecraft.util.IThreadListener; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MinecraftError; +import net.minecraft.util.MouseHelper; +import net.minecraft.util.MovementInputFromOptions; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.ReportedException; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.ScreenShotHelper; +import net.minecraft.util.Session; +import net.minecraft.util.StringTranslate; +import net.minecraft.util.Timer; +import net.minecraft.util.Util; +import net.minecraft.world.EnumDifficulty; +import net.minecraft.world.WorldProviderEnd; +import net.minecraft.world.WorldProviderHell; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.storage.ISaveFormat; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class Minecraft implements IThreadListener { + private static final Logger logger = LogManager.getLogger(); + private static final ResourceLocation locationMojangPng = new ResourceLocation("textures/gui/title/mojang.png"); + public static final boolean isRunningOnMac = false; + private ServerData currentServerData; + private TextureManager renderEngine; + private static Minecraft theMinecraft; + public PlayerControllerMP playerController; + private boolean fullscreen; + private boolean enableGLErrorChecking = true; + private boolean hasCrashed; + private CrashReport crashReporter; + public int displayWidth; + public int displayHeight; + public float displayDPI; + private boolean field_181541_X = false; + private Timer timer = new Timer(20.0F); + public WorldClient theWorld; + public RenderGlobal renderGlobal; + private RenderManager renderManager; + private RenderItem renderItem; + private ItemRenderer itemRenderer; + public EntityPlayerSP thePlayer; + private Entity renderViewEntity; + public Entity pointedEntity; + public EffectRenderer effectRenderer; + private final Session session; + private boolean isGamePaused; + private boolean wasPaused; + public FontRenderer fontRendererObj; + public FontRenderer standardGalacticFontRenderer; + public GuiScreen currentScreen; + public LoadingScreenRenderer loadingScreen; + public EntityRenderer entityRenderer; + private int leftClickCounter; + private int tempDisplayWidth; + private int tempDisplayHeight; + public GuiAchievement guiAchievement; + public GuiIngame ingameGUI; + public boolean skipRenderWorld; + public MovingObjectPosition objectMouseOver; + public GameSettings gameSettings; + public MouseHelper mouseHelper; + private final String launchedVersion; + private static int debugFPS; + private int rightClickDelayTimer; + private String serverName; + private int serverPort; + public boolean inGameHasFocus; + long systemTime = getSystemTime(); + private int joinPlayerCounter; + public final FrameTimer field_181542_y = new FrameTimer(); + long field_181543_z = System.nanoTime(); + private final boolean jvm64bit; + private EaglercraftNetworkManager myNetworkManager; + private boolean integratedServerIsRunning; + /**+ + * Keeps track of how long the debug crash keycombo (F3+C) has + * been pressed for, in order to crash after 10 seconds. + */ + private long debugCrashKeyPressTime = -1L; + private IReloadableResourceManager mcResourceManager; + private final IMetadataSerializer metadataSerializer_ = new IMetadataSerializer(); + private final List defaultResourcePacks = Lists.newArrayList(); + private final DefaultResourcePack mcDefaultResourcePack; + private ResourcePackRepository mcResourcePackRepository; + private LanguageManager mcLanguageManager; + private TextureMap textureMapBlocks; + private SoundHandler mcSoundHandler; + private MusicTicker mcMusicTicker; + private ResourceLocation mojangLogo; + private final List> scheduledTasks = new LinkedList<>(); + private long field_175615_aJ = 0L; + private final Thread mcThread = Thread.currentThread(); + private ModelManager modelManager; + private BlockRendererDispatcher blockRenderDispatcher; + /** + * + + * Set to true to keep the game loop running. Set to false by + * shutdown() to allow the game loop to exit cleanly. + */ + volatile boolean running = true; + /** + * + + * String that shows the debug information + */ + public String debug = ""; + public boolean field_175613_B = false; + public boolean field_175614_C = false; + public boolean field_175611_D = false; + public boolean renderChunksMany = true; + long debugUpdateTime = getSystemTime(); + int fpsCounter; + long prevFrameTime = -1L; + /** + * + + * Profiler currently displayed in the debug screen pie chart + */ + private String debugProfilerName = "root"; + public int joinWorldTickCounter = 0; + private int dontPauseTimer = 0; + public int bungeeOutdatedMsgTimer = 0; + private boolean isLANOpen = false; + + public SkullCommand eagskullCommand; + + public GuiVoiceOverlay voiceOverlay; + public ServerNotificationRenderer notifRenderer; + public TouchOverlayRenderer touchOverlayRenderer; + + public float startZoomValue = 18.0f; + public float adjustedZoomValue = 18.0f; + public boolean isZoomKey = false; + private String reconnectURI = null; + public boolean mouseGrabSupported = false; + public ScaledResolution scaledResolution = null; + + public Minecraft(GameConfiguration gameConfig) { + theMinecraft = this; + StringTranslate.initClient(); + this.launchedVersion = gameConfig.gameInfo.version; + this.mcDefaultResourcePack = new DefaultResourcePack(); + this.session = gameConfig.userInfo.session; + logger.info("Setting user: " + this.session.getProfile().getName()); + this.displayWidth = gameConfig.displayInfo.width > 0 ? gameConfig.displayInfo.width : 1; + this.displayHeight = gameConfig.displayInfo.height > 0 ? gameConfig.displayInfo.height : 1; + this.displayDPI = 1.0f; + this.tempDisplayWidth = gameConfig.displayInfo.width; + this.tempDisplayHeight = gameConfig.displayInfo.height; + this.fullscreen = gameConfig.displayInfo.fullscreen; + this.jvm64bit = isJvm64bit(); + String serverToJoin = EagRuntime.getConfiguration().getServerToJoin(); + if (serverToJoin != null) { + ServerAddress addr = AddressResolver.resolveAddressFromURI(serverToJoin); + this.serverName = addr.getIP(); + this.serverPort = addr.getPort(); + } + + Bootstrap.register(); + } + + public boolean isCallingFromMinecraftThread() { + return Thread.currentThread() == this.mcThread; + } + + public void run() { + CullingMod.intialize(); + this.running = true; + + try { + this.startGame(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Initializing game"); + crashreport.makeCategory("Initialization"); + this.displayCrashReport(this.addGraphicsAndWorldToCrashReport(crashreport)); + return; + } + + try { + while (true) { + if (!this.running) { + break; + } + + if (!this.hasCrashed || this.crashReporter == null) { + this.runGameLoop(); + continue; + } + + this.displayCrashReport(this.crashReporter); + } + } catch (MinecraftError var12) { + // ?? + } catch (ReportedException reportedexception) { + this.addGraphicsAndWorldToCrashReport(reportedexception.getCrashReport()); + logger.fatal("Reported exception thrown!", reportedexception); + this.displayCrashReport(reportedexception.getCrashReport()); + } catch (Throwable throwable1) { + CrashReport crashreport1 = this + .addGraphicsAndWorldToCrashReport(new CrashReport("Unexpected error", throwable1)); + logger.fatal("Unreported exception thrown!", throwable1); + this.displayCrashReport(crashreport1); + } finally { + this.shutdownMinecraftApplet(); + } + + } + + /** + * + + * Starts the game: initializes the canvas, the title, the + * settings, etcetera. + */ + private void startGame() throws IOException { + this.gameSettings = new GameSettings(this); + this.defaultResourcePacks.add(this.mcDefaultResourcePack); + if (this.gameSettings.overrideHeight > 0 && this.gameSettings.overrideWidth > 0) { + this.displayWidth = this.gameSettings.overrideWidth; + this.displayHeight = this.gameSettings.overrideHeight; + } + + logger.info("EagRuntime Version: " + EagRuntime.getVersion()); + this.createDisplay(); + this.registerMetadataSerializers(); + EaglerFolderResourcePack.deleteOldResourcePacks(EaglerFolderResourcePack.SERVER_RESOURCE_PACKS, 604800000L); + this.mcResourcePackRepository = new ResourcePackRepository(this.mcDefaultResourcePack, this.metadataSerializer_, + this.gameSettings); + this.mcResourceManager = new SimpleReloadableResourceManager(this.metadataSerializer_); + this.mcLanguageManager = new LanguageManager(this.metadataSerializer_, this.gameSettings.language); + this.mcResourceManager.registerReloadListener(this.mcLanguageManager); + this.scaledResolution = new ScaledResolution(this); + this.refreshResources(); + this.renderEngine = new TextureManager(this.mcResourceManager); + this.mcResourceManager.registerReloadListener(this.renderEngine); + this.drawSplashScreen(this.renderEngine); + this.mcSoundHandler = new SoundHandler(this.mcResourceManager, this.gameSettings); + this.mcResourceManager.registerReloadListener(this.mcSoundHandler); + this.mcMusicTicker = new MusicTicker(this); + this.fontRendererObj = EaglerFontRenderer.createSupportedFontRenderer(this.gameSettings, + new ResourceLocation("textures/font/ascii.png"), this.renderEngine, false); + if (this.gameSettings.language != null) { + this.fontRendererObj.setUnicodeFlag(this.isUnicode()); + this.fontRendererObj.setBidiFlag(this.mcLanguageManager.isCurrentLanguageBidirectional()); + } + + this.standardGalacticFontRenderer = EaglerFontRenderer.createSupportedFontRenderer(this.gameSettings, + new ResourceLocation("textures/font/ascii_sga.png"), this.renderEngine, false); + this.mcResourceManager.registerReloadListener(this.fontRendererObj); + this.mcResourceManager.registerReloadListener(this.standardGalacticFontRenderer); + this.mcResourceManager.registerReloadListener(new GrassColorReloadListener()); + this.mcResourceManager.registerReloadListener(new FoliageColorReloadListener()); + this.mcResourceManager.registerReloadListener(new ShaderPackInfoReloadListener()); + this.mcResourceManager.registerReloadListener(PBRTextureMapUtils.blockMaterialConstants); + this.mcResourceManager.registerReloadListener(new TemperaturesLUT()); + this.mcResourceManager.registerReloadListener(new MetalsLUT()); + this.mcResourceManager.registerReloadListener(new EmissiveItems()); + this.mcResourceManager.registerReloadListener(new BlockVertexIDs()); + this.mcResourceManager.registerReloadListener(new EaglerMeshLoader()); + AchievementList.openInventory.setStatStringFormatter(new IStatStringFormat() { + public String formatString(String parString1) { + try { + return HString.format(parString1, new Object[] { GameSettings + .getKeyDisplayString(Minecraft.this.gameSettings.keyBindInventory.getKeyCode()) }); + } catch (Exception exception) { + return "Error: " + exception.getLocalizedMessage(); + } + } + }); + this.mouseHelper = new MouseHelper(); + this.checkGLError("Pre startup"); + GlStateManager.enableTexture2D(); + GlStateManager.shadeModel(GL_SMOOTH); + GlStateManager.clearDepth(1.0f); + GlStateManager.enableDepth(); + GlStateManager.depthFunc(GL_LEQUAL); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(GL_GREATER, 0.1F); + GlStateManager.cullFace(GL_BACK); + GlStateManager.matrixMode(GL_PROJECTION); + GlStateManager.loadIdentity(); + GlStateManager.matrixMode(GL_MODELVIEW); + this.checkGLError("Startup"); + this.textureMapBlocks = new TextureMap("textures"); + this.textureMapBlocks.setEnablePBREagler(gameSettings.shaders); + this.textureMapBlocks.setMipmapLevels(this.gameSettings.mipmapLevels); + this.renderEngine.loadTickableTexture(TextureMap.locationBlocksTexture, this.textureMapBlocks); + this.renderEngine.bindTexture(TextureMap.locationBlocksTexture); + this.textureMapBlocks.setBlurMipmapDirect(false, this.gameSettings.mipmapLevels > 0); + this.modelManager = new ModelManager(this.textureMapBlocks); + this.mcResourceManager.registerReloadListener(this.modelManager); + this.renderItem = new RenderItem(this.renderEngine, this.modelManager); + this.renderManager = new RenderManager(this.renderEngine, this.renderItem); + this.itemRenderer = new ItemRenderer(this); + this.mcResourceManager.registerReloadListener(this.renderItem); + this.entityRenderer = new EntityRenderer(this, this.mcResourceManager); + this.mcResourceManager.registerReloadListener(this.entityRenderer); + this.blockRenderDispatcher = new BlockRendererDispatcher(this.modelManager.getBlockModelShapes(), + this.gameSettings); + this.mcResourceManager.registerReloadListener(this.blockRenderDispatcher); + this.renderGlobal = new RenderGlobal(this); + this.mcResourceManager.registerReloadListener(this.renderGlobal); + this.guiAchievement = new GuiAchievement(this); + GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); + this.effectRenderer = new EffectRenderer(this.theWorld, this.renderEngine); + SkinPreviewRenderer.initialize(); + this.checkGLError("Post startup"); + this.ingameGUI = new GuiIngame(this); + + this.mouseGrabSupported = Mouse.isMouseGrabSupported(); + PointerInputAbstraction.init(this); + + this.eagskullCommand = new SkullCommand(this); + this.voiceOverlay = new GuiVoiceOverlay(this); + this.voiceOverlay.setResolution(scaledResolution.getScaledWidth(), scaledResolution.getScaledHeight()); + + this.notifRenderer = new ServerNotificationRenderer(); + this.notifRenderer.init(); + this.notifRenderer.setResolution(this, scaledResolution.getScaledWidth(), scaledResolution.getScaledHeight(), + scaledResolution.getScaleFactor()); + this.touchOverlayRenderer = new TouchOverlayRenderer(this); + + ServerList.initServerList(this); + EaglerProfile.read(); + ServerCookieDataStore.load(); + + GuiScreen mainMenu = new GuiMainMenu(); + if (isDemo()) { + mainMenu = new GuiScreenDemoIntegratedServerStartup(mainMenu); + } + if (this.serverName != null) { + mainMenu = new GuiConnecting(mainMenu, this, this.serverName, this.serverPort); + } + + mainMenu = new GuiScreenEditProfile(mainMenu); + + if (!EagRuntime.getConfiguration().isForceProfanityFilter() && !gameSettings.hasShownProfanityFilter) { + mainMenu = new GuiScreenContentWarning(mainMenu); + } + + this.displayGuiScreen(mainMenu); + + this.renderEngine.deleteTexture(this.mojangLogo); + this.mojangLogo = null; + this.loadingScreen = new LoadingScreenRenderer(this); + + while (Mouse.next()) + ; + while (Keyboard.next()) + ; + while (Touch.next()) + ; + } + + private void registerMetadataSerializers() { + this.metadataSerializer_.registerMetadataSectionType(new TextureMetadataSectionSerializer(), + TextureMetadataSection.class); + this.metadataSerializer_.registerMetadataSectionType(new FontMetadataSectionSerializer(), + FontMetadataSection.class); + this.metadataSerializer_.registerMetadataSectionType(new AnimationMetadataSectionSerializer(), + AnimationMetadataSection.class); + this.metadataSerializer_.registerMetadataSectionType(new PackMetadataSectionSerializer(), + PackMetadataSection.class); + this.metadataSerializer_.registerMetadataSectionType(new LanguageMetadataSectionSerializer(), + LanguageMetadataSection.class); + } + + private void createDisplay() { + Display.create(); + Display.setTitle("Eaglercraft 1.9.4"); + } + + private static boolean isJvm64bit() { + return true; + } + + public String getVersion() { + return this.launchedVersion; + } + + public void crashed(CrashReport crash) { + this.hasCrashed = true; + this.crashReporter = crash; + } + + /** + * + + * Wrapper around displayCrashReportInternal + */ + public void displayCrashReport(CrashReport crashReportIn) { + String report = crashReportIn.getCompleteReport(); + Bootstrap.printToSYSOUT(report); + PlatformRuntime.writeCrashReport(report); + if (PlatformRuntime.getPlatformType() == EnumPlatformType.JAVASCRIPT) { + System.err.println( + "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); + System.err.println("NATIVE BROWSER EXCEPTION:"); + if (!PlatformRuntime.printJSExceptionIfBrowser(crashReportIn.getCrashCause())) { + System.err.println(""); + } + System.err.println( + "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); + } + } + + public boolean isUnicode() { + return this.mcLanguageManager.isCurrentLocaleUnicode() || this.gameSettings.forceUnicodeFont; + } + + public void refreshResources() { + GlStateManager.recompileShaders(); + + ArrayList arraylist = Lists.newArrayList(this.defaultResourcePacks); + + for (ResourcePackRepository.Entry resourcepackrepository$entry : this.mcResourcePackRepository + .getRepositoryEntries()) { + arraylist.add(resourcepackrepository$entry.getResourcePack()); + } + + if (this.mcResourcePackRepository.getResourcePackInstance() != null) { + arraylist.add(this.mcResourcePackRepository.getResourcePackInstance()); + } + + try { + this.mcResourceManager.reloadResources(arraylist); + } catch (RuntimeException runtimeexception) { + logger.info("Caught error stitching, removing all assigned resourcepacks"); + logger.info(runtimeexception); + arraylist.clear(); + arraylist.addAll(this.defaultResourcePacks); + this.mcResourcePackRepository.setRepositories(Collections.emptyList()); + this.mcResourceManager.reloadResources(arraylist); + this.gameSettings.resourcePacks.clear(); + this.gameSettings.field_183018_l.clear(); + this.gameSettings.saveOptions(); + } + + ShaderSource.clearCache(); + GuiMainMenu.doResourceReloadHack(); + + this.mcLanguageManager.parseLanguageMetadata(arraylist); + if (this.renderGlobal != null) { + this.renderGlobal.loadRenderers(); + } + + } + + private void updateDisplayMode() { + this.displayWidth = Display.getWidth(); + this.displayHeight = Display.getHeight(); + this.displayDPI = Display.getDPI(); + this.scaledResolution = new ScaledResolution(this); + } + + private void drawSplashScreen(TextureManager textureManagerInstance) { + Display.update(); + updateDisplayMode(); + GlStateManager.viewport(0, 0, displayWidth, displayHeight); + GlStateManager.matrixMode(GL_PROJECTION); + GlStateManager.loadIdentity(); + GlStateManager.ortho(0.0D, (double) scaledResolution.getScaledWidth(), + (double) scaledResolution.getScaledHeight(), 0.0D, 1000.0D, 3000.0D); + GlStateManager.matrixMode(GL_MODELVIEW); + GlStateManager.loadIdentity(); + GlStateManager.translate(0.0F, 0.0F, -2000.0F); + GlStateManager.disableLighting(); + GlStateManager.disableFog(); + GlStateManager.disableDepth(); + GlStateManager.enableTexture2D(); + InputStream inputstream = null; + + try { + inputstream = this.mcDefaultResourcePack.getInputStream(locationMojangPng); + this.mojangLogo = textureManagerInstance.getDynamicTextureLocation("logo", + new DynamicTexture(ImageData.loadImageFile(inputstream))); + textureManagerInstance.bindTexture(this.mojangLogo); + } catch (IOException ioexception) { + logger.error("Unable to load logo: " + locationMojangPng, ioexception); + } finally { + IOUtils.closeQuietly(inputstream); + } + + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); + worldrenderer.pos(0.0D, (double) this.displayHeight, 0.0D).tex(0.0D, 0.0D).color(255, 255, 255, 255) + .endVertex(); + worldrenderer.pos((double) this.displayWidth, (double) this.displayHeight, 0.0D).tex(0.0D, 0.0D) + .color(255, 255, 255, 255).endVertex(); + worldrenderer.pos((double) this.displayWidth, 0.0D, 0.0D).tex(0.0D, 0.0D).color(255, 255, 255, 255).endVertex(); + worldrenderer.pos(0.0D, 0.0D, 0.0D).tex(0.0D, 0.0D).color(255, 255, 255, 255).endVertex(); + tessellator.draw(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + short short1 = 256; + short short2 = 256; + this.func_181536_a((scaledResolution.getScaledWidth() - short1) / 2, + (scaledResolution.getScaledHeight() - short2) / 2, 0, 0, short1, short2, 255, 255, 255, 255); + GlStateManager.disableLighting(); + GlStateManager.disableFog(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(GL_GREATER, 0.1F); + this.updateDisplay(); + } + + public void func_181536_a(int parInt1, int parInt2, int parInt3, int parInt4, int parInt5, int parInt6, int parInt7, + int parInt8, int parInt9, int parInt10) { + float f = 0.00390625F; + float f1 = 0.00390625F; + WorldRenderer worldrenderer = Tessellator.getInstance().getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); + worldrenderer.pos((double) parInt1, (double) (parInt2 + parInt6), 0.0D) + .tex((double) ((float) parInt3 * f), (double) ((float) (parInt4 + parInt6) * f1)) + .color(parInt7, parInt8, parInt9, parInt10).endVertex(); + worldrenderer.pos((double) (parInt1 + parInt5), (double) (parInt2 + parInt6), 0.0D) + .tex((double) ((float) (parInt3 + parInt5) * f), (double) ((float) (parInt4 + parInt6) * f1)) + .color(parInt7, parInt8, parInt9, parInt10).endVertex(); + worldrenderer.pos((double) (parInt1 + parInt5), (double) parInt2, 0.0D) + .tex((double) ((float) (parInt3 + parInt5) * f), (double) ((float) parInt4 * f1)) + .color(parInt7, parInt8, parInt9, parInt10).endVertex(); + worldrenderer.pos((double) parInt1, (double) parInt2, 0.0D) + .tex((double) ((float) parInt3 * f), (double) ((float) parInt4 * f1)) + .color(parInt7, parInt8, parInt9, parInt10).endVertex(); + Tessellator.getInstance().draw(); + } + + /** + * + + * Sets the argument GuiScreen as the main (topmost visible) + * screen. + */ + public void displayGuiScreen(GuiScreen guiScreenIn) { + if (this.currentScreen != null) { + this.currentScreen.onGuiClosed(); + } + + if (guiScreenIn == null && this.theWorld == null) { + guiScreenIn = new GuiMainMenu(); + } else if (guiScreenIn == null && this.thePlayer.getHealth() <= 0.0F) { + guiScreenIn = new GuiGameOver(); + } + + if (guiScreenIn instanceof GuiMainMenu) { + this.gameSettings.showDebugInfo = false; + this.ingameGUI.getChatGUI().clearChatMessages(); + } + + this.currentScreen = (GuiScreen) guiScreenIn; + this.scaledResolution = new ScaledResolution(this); + if (guiScreenIn != null) { + this.setIngameNotInFocus(); + ((GuiScreen) guiScreenIn).setWorldAndResolution(this, scaledResolution.getScaledWidth(), + scaledResolution.getScaledHeight()); + this.skipRenderWorld = false; + } else { + this.mcSoundHandler.resumeSounds(); + this.setIngameFocus(); + } + EagRuntime.getConfiguration().getHooks().callScreenChangedHook( + currentScreen != null ? currentScreen.getClass().getName() : null, scaledResolution.getScaledWidth(), + scaledResolution.getScaledHeight(), displayWidth, displayHeight, scaledResolution.getScaleFactor()); + } + + public void shutdownIntegratedServer(GuiScreen cont) { + if (SingleplayerServerController.shutdownEaglercraftServer() + || SingleplayerServerController.getStatusState() == IntegratedServerState.WORLD_UNLOADING) { + displayGuiScreen(new GuiScreenIntegratedServerBusy(cont, "singleplayer.busy.stoppingIntegratedServer", + "singleplayer.failed.stoppingIntegratedServer", SingleplayerServerController::isReady)); + } else { + displayGuiScreen(cont); + } + } + + /** + * + + * Checks for an OpenGL error. If there is one, prints the error + * ID and error string. + */ + public void checkGLError(String message) { + if (this.enableGLErrorChecking) { + int i = EaglercraftGPU.glGetError(); + if (i != 0) { + String s = EaglercraftGPU.gluErrorString(i); + logger.error("########## GL ERROR ##########"); + logger.error("@ " + message); + logger.error(i + ": " + s); + } + + } + } + + /** + * + + * Shuts down the minecraft applet by stopping the resource + * downloads, and clearing up GL stuff; called when the + * application (or web page) is exited. + */ + public void shutdownMinecraftApplet() { + try { + logger.info("Stopping!"); + + try { + this.loadWorld((WorldClient) null); + } catch (Throwable var5) { + ; + } + + this.mcSoundHandler.unloadSounds(); + if (SingleplayerServerController.isWorldRunning()) { + SingleplayerServerController.shutdownEaglercraftServer(); + while (SingleplayerServerController.getStatusState() == IntegratedServerState.WORLD_UNLOADING) { + EagUtils.sleep(50l); + SingleplayerServerController.runTick(); + } + } + if (SingleplayerServerController.isIntegratedServerWorkerAlive() + && SingleplayerServerController.canKillWorker()) { + SingleplayerServerController.killWorker(); + EagUtils.sleep(50l); + } + } finally { + EagRuntime.destroy(); + if (!this.hasCrashed) { + EagRuntime.exit(); + } + + } + } + + /** + * + + * Called repeatedly from run() + */ + private void runGameLoop() throws IOException { + long i = System.nanoTime(); + if (Display.isCloseRequested()) { + this.shutdown(); + } + + PointerInputAbstraction.runGameLoop(); + this.gameSettings.touchscreen = PointerInputAbstraction.isTouchMode(); + + if (this.isGamePaused && this.theWorld != null) { + float f = this.timer.renderPartialTicks; + this.timer.updateTimer(); + this.timer.renderPartialTicks = f; + } else { + this.timer.updateTimer(); + } + + synchronized (this.scheduledTasks) { + while (!this.scheduledTasks.isEmpty()) { + Util.func_181617_a((FutureTask) this.scheduledTasks.remove(0), logger); + } + } + + long l = System.nanoTime(); + + if (this.timer.elapsedTicks > 1) { + long watchdog = EagRuntime.steadyTimeMillis(); + for (int j = 0; j < this.timer.elapsedTicks; ++j) { + this.runTick(); + if (j < this.timer.elapsedTicks - 1) { + PointerInputAbstraction.runGameLoop(); + } + long millis = EagRuntime.steadyTimeMillis(); + if (millis - watchdog > 50l) { + watchdog = millis; + EagRuntime.immediateContinue(); + } + } + } else if (this.timer.elapsedTicks == 1) { + this.runTick(); + } + + long i1 = System.nanoTime() - l; + this.checkGLError("Pre render"); + this.mcSoundHandler.setListener(this.thePlayer, this.timer.renderPartialTicks); + + if (!Display.contextLost()) { + EaglercraftGPU.optimize(); + _wglBindFramebuffer(0x8D40, null); + GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); + GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); + GlStateManager.pushMatrix(); + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GlStateManager.enableTexture2D(); + if (this.thePlayer != null && this.thePlayer.isEntityInsideOpaqueBlock()) { + this.gameSettings.thirdPersonView = 0; + } + + if (!this.skipRenderWorld) { + this.entityRenderer.func_181560_a(this.timer.renderPartialTicks, i); + } + + this.guiAchievement.updateAchievementWindow(); + this.touchOverlayRenderer.render(displayWidth, displayHeight, scaledResolution); + GlStateManager.popMatrix(); + } + + this.updateDisplay(); + this.checkGLError("Post render"); + + ++this.fpsCounter; + long k = System.nanoTime(); + this.field_181542_y.func_181747_a(k - this.field_181543_z); + this.field_181543_z = k; + + while (getSystemTime() >= this.debugUpdateTime + 1000L) { + debugFPS = this.fpsCounter; + this.debug = HString.format("%d fps (%d chunk update%s) T: %s%s%s%s", + new Object[] { Integer.valueOf(debugFPS), Integer.valueOf(RenderChunk.renderChunksUpdated), + RenderChunk.renderChunksUpdated != 1 ? "s" : "", + (float) this.gameSettings.limitFramerate == GameSettings.Options.FRAMERATE_LIMIT + .getValueMax() ? "inf" : Integer.valueOf(this.gameSettings.limitFramerate), + this.gameSettings.enableVsync ? " vsync" : "", + this.gameSettings.fancyGraphics ? "" : " fast", this.gameSettings.clouds == 0 ? "" + : (this.gameSettings.clouds == 1 ? " fast-clouds" : " fancy-clouds") }); + RenderChunk.renderChunksUpdated = 0; + this.debugUpdateTime += 1000L; + this.fpsCounter = 0; + } + + if (this.isFramerateLimitBelowMax()) { + Display.sync(this.getLimitFramerate()); + } + + Mouse.tickCursorShape(); + } + + public void updateDisplay() { + if (Display.isVSyncSupported()) { + Display.setVSync(this.gameSettings.enableVsync); + } else { + this.gameSettings.enableVsync = false; + } + Display.update(); + this.checkWindowResize(); + } + + protected void checkWindowResize() { + float dpiFetch = -1.0f; + if (!this.fullscreen + && (Display.wasResized() || (dpiFetch = Math.max(Display.getDPI(), 1.0f)) != this.displayDPI)) { + int i = this.displayWidth; + int j = this.displayHeight; + float f = this.displayDPI; + this.displayWidth = Display.getWidth(); + this.displayHeight = Display.getHeight(); + this.displayDPI = dpiFetch == -1.0f ? Math.max(Display.getDPI(), 1.0f) : dpiFetch; + if (this.displayWidth != i || this.displayHeight != j || this.displayDPI != f) { + if (this.displayWidth <= 0) { + this.displayWidth = 1; + } + + if (this.displayHeight <= 0) { + this.displayHeight = 1; + } + + this.resize(this.displayWidth, this.displayHeight); + } + } + + } + + public int getLimitFramerate() { + return this.theWorld == null && this.currentScreen != null ? 30 : this.gameSettings.limitFramerate; + } + + public boolean isFramerateLimitBelowMax() { + return (float) this.getLimitFramerate() < GameSettings.Options.FRAMERATE_LIMIT.getValueMax(); + } + + /** + * + + * Called when the window is closing. Sets 'running' to false + * which allows the game loop to exit cleanly. + */ + public void shutdown() { + this.running = false; + } + + /** + * + + * Will set the focus to ingame if the Minecraft window is the + * active with focus. Also clears any GUI screen currently + * displayed + */ + public void setIngameFocus() { + boolean touch = PointerInputAbstraction.isTouchMode(); + if (touch || Display.isActive()) { + if (!this.inGameHasFocus) { + this.inGameHasFocus = true; + if (!touch && mouseGrabSupported) { + this.mouseHelper.grabMouseCursor(); + } + this.displayGuiScreen((GuiScreen) null); + this.leftClickCounter = 10000; + } + } + } + + /** + * + + * Resets the player keystate, disables the ingame focus, and + * ungrabs the mouse cursor. + */ + public void setIngameNotInFocus() { + if (this.inGameHasFocus) { + KeyBinding.unPressAllKeys(); + this.inGameHasFocus = false; + if (!PointerInputAbstraction.isTouchMode() && mouseGrabSupported) { + this.mouseHelper.ungrabMouseCursor(); + } + } + } + + /** + * + + * Displays the ingame menu + */ + public void displayInGameMenu() { + if (this.currentScreen == null) { + this.displayGuiScreen(new GuiIngameMenu()); + } + } + + private void sendClickBlockToController(boolean leftClick) { + if (!leftClick) { + this.leftClickCounter = 0; + } + + if (this.leftClickCounter <= 0 && !this.thePlayer.isUsingItem()) { + if (leftClick && this.objectMouseOver != null + && this.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + BlockPos blockpos = this.objectMouseOver.getBlockPos(); + if (this.theWorld.getBlockState(blockpos).getBlock().getMaterial() != Material.air + && this.playerController.onPlayerDamageBlock(blockpos, this.objectMouseOver.sideHit)) { + this.effectRenderer.addBlockHitEffects(blockpos, this.objectMouseOver.sideHit); + this.thePlayer.swingItem(); + } + + } else { + this.playerController.resetBlockRemoving(); + } + } + } + + private void clickMouse() { + if (this.leftClickCounter <= 0) { + if (this.objectMouseOver == null) { + logger.error("Null returned as \'hitResult\', this shouldn\'t happen!"); + if (this.playerController.isNotCreative()) { + this.leftClickCounter = 10; + } + + } else { + switch (this.objectMouseOver.typeOfHit) { + case ENTITY: + this.playerController.attackEntity(this.thePlayer, this.objectMouseOver.entityHit); + break; + case BLOCK: + BlockPos blockpos = this.objectMouseOver.getBlockPos(); + if (this.theWorld.getBlockState(blockpos).getBlock().getMaterial() != Material.air) { + this.playerController.clickBlock(blockpos, this.objectMouseOver.sideHit); + break; + } + case MISS: + default: + if (this.playerController.isNotCreative()) { + this.leftClickCounter = 10; + } + + this.thePlayer.resetCooldown(); + } + + } + + this.thePlayer.swingItem(); + } + } + + /** + * + + * Called when user clicked he's mouse right button (place) + */ + public void rightClickMouse() { + if (!this.playerController.func_181040_m()) { + this.rightClickDelayTimer = 4; + boolean flag = true; + ItemStack itemstack = this.thePlayer.inventory.getCurrentItem(); + if (this.objectMouseOver == null) { + logger.warn("Null returned as \'hitResult\', this shouldn\'t happen!"); + } else { + switch (this.objectMouseOver.typeOfHit) { + case ENTITY: + if (this.playerController.func_178894_a(this.thePlayer, this.objectMouseOver.entityHit, + this.objectMouseOver)) { + flag = false; + } else if (this.playerController.interactWithEntitySendPacket(this.thePlayer, + this.objectMouseOver.entityHit)) { + flag = false; + } + break; + case BLOCK: + BlockPos blockpos = this.objectMouseOver.getBlockPos(); + if (this.theWorld.getBlockState(blockpos).getBlock().getMaterial() != Material.air) { + int i = itemstack != null ? itemstack.stackSize : 0; + if (this.playerController.onPlayerRightClick(this.thePlayer, this.theWorld, itemstack, + blockpos, + this.objectMouseOver.sideHit, this.objectMouseOver.hitVec)) { + flag = false; + this.thePlayer.swingItem(); + } + + if (itemstack == null) { + return; + } + + if (itemstack.stackSize == 0) { + this.thePlayer.inventory.mainInventory[this.thePlayer.inventory.currentItem] = null; + } else if (itemstack.stackSize != i || this.playerController.isInCreativeMode()) { + this.entityRenderer.itemRenderer.resetEquippedProgress(); + } + } + } + } + + if (flag) { + ItemStack itemstack1 = this.thePlayer.inventory.getCurrentItem(); + if (itemstack1 != null + && this.playerController.sendUseItem(this.thePlayer, this.theWorld, itemstack1)) { + this.entityRenderer.itemRenderer.resetEquippedProgress2(); + } + } + + } + } + + /** + * + + * Toggles fullscreen mode. + */ + public void toggleFullscreen() { + Display.toggleFullscreen(); + } + + /** + * + + * Called to resize the current screen. + */ + private void resize(int width, int height) { + this.displayWidth = Math.max(1, width); + this.displayHeight = Math.max(1, height); + this.scaledResolution = new ScaledResolution(this); + if (this.currentScreen != null) { + this.currentScreen.onResize(this, scaledResolution.getScaledWidth(), scaledResolution.getScaledHeight()); + } + + this.loadingScreen = new LoadingScreenRenderer(this); + + if (voiceOverlay != null) { + voiceOverlay.setResolution(scaledResolution.getScaledWidth(), scaledResolution.getScaledHeight()); + } + if (notifRenderer != null) { + notifRenderer.setResolution(this, scaledResolution.getScaledWidth(), scaledResolution.getScaledHeight(), + scaledResolution.getScaleFactor()); + } + + EagRuntime.getConfiguration().getHooks().callScreenChangedHook( + currentScreen != null ? currentScreen.getClass().getName() : null, scaledResolution.getScaledWidth(), + scaledResolution.getScaledHeight(), displayWidth, displayHeight, scaledResolution.getScaleFactor()); + } + + public MusicTicker func_181535_r() { + return this.mcMusicTicker; + } + + /** + * + + * Runs the current tick. + */ + public void runTick() throws IOException { + CullingMod.setRequestCull(true); + if (this.rightClickDelayTimer > 0) { + --this.rightClickDelayTimer; + } + + RateLimitTracker.tick(); + + boolean isHostingLAN = LANServerController.isHostingLAN(); + this.isGamePaused = !isHostingLAN && this.isSingleplayer() && this.theWorld != null && this.thePlayer != null + && this.currentScreen != null && this.currentScreen.doesGuiPauseGame(); + + if (isLANOpen && !isHostingLAN) { + ingameGUI.getChatGUI().printChatMessage(new ChatComponentTranslation("lanServer.relayDisconnected")); + } + isLANOpen = isHostingLAN; + + if (wasPaused != isGamePaused) { + SingleplayerServerController.setPaused(this.isGamePaused); + wasPaused = isGamePaused; + } + + PlatformWebRTC.runScheduledTasks(); + WebViewOverlayController.runTick(); + SingleplayerServerController.runTick(); + RelayUpdateChecker.runTick(); + + UpdateResultObj update = UpdateService.getUpdateResult(); + if (update != null) { + if (update.isSuccess()) { + UpdateDataObj updateSuccess = update.getSuccess(); + if (EagRuntime.getConfiguration().isAllowBootMenu()) { + if (currentScreen == null || (!(currentScreen instanceof GuiUpdateDownloadSuccess) + && !(currentScreen instanceof GuiUpdateInstallOptions))) { + displayGuiScreen(new GuiUpdateDownloadSuccess(currentScreen, updateSuccess)); + } + } else { + UpdateService.quine(updateSuccess.clientSignature, updateSuccess.clientBundle); + } + } else { + displayGuiScreen( + new GuiScreenGenericErrorMessage("updateFailed.title", update.getFailure(), currentScreen)); + } + } + + if (!this.isGamePaused) { + this.ingameGUI.updateTick(); + } + + VoiceClientController.tickVoiceClient(this); + + this.entityRenderer.getMouseOver(1.0F); + if (!this.isGamePaused && this.theWorld != null) { + this.playerController.updateController(); + } + + if (this.thePlayer != null && this.thePlayer.sendQueue != null) { + this.thePlayer.sendQueue.getEaglerMessageController().flush(); + } + + if (!this.isGamePaused) { + this.renderEngine.tick(); + GlStateManager.viewport(0, 0, displayWidth, displayHeight); // to be safe + GlStateManager.enableAlpha(); + } + + if (this.currentScreen == null && this.thePlayer != null) { + if (this.thePlayer.getHealth() <= 0.0F) { + this.displayGuiScreen((GuiScreen) null); + } else if (this.thePlayer.isPlayerSleeping() && this.theWorld != null) { + this.displayGuiScreen(new GuiSleepMP()); + } + if (this.currentScreen == null && this.dontPauseTimer <= 0 && !PointerInputAbstraction.isTouchMode()) { + if (!Mouse.isMouseGrabbed()) { + this.setIngameNotInFocus(); + this.displayInGameMenu(); + } + } + } else if (this.currentScreen != null && this.currentScreen instanceof GuiSleepMP + && !this.thePlayer.isPlayerSleeping()) { + this.displayGuiScreen((GuiScreen) null); + } + + if (this.currentScreen != null) { + this.leftClickCounter = 10000; + this.dontPauseTimer = 6; + } else { + if (this.dontPauseTimer > 0) { + --this.dontPauseTimer; + } + } + + String pastedStr; + while ((pastedStr = Touch.getPastedString()) != null) { + if (this.currentScreen != null) { + this.currentScreen.fireInputEvent(EnumInputEvent.CLIPBOARD_PASTE, pastedStr); + } + } + + if (this.currentScreen != null) { + try { + this.currentScreen.handleInput(); + } catch (Throwable throwable1) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable1, "Updating screen events"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Affected screen"); + crashreportcategory.addCrashSectionCallable("Screen name", new Callable() { + public String call() throws Exception { + return Minecraft.this.currentScreen.getClass().getName(); + } + }); + throw new ReportedException(crashreport); + } + + if (this.currentScreen != null) { + try { + this.currentScreen.updateScreen(); + } catch (Throwable throwable) { + CrashReport crashreport1 = CrashReport.makeCrashReport(throwable, "Ticking screen"); + CrashReportCategory crashreportcategory1 = crashreport1.makeCategory("Affected screen"); + crashreportcategory1.addCrashSectionCallable("Screen name", new Callable() { + public String call() throws Exception { + return Minecraft.this.currentScreen.getClass().getName(); + } + }); + throw new ReportedException(crashreport1); + } + } + } + + if (this.currentScreen == null || this.currentScreen.allowUserInput) { + + boolean touched; + boolean moused = false; + + while ((touched = Touch.next()) || (moused = Mouse.next())) { + boolean touch = false; + if (touched) { + PointerInputAbstraction.enterTouchModeHook(); + boolean mouse = moused; + moused = false; + int tc = Touch.getEventTouchPointCount(); + if (tc > 0) { + for (int i = 0; i < tc; ++i) { + final int uid = Touch.getEventTouchPointUID(i); + int x = Touch.getEventTouchX(i); + int y = Touch.getEventTouchY(i); + switch (Touch.getEventType()) { + case TOUCHSTART: + if (TouchControls.handleTouchBegin(uid, x, y)) { + break; + } + touch = true; + handlePlaceTouchStart(); + break; + case TOUCHEND: + if (TouchControls.handleTouchEnd(uid, x, y)) { + touch = true; + break; + } + handlePlaceTouchEnd(); + break; + default: + break; + } + } + TouchControls.handleInput(); + if (!touch) { + continue; + } + } else { + if (!mouse) { + continue; + } + } + } + + if (!touch) { + int i = Mouse.getEventButton(); + KeyBinding.setKeyBindState(i - 100, Mouse.getEventButtonState()); + if (Mouse.getEventButtonState()) { + PointerInputAbstraction.enterMouseModeHook(); + if (this.thePlayer.isSpectator() && i == 2) { + this.ingameGUI.getSpectatorGui().func_175261_b(); + } else { + KeyBinding.onTick(i - 100); + } + } + } + + long i1 = getSystemTime() - this.systemTime; + if (i1 <= 200L) { + if (!touch) { + int j = Mouse.getEventDWheel(); + if (j != 0) { + if (this.isZoomKey) { + this.adjustedZoomValue = MathHelper.clamp_float(adjustedZoomValue - j * 4.0f, 5.0f, + 32.0f); + } else if (this.thePlayer.isSpectator()) { + j = j < 0 ? -1 : 1; + if (this.ingameGUI.getSpectatorGui().func_175262_a()) { + this.ingameGUI.getSpectatorGui().func_175259_b(-j); + } else { + float f = MathHelper.clamp_float( + this.thePlayer.capabilities.getFlySpeed() + (float) j * 0.005F, 0.0F, 0.2F); + this.thePlayer.capabilities.setFlySpeed(f); + } + } else { + this.thePlayer.inventory.changeCurrentItem(j); + } + } + } + + if (this.currentScreen == null) { + if ((!this.inGameHasFocus || !(touch || Mouse.isActuallyGrabbed())) + && (touch || Mouse.getEventButtonState())) { + this.inGameHasFocus = false; + this.setIngameFocus(); + } + } else if (this.currentScreen != null) { + if (touch) { + this.currentScreen.handleTouchInput(); + } else { + this.currentScreen.handleMouseInput(); + } + } + } + } + + if (this.leftClickCounter > 0) { + --this.leftClickCounter; + } + + processTouchMine(); + + while (Keyboard.next()) { + int k = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey(); + if (k == 0x1D && (areKeysLocked() || isFullScreen())) { + KeyBinding.setKeyBindState(gameSettings.keyBindSprint.getKeyCode(), Keyboard.getEventKeyState()); + } + KeyBinding.setKeyBindState(k, Keyboard.getEventKeyState()); + if (Keyboard.getEventKeyState()) { + KeyBinding.onTick(k); + } + + if (this.debugCrashKeyPressTime > 0L) { + if (getSystemTime() - this.debugCrashKeyPressTime >= 6000L) { + throw new ReportedException(new CrashReport("Manually triggered debug crash", new Throwable())); + } + + if (!Keyboard.isKeyDown(46) || !Keyboard.isKeyDown(61)) { + this.debugCrashKeyPressTime = -1L; + } + } else if (Keyboard.isKeyDown(46) && Keyboard.isKeyDown(61)) { + this.debugCrashKeyPressTime = getSystemTime(); + } + + this.dispatchKeypresses(); + if (Keyboard.getEventKeyState()) { + if (EaglerDeferredPipeline.instance != null) { + if (k == 62) { + DebugFramebufferView.toggleDebugView(); + } else if (k == 0xCB || k == 0xC8) { + DebugFramebufferView.switchView(-1); + } else if (k == 0xCD || k == 0xD0) { + DebugFramebufferView.switchView(1); + } + } + + if (this.currentScreen != null) { + this.currentScreen.handleKeyboardInput(); + } else { + if (k == 1 || (k > -1 && k == this.gameSettings.keyBindClose.getKeyCode())) { + this.displayInGameMenu(); + } + + if (k == 32 && Keyboard.isKeyDown(61) && this.ingameGUI != null) { + this.ingameGUI.getChatGUI().clearChatMessages(); + } + + if (k == 31 && Keyboard.isKeyDown(61)) { + this.refreshResources(); + } + + if (k == 19 && Keyboard.isKeyDown(61)) { // F3+R + if (gameSettings.shaders) { + ShaderSource.clearCache(); + this.renderGlobal.loadRenderers(); + } + } + + if (k == 17 && Keyboard.isKeyDown(61)) { + ; + } + + if (k == 18 && Keyboard.isKeyDown(61)) { + ; + } + + if (k == 47 && Keyboard.isKeyDown(61)) { + ; + } + + if (k == 38 && Keyboard.isKeyDown(61)) { + ; + } + + if (k == 22 && Keyboard.isKeyDown(61)) { + ; + } + + if (k == 20 && Keyboard.isKeyDown(61)) { + this.refreshResources(); + } + + if (k == 33 && Keyboard.isKeyDown(61)) { + this.gameSettings.setOptionValue(GameSettings.Options.RENDER_DISTANCE, + GuiScreen.isShiftKeyDown() ? -1 : 1); + } + + if (k == 30 && Keyboard.isKeyDown(61)) { + GlStateManager.recompileShaders(); + this.renderGlobal.loadRenderers(); + } + + if (k == 35 && Keyboard.isKeyDown(61)) { + this.gameSettings.advancedItemTooltips = !this.gameSettings.advancedItemTooltips; + this.gameSettings.saveOptions(); + } + + if (k == 48 && Keyboard.isKeyDown(61)) { + this.renderManager.setDebugBoundingBox(!this.renderManager.isDebugBoundingBox()); + } + + if (k == 25 && Keyboard.isKeyDown(61)) { + this.gameSettings.pauseOnLostFocus = !this.gameSettings.pauseOnLostFocus; + this.gameSettings.saveOptions(); + } + + if (k == 59) { + this.gameSettings.hideGUI = !this.gameSettings.hideGUI; + } + + if (k == 61) { + this.gameSettings.showDebugInfo = !this.gameSettings.showDebugInfo; + this.gameSettings.showDebugProfilerChart = GuiScreen.isShiftKeyDown(); + this.gameSettings.field_181657_aC = GuiScreen.isAltKeyDown(); + } + + if (this.gameSettings.keyBindTogglePerspective.isPressed()) { + togglePerspective(); + } + + if (this.gameSettings.keyBindSmoothCamera.isPressed()) { + this.gameSettings.smoothCamera = !this.gameSettings.smoothCamera; + } + } + } + } + + for (int l = 0; l < 9; ++l) { + if (this.gameSettings.keyBindsHotbar[l].isPressed()) { + if (this.thePlayer.isSpectator()) { + this.ingameGUI.getSpectatorGui().func_175260_a(l); + } else { + this.thePlayer.inventory.currentItem = l; + } + } + } + + boolean zoomKey = this.gameSettings.keyBindZoomCamera.isKeyDown(); + if (zoomKey != isZoomKey) { + adjustedZoomValue = startZoomValue; + isZoomKey = zoomKey; + } + + boolean flag = this.gameSettings.chatVisibility != EntityPlayer.EnumChatVisibility.HIDDEN; + + while (this.gameSettings.keyBindInventory.isPressed()) { + if (this.playerController.isRidingHorse()) { + this.thePlayer.sendHorseInventory(); + } else { + this.getNetHandler().addToSendQueue( + new C16PacketClientStatus(C16PacketClientStatus.EnumState.OPEN_INVENTORY_ACHIEVEMENT)); + this.displayGuiScreen(new GuiInventory(this.thePlayer)); + } + } + + while (this.gameSettings.keyBindDrop.isPressed()) { + if (!this.thePlayer.isSpectator()) { + this.thePlayer.dropOneItem(GuiScreen.isCtrlKeyDown()); + } + } + + while (this.gameSettings.keyBindChat.isPressed() && flag) { + this.displayGuiScreen(new GuiChat()); + } + + if (this.currentScreen == null && this.gameSettings.keyBindCommand.isPressed() && flag) { + this.displayGuiScreen(new GuiChat("/")); + } + + boolean miningTouch = isMiningTouch(); + boolean useTouch = thePlayer.getItemShouldUseOnTouchEagler(); + if (this.thePlayer.isUsingItem()) { + if (!this.gameSettings.keyBindUseItem.isKeyDown() && !miningTouch) { + this.playerController.onStoppedUsingItem(this.thePlayer); + } + + while (this.gameSettings.keyBindAttack.isPressed()) { + ; + } + + while (this.gameSettings.keyBindUseItem.isPressed()) { + ; + } + + while (this.gameSettings.keyBindPickBlock.isPressed()) { + ; + } + } else { + while (this.gameSettings.keyBindAttack.isPressed()) { + this.clickMouse(); + } + + if (miningTouch && !wasMiningTouch) { + if ((objectMouseOver != null && objectMouseOver.typeOfHit == MovingObjectType.ENTITY) || useTouch) { + this.rightClickMouse(); + } else { + this.clickMouse(); + } + wasMiningTouch = true; + } + + while (this.gameSettings.keyBindUseItem.isPressed()) { + this.rightClickMouse(); + } + + while (this.gameSettings.keyBindPickBlock.isPressed()) { + this.middleClickMouse(); + } + } + wasMiningTouch = miningTouch; + + if (this.gameSettings.keyBindUseItem.isKeyDown() && this.rightClickDelayTimer == 0 + && !this.thePlayer.isUsingItem()) { + this.rightClickMouse(); + } + + if (miningTouch && useTouch && this.rightClickDelayTimer == 0 && !this.thePlayer.isUsingItem()) { + this.rightClickMouse(); + } + + this.sendClickBlockToController( + this.currentScreen == null && (this.gameSettings.keyBindAttack.isKeyDown() || miningTouch) + && this.inGameHasFocus && !useTouch); + } + + if (this.theWorld != null) { + if (this.thePlayer != null) { + ++this.joinPlayerCounter; + if (this.joinPlayerCounter == 30) { + this.joinPlayerCounter = 0; + this.theWorld.joinEntityInSurroundings(this.thePlayer); + } + } + + if (!this.isGamePaused) { + this.entityRenderer.updateRenderer(); + } + + if (!this.isGamePaused) { + this.renderGlobal.updateClouds(); + } + this.mcProfiler.endStartSection("processRenderGlobalLightUpdates"); + if (!isGamePaused) + ((ILightUpdatesProcessor) renderGlobal).alfheim$processLightUpdates(); + + if (!this.isGamePaused) { + if (this.theWorld.getLastLightningBolt() > 0) { + this.theWorld.setLastLightningBolt(this.theWorld.getLastLightningBolt() - 1); + } + + this.theWorld.updateEntities(); + } + this.eagskullCommand.tick(); + } else if (this.entityRenderer.isShaderActive()) { + this.entityRenderer.func_181022_b(); + } + + if (!this.isGamePaused && Config.audioEnabled()) { + this.mcMusicTicker.update(); + this.mcSoundHandler.update(); + } + + if (this.theWorld != null) { + if (!this.isGamePaused) { + this.theWorld.setAllowedSpawnTypes(this.theWorld.getDifficulty() != EnumDifficulty.PEACEFUL, true); + + try { + this.theWorld.tick(); + } catch (Throwable throwable2) { + CrashReport crashreport2 = CrashReport.makeCrashReport(throwable2, "Exception in world tick"); + if (this.theWorld == null) { + CrashReportCategory crashreportcategory2 = crashreport2.makeCategory("Affected level"); + crashreportcategory2.addCrashSection("Problem", "Level is null!"); + } else { + this.theWorld.addWorldInfoToCrashReport(crashreport2); + } + + throw new ReportedException(crashreport2); + } + } + + // if (!this.isGamePaused && this.theWorld != null) { + // this.theWorld.doVoidFogParticles(MathHelper.floor_double(this.thePlayer.posX), + // MathHelper.floor_double(this.thePlayer.posY), MathHelper.floor_double(this.thePlayer.posZ)); + // } + + if (!this.isGamePaused) { + this.effectRenderer.updateEffects(); + } + } else if (this.myNetworkManager != null) { + this.myNetworkManager.processReceivedPackets(); + } + + if (this.theWorld != null) { + ++joinWorldTickCounter; + if (bungeeOutdatedMsgTimer > 0) { + if (--bungeeOutdatedMsgTimer == 0 && this.thePlayer.sendQueue != null) { + String pluginBrand = this.thePlayer.sendQueue.getNetworkManager().getPluginBrand(); + String pluginVersion = this.thePlayer.sendQueue.getNetworkManager().getPluginVersion(); + if (pluginBrand != null && pluginVersion != null + && EaglerXBungeeVersion.isUpdateToPluginAvailable(pluginBrand, pluginVersion)) { + String pfx = EnumChatFormatting.GOLD + "[EagX]" + EnumChatFormatting.AQUA; + ingameGUI.getChatGUI().printChatMessage( + new ChatComponentText(pfx + " ---------------------------------------")); + ingameGUI.getChatGUI().printChatMessage( + new ChatComponentText(pfx + " This server appears to be using version " + + EnumChatFormatting.YELLOW + pluginVersion)); + ingameGUI.getChatGUI().printChatMessage( + new ChatComponentText(pfx + " of the EaglerXBungee plugin which is outdated")); + ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); + ingameGUI.getChatGUI() + .printChatMessage(new ChatComponentText(pfx + " If you are the admin update to " + + EnumChatFormatting.YELLOW + EaglerXBungeeVersion.getPluginVersion() + + EnumChatFormatting.AQUA + " or newer")); + ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); + ingameGUI.getChatGUI().printChatMessage((new ChatComponentText(pfx + " Click: ")) + .appendSibling((new ChatComponentText("" + EnumChatFormatting.GREEN + + EnumChatFormatting.UNDERLINE + EaglerXBungeeVersion.getPluginButton())) + .setChatStyle((new ChatStyle()).setChatClickEvent( + new ClickEvent(ClickEvent.Action.EAGLER_PLUGIN_DOWNLOAD, + "plugin_download.zip"))))); + ingameGUI.getChatGUI().printChatMessage( + new ChatComponentText(pfx + " ---------------------------------------")); + } + } + } + } else { + joinWorldTickCounter = 0; + if (currentScreen != null && currentScreen.shouldHangupIntegratedServer()) { + if (SingleplayerServerController.hangupEaglercraftServer()) { + this.displayGuiScreen(new GuiScreenIntegratedServerBusy(currentScreen, + "singleplayer.busy.stoppingIntegratedServer", + "singleplayer.failed.stoppingIntegratedServer", SingleplayerServerController::isReady)); + } + } + TouchControls.resetSneak(); + } + + if (reconnectURI != null) { + String reconURI = reconnectURI; + reconnectURI = null; + if (EagRuntime.getConfiguration().isAllowServerRedirects()) { + boolean enableCookies; + boolean msg; + if (this.currentServerData != null) { + enableCookies = this.currentServerData.enableCookies; + msg = false; + } else { + enableCookies = EagRuntime.getConfiguration().isEnableServerCookies(); + msg = true; + } + if (theWorld != null) { + theWorld.sendQuittingDisconnectingPacket(); + loadWorld(null); + } + logger.info("Recieved SPacketRedirectClientV4EAG, reconnecting to: {}", reconURI); + if (msg) { + logger.warn("No existing server connection, cookies will default to {}!", + enableCookies ? "enabled" : "disabled"); + } + ServerAddress addr = AddressResolver.resolveAddressFromURI(reconURI); + this.displayGuiScreen( + new GuiConnecting(new GuiMainMenu(), this, addr.getIP(), addr.getPort(), enableCookies)); + } else { + logger.warn("Server redirect blocked: {}", reconURI); + } + } + + this.systemTime = getSystemTime(); + } + + private long placeTouchStartTime = -1l; + private long mineTouchStartTime = -1l; + private boolean wasMiningTouch = false; + + private void processTouchMine() { + if ((currentScreen == null || currentScreen.allowUserInput) + && PointerInputAbstraction.isTouchingScreenNotButton()) { + if (PointerInputAbstraction.isDraggingNotTouching()) { + if (mineTouchStartTime != -1l) { + long l = EagRuntime.steadyTimeMillis(); + if ((placeTouchStartTime == -1l || (l - placeTouchStartTime) < 350l) + || (l - mineTouchStartTime) < 350l) { + mineTouchStartTime = -1l; + } + } + } else { + if (mineTouchStartTime == -1l) { + mineTouchStartTime = EagRuntime.steadyTimeMillis(); + } + } + } else { + mineTouchStartTime = -1l; + } + } + + private boolean isMiningTouch() { + if (mineTouchStartTime == -1l) + return false; + long l = EagRuntime.steadyTimeMillis(); + return (placeTouchStartTime == -1l || (l - placeTouchStartTime) >= 350l) && (l - mineTouchStartTime) >= 350l; + } + + private void handlePlaceTouchStart() { + if (placeTouchStartTime == -1l) { + placeTouchStartTime = EagRuntime.steadyTimeMillis(); + } + } + + private void handlePlaceTouchEnd() { + if (placeTouchStartTime != -1l) { + int len = (int) (EagRuntime.steadyTimeMillis() - placeTouchStartTime); + if (len < 350l && !PointerInputAbstraction.isDraggingNotTouching()) { + if (objectMouseOver != null && objectMouseOver.typeOfHit == MovingObjectType.ENTITY) { + clickMouse(); + } else { + rightClickMouse(); + } + } + placeTouchStartTime = -1l; + } + } + + public void togglePerspective() { + ++this.gameSettings.thirdPersonView; + if (this.gameSettings.thirdPersonView > 2) { + this.gameSettings.thirdPersonView = 0; + } + + if (this.gameSettings.thirdPersonView == 0) { + this.entityRenderer.loadEntityShader(this.getRenderViewEntity()); + } else if (this.gameSettings.thirdPersonView == 1) { + this.entityRenderer.loadEntityShader((Entity) null); + } + + this.renderGlobal.setDisplayListEntitiesDirty(); + } + + /**+ + * Arguments: World foldername, World ingame name, WorldSettings + */ + public void launchIntegratedServer(String folderName, String worldName, WorldSettings worldSettingsIn) { + this.loadWorld((WorldClient) null); + renderManager.setEnableFNAWSkins(this.gameSettings.enableFNAWSkins); + session.reset(); + EaglerProfile.clearServerSkinOverride(); + PauseMenuCustomizeState.reset(); + SingleplayerServerController.launchEaglercraftServer(folderName, gameSettings.difficulty.getDifficultyId(), + Math.max(gameSettings.renderDistanceChunks, 2), worldSettingsIn); + EagRuntime.setMCServerWindowGlobal("singleplayer"); + this.displayGuiScreen(new GuiScreenIntegratedServerBusy( + new GuiScreenSingleplayerConnecting(new GuiMainMenu(), "Connecting to " + folderName), + "singleplayer.busy.startingIntegratedServer", "singleplayer.failed.startingIntegratedServer", + () -> SingleplayerServerController.isWorldReady(), (t, u) -> { + Minecraft.this.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiMainMenu(), + ((GuiScreenIntegratedServerBusy) t).failMessage, u)); + })); + } + + /** + * + + * unloads the current world first + */ + public void loadWorld(WorldClient worldClientIn) { + this.loadWorld(worldClientIn, ""); + } + + /** + * + + * unloads the current world first + */ + public void loadWorld(WorldClient worldClientIn, String loadingMessage) { + if (worldClientIn != theWorld) { + this.entityRenderer.getMapItemRenderer().clearLoadedMaps(); + } + if (worldClientIn == null) { + NetHandlerPlayClient nethandlerplayclient = this.getNetHandler(); + if (nethandlerplayclient != null) { + nethandlerplayclient.cleanup(); + } + session.reset(); + EaglerProfile.clearServerSkinOverride(); + PauseMenuCustomizeState.reset(); + ClientUUIDLoadingCache.flushRequestCache(); + WebViewOverlayController.setPacketSendCallback(null); + + this.guiAchievement.clearAchievements(); + this.entityRenderer.getMapItemRenderer().clearLoadedMaps(); + } + + this.renderViewEntity = null; + this.myNetworkManager = null; + if (this.loadingScreen != null) { + this.loadingScreen.resetProgressAndMessage(loadingMessage); + this.loadingScreen.displayLoadingString(""); + } + + if (worldClientIn == null && this.theWorld != null) { + this.mcResourcePackRepository.func_148529_f(); + this.ingameGUI.func_181029_i(); + this.setServerData((ServerData) null); + this.integratedServerIsRunning = false; + } + + this.mcSoundHandler.stopSounds(); + this.theWorld = worldClientIn; + if (worldClientIn != null) { + if (this.renderGlobal != null) { + this.renderGlobal.setWorldAndLoadRenderers(worldClientIn); + } + + if (this.effectRenderer != null) { + this.effectRenderer.clearEffects(worldClientIn); + } + + if (this.thePlayer == null) { + this.thePlayer = this.playerController.func_178892_a(worldClientIn, new StatFileWriter()); + this.playerController.flipPlayer(this.thePlayer); + } + + this.thePlayer.preparePlayerToSpawn(); + worldClientIn.spawnEntityInWorld(this.thePlayer); + this.thePlayer.movementInput = new MovementInputFromOptions(this.gameSettings); + this.playerController.setPlayerCapabilities(this.thePlayer); + this.renderViewEntity = this.thePlayer; + } else { + this.thePlayer = null; + } + + System.gc(); + this.systemTime = 0L; + } + + public void setDimensionAndSpawnPlayer(int dimension) { + this.theWorld.setInitialSpawnLocation(); + this.theWorld.removeAllEntities(); + int i = 0; + String s = null; + if (this.thePlayer != null) { + i = this.thePlayer.getEntityId(); + this.theWorld.removeEntity(this.thePlayer); + s = this.thePlayer.getClientBrand(); + } + + this.renderViewEntity = null; + EntityPlayerSP entityplayersp = this.thePlayer; + this.thePlayer = this.playerController.func_178892_a(this.theWorld, new StatFileWriter()); + this.thePlayer.getDataWatcher().updateWatchedObjectsFromList(entityplayersp.getDataWatcher().getAllWatched()); + this.thePlayer.dimension = dimension; + this.renderViewEntity = this.thePlayer; + this.thePlayer.preparePlayerToSpawn(); + this.thePlayer.setClientBrand(s); + this.theWorld.spawnEntityInWorld(this.thePlayer); + this.playerController.flipPlayer(this.thePlayer); + this.thePlayer.movementInput = new MovementInputFromOptions(this.gameSettings); + this.thePlayer.setEntityId(i); + this.playerController.setPlayerCapabilities(this.thePlayer); + this.thePlayer.setReducedDebug(entityplayersp.hasReducedDebug()); + if (this.currentScreen instanceof GuiGameOver) { + this.displayGuiScreen((GuiScreen) null); + } + + } + + /** + * + + * Gets whether this is a demo or not. + */ + public final boolean isDemo() { + return EagRuntime.getConfiguration().isDemo(); + } + + public NetHandlerPlayClient getNetHandler() { + return this.thePlayer != null ? this.thePlayer.sendQueue : null; + } + + public static boolean isGuiEnabled() { + return theMinecraft == null || !theMinecraft.gameSettings.hideGUI; + } + + public static boolean isFancyGraphicsEnabled() { + return theMinecraft != null && theMinecraft.gameSettings.fancyGraphics; + } + + /** + * + + * Returns if ambient occlusion is enabled + */ + public static boolean isAmbientOcclusionEnabled() { + if (theMinecraft == null) + return false; + GameSettings g = theMinecraft.gameSettings; + return g.ambientOcclusion != 0 && !g.shadersAODisable; + } + + /** + * + + * Called when user clicked he's mouse middle button (pick + * block) + */ + public void middleClickMouse() { + if (this.objectMouseOver != null) { + boolean flag = this.thePlayer.capabilities.isCreativeMode; + int i = 0; + boolean flag1 = false; + TileEntity tileentity = null; + Object object; + if (this.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + BlockPos blockpos = this.objectMouseOver.getBlockPos(); + Block block = this.theWorld.getBlockState(blockpos).getBlock(); + if (block.getMaterial() == Material.air) { + return; + } + + object = block.getItem(this.theWorld, blockpos); + if (object == null) { + return; + } + + if (flag && GuiScreen.isCtrlKeyDown()) { + tileentity = this.theWorld.getTileEntity(blockpos); + } + + Block block1 = object instanceof ItemBlock && !block.isFlowerPot() + ? Block.getBlockFromItem((Item) object) + : block; + i = block1.getDamageValue(this.theWorld, blockpos); + flag1 = ((Item) object).getHasSubtypes(); + } else { + if (this.objectMouseOver.typeOfHit != MovingObjectPosition.MovingObjectType.ENTITY + || this.objectMouseOver.entityHit == null || !flag) { + return; + } + + if (this.objectMouseOver.entityHit instanceof EntityPainting) { + object = Items.painting; + } else if (this.objectMouseOver.entityHit instanceof EntityLeashKnot) { + object = Items.lead; + } else if (this.objectMouseOver.entityHit instanceof EntityItemFrame) { + EntityItemFrame entityitemframe = (EntityItemFrame) this.objectMouseOver.entityHit; + ItemStack itemstack = entityitemframe.getDisplayedItem(); + if (itemstack == null) { + object = Items.item_frame; + } else { + object = itemstack.getItem(); + i = itemstack.getMetadata(); + flag1 = true; + } + } else if (this.objectMouseOver.entityHit instanceof EntityMinecart) { + EntityMinecart entityminecart = (EntityMinecart) this.objectMouseOver.entityHit; + switch (entityminecart.getMinecartType()) { + case FURNACE: + object = Items.furnace_minecart; + break; + case CHEST: + object = Items.chest_minecart; + break; + case TNT: + object = Items.tnt_minecart; + break; + case HOPPER: + object = Items.hopper_minecart; + break; + case COMMAND_BLOCK: + object = Items.command_block_minecart; + break; + default: + object = Items.minecart; + } + } else if (this.objectMouseOver.entityHit instanceof EntityBoat) { + object = Items.boat; + } else if (this.objectMouseOver.entityHit instanceof EntityArmorStand) { + object = Items.armor_stand; + } else { + object = Items.spawn_egg; + i = EntityList.getEntityID(this.objectMouseOver.entityHit); + flag1 = true; + if (!EntityList.entityEggs.containsKey(Integer.valueOf(i))) { + return; + } + } + } + + InventoryPlayer inventoryplayer = this.thePlayer.inventory; + if (tileentity == null) { + inventoryplayer.setCurrentItem((Item) object, i, flag1, flag); + } else { + ItemStack itemstack1 = this.func_181036_a((Item) object, i, tileentity); + inventoryplayer.setInventorySlotContents(inventoryplayer.currentItem, itemstack1); + } + + if (flag) { + int j = this.thePlayer.inventoryContainer.inventorySlots.size() - 9 + inventoryplayer.currentItem; + this.playerController.sendSlotPacket(inventoryplayer.getStackInSlot(inventoryplayer.currentItem), j); + } + + } + } + + private ItemStack func_181036_a(Item parItem, int parInt1, TileEntity parTileEntity) { + ItemStack itemstack = new ItemStack(parItem, 1, parInt1); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + parTileEntity.writeToNBT(nbttagcompound); + if (parItem == Items.skull && nbttagcompound.hasKey("Owner")) { + NBTTagCompound nbttagcompound2 = nbttagcompound.getCompoundTag("Owner"); + NBTTagCompound nbttagcompound3 = new NBTTagCompound(); + nbttagcompound3.setTag("SkullOwner", nbttagcompound2); + itemstack.setTagCompound(nbttagcompound3); + return itemstack; + } else { + itemstack.setTagInfo("BlockEntityTag", nbttagcompound); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + NBTTagList nbttaglist = new NBTTagList(); + nbttaglist.appendTag(new NBTTagString("(+NBT)")); + nbttagcompound1.setTag("Lore", nbttaglist); + itemstack.setTagInfo("display", nbttagcompound1); + return itemstack; + } + } + + /** + * + + * adds core server Info (GL version , Texture pack, isModded, + * type), and the worldInfo to the crash report + */ + public CrashReport addGraphicsAndWorldToCrashReport(CrashReport theCrash) { + theCrash.getCategory().addCrashSectionCallable("Launched Version", new Callable() { + public String call() throws Exception { + return Minecraft.this.launchedVersion; + } + }); + theCrash.getCategory().addCrashSectionCallable("LWJGL", new Callable() { + public String call() { + return EagRuntime.getVersion(); + } + }); + theCrash.getCategory().addCrashSectionCallable("OpenGL", new Callable() { + public String call() { + return EaglercraftGPU.glGetString(7937) + " GL version " + EaglercraftGPU.glGetString(7938) + ", " + + EaglercraftGPU.glGetString(7936); + } + }); + theCrash.getCategory().addCrashSectionCallable("Is Eagler Shaders", new Callable() { + public String call() throws Exception { + return Minecraft.this.gameSettings.shaders ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("Is Dynamic Lights", new Callable() { + public String call() throws Exception { + return !Minecraft.this.gameSettings.shaders && Minecraft.this.gameSettings.enableDynamicLights ? "Yes" + : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("In Ext. Pipeline", new Callable() { + public String call() throws Exception { + return GlStateManager.isExtensionPipeline() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("GPU Shader5 Capable", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.checkShader5Capable() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("GPU TexStorage Capable", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.checkTexStorageCapable() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("GPU TextureLOD Capable", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.checkTextureLODCapable() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("GPU Instancing Capable", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.checkInstancingCapable() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("GPU VAO Capable", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.checkVAOCapable() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("Is Software VAOs", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.areVAOsEmulated() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("GPU Render-to-MipMap", new Callable() { + public String call() throws Exception { + return EaglercraftGPU.checkFBORenderMipmapCapable() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("Touch Mode", new Callable() { + public String call() throws Exception { + return PointerInputAbstraction.isTouchMode() ? "Yes" : "No"; + } + }); + theCrash.getCategory().addCrashSectionCallable("Is Modded", new Callable() { + public String call() throws Exception { + return "Definitely Not; You're an eagler"; + } + }); + theCrash.getCategory().addCrashSectionCallable("Type", new Callable() { + public String call() throws Exception { + return "Client (map_client.txt)"; + } + }); + theCrash.getCategory().addCrashSectionCallable("Resource Packs", new Callable() { + public String call() throws Exception { + StringBuilder stringbuilder = new StringBuilder(); + + for (String s : Minecraft.this.gameSettings.resourcePacks) { + if (stringbuilder.length() > 0) { + stringbuilder.append(", "); + } + + stringbuilder.append(s); + if (Minecraft.this.gameSettings.field_183018_l.contains(s)) { + stringbuilder.append(" (incompatible)"); + } + } + + return stringbuilder.toString(); + } + }); + theCrash.getCategory().addCrashSectionCallable("Current Language", new Callable() { + public String call() throws Exception { + return Minecraft.this.mcLanguageManager.getCurrentLanguage().toString(); + } + }); + theCrash.getCategory().addCrashSectionCallable("Profiler Position", new Callable() { + public String call() throws Exception { + return "N/A (disabled)"; + } + }); + if (this.theWorld != null) { + this.theWorld.addWorldInfoToCrashReport(theCrash); + } + + return theCrash; + } + + /** + * + + * Return the singleton Minecraft instance for the game + */ + public static Minecraft getMinecraft() { + return theMinecraft; + } + + public ListenableFuture scheduleResourcesRefresh() { + return this.addScheduledTaskFuture(new Runnable() { + public void run() { + Minecraft.this.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), + I18n.format("resourcePack.load.pleaseWait")); + Minecraft.this.refreshResources(); + } + }); + } + + private String func_181538_aA() { + return this.currentServerData != null ? "multiplayer" : "out_of_game"; + } + + /** + * + + * Returns whether snooping is enabled or not. + */ + public boolean isSnooperEnabled() { + return this.gameSettings.snooperEnabled; + } + + /** + * + + * Set the current ServerData instance. + */ + public void setServerData(ServerData serverDataIn) { + this.currentServerData = serverDataIn; + EagRuntime.setMCServerWindowGlobal(serverDataIn != null ? serverDataIn.serverIP : null); + } + + public ServerData getCurrentServerData() { + return this.currentServerData; + } + + public boolean isIntegratedServerRunning() { + return SingleplayerServerController.isWorldRunning(); + } + + /** + * + + * Returns true if there is only one player playing, and the + * current server is the integrated one. + */ + public boolean isSingleplayer() { + return SingleplayerServerController.isWorldRunning(); + } + + public static void stopIntegratedServer() { + + } + + /** + * + + * Gets the system time in milliseconds. + */ + public static long getSystemTime() { + return EagRuntime.steadyTimeMillis(); + } + + /** + * + + * Returns whether we're in full screen or not. + */ + public boolean isFullScreen() { + return Display.isFullscreen(); + } + + public Session getSession() { + return this.session; + } + + public TextureManager getTextureManager() { + return this.renderEngine; + } + + public IResourceManager getResourceManager() { + return this.mcResourceManager; + } + + public ResourcePackRepository getResourcePackRepository() { + return this.mcResourcePackRepository; + } + + public LanguageManager getLanguageManager() { + return this.mcLanguageManager; + } + + public TextureMap getTextureMapBlocks() { + return this.textureMapBlocks; + } + + public boolean isJava64bit() { + return this.jvm64bit; + } + + public boolean isGamePaused() { + return this.isGamePaused; + } + + public SoundHandler getSoundHandler() { + return this.mcSoundHandler; + } + + public MusicTicker.MusicType getAmbientMusicType() { + return this.thePlayer != null ? (this.thePlayer.worldObj.provider instanceof WorldProviderHell + ? MusicTicker.MusicType.NETHER + : (this.thePlayer.worldObj.provider instanceof WorldProviderEnd + ? (BossStatus.bossName != null && BossStatus.statusBarTime > 0 ? MusicTicker.MusicType.END_BOSS + : MusicTicker.MusicType.END) + : (this.thePlayer.capabilities.isCreativeMode && this.thePlayer.capabilities.allowFlying + ? MusicTicker.MusicType.CREATIVE + : MusicTicker.MusicType.GAME))) + : MusicTicker.MusicType.MENU; + } + + public void dispatchKeypresses() { + int i = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() : Keyboard.getEventKey(); + if (i != 0 && !Keyboard.isRepeatEvent()) { + if (!(this.currentScreen instanceof GuiControls) + || ((GuiControls) this.currentScreen).time <= getSystemTime() - 20L) { + if (Keyboard.getEventKeyState()) { + if (i == this.gameSettings.keyBindScreenshot.getKeyCode()) { + this.ingameGUI.getChatGUI().printChatMessage(ScreenShotHelper.saveScreenshot()); + } + } + } + } + } + + public Entity getRenderViewEntity() { + return this.renderViewEntity; + } + + public void setRenderViewEntity(Entity viewingEntity) { + this.renderViewEntity = viewingEntity; + this.entityRenderer.loadEntityShader(viewingEntity); + } + + public ListenableFuture addScheduledTaskFuture(Callable callableToSchedule) { + Validate.notNull(callableToSchedule); + ListenableFutureTask listenablefuturetask = ListenableFutureTask.create(callableToSchedule); + synchronized (this.scheduledTasks) { + this.scheduledTasks.add(listenablefuturetask); + return listenablefuturetask; + } + } + + public ListenableFuture addScheduledTaskFuture(Runnable runnableToSchedule) { + Validate.notNull(runnableToSchedule); + return this.addScheduledTaskFuture(Executors.callable(runnableToSchedule)); + } + + public void addScheduledTask(Runnable runnableToSchedule) { + this.addScheduledTaskFuture(Executors.callable(runnableToSchedule)); + } + + public BlockRendererDispatcher getBlockRendererDispatcher() { + return this.blockRenderDispatcher; + } + + public RenderManager getRenderManager() { + return this.renderManager; + } + + public RenderItem getRenderItem() { + return this.renderItem; + } + + public ItemRenderer getItemRenderer() { + return this.itemRenderer; + } + + public static int getDebugFPS() { + return debugFPS; + } + + public FrameTimer func_181539_aj() { + return this.field_181542_y; + } + + public boolean func_181540_al() { + return this.field_181541_X; + } + + public void func_181537_a(boolean parFlag) { + this.field_181541_X = parFlag; + } + + /** + * + + * Used in the usage snooper. + */ + public static int getGLMaximumTextureSize() { + return EaglercraftGPU.glGetInteger(GL_MAX_TEXTURE_SIZE); + } + + public boolean areKeysLocked() { + return PlatformInput.lockKeys; + } + + public ModelManager getModelManager() { + return modelManager; + } + + public float getRenderPartialTicks() + { + return this.timer.renderPartialTicks; + } + + /** + * + + * Returns the save loader that is currently being used + */ + public ISaveFormat getSaveLoader() { + return SingleplayerServerController.instance; + } + + public void clearTitles() { + ingameGUI.displayTitle(null, null, -1, -1, -1); + } + + public boolean getEnableFNAWSkins() { + boolean ret = this.gameSettings.enableFNAWSkins; + if (this.thePlayer != null) { + if (this.thePlayer.sendQueue.currentFNAWSkinForcedState) { + ret = true; + } else { + ret &= this.thePlayer.sendQueue.currentFNAWSkinAllowedState; + } + } + return ret; + } + + public void handleReconnectPacket(String redirectURI) { + this.reconnectURI = redirectURI; + } + + public boolean isEnableProfanityFilter() { + return EagRuntime.getConfiguration().isForceProfanityFilter() || gameSettings.enableProfanityFilter; + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/audio/ElytraSound.java b/src/game/java/net/minecraft/client/audio/ElytraSound.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/ElytraSound.java rename to src/game/java/net/minecraft/client/audio/ElytraSound.java diff --git a/src/main/java/net/minecraft/client/audio/GuardianSound.java b/src/game/java/net/minecraft/client/audio/GuardianSound.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/GuardianSound.java rename to src/game/java/net/minecraft/client/audio/GuardianSound.java diff --git a/src/main/java/net/minecraft/client/audio/ISound.java b/src/game/java/net/minecraft/client/audio/ISound.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/ISound.java rename to src/game/java/net/minecraft/client/audio/ISound.java diff --git a/src/main/java/net/minecraft/client/audio/ISoundEventAccessor.java b/src/game/java/net/minecraft/client/audio/ISoundEventAccessor.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/ISoundEventAccessor.java rename to src/game/java/net/minecraft/client/audio/ISoundEventAccessor.java diff --git a/src/main/java/net/minecraft/client/audio/ITickableSound.java b/src/game/java/net/minecraft/client/audio/ITickableSound.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/ITickableSound.java rename to src/game/java/net/minecraft/client/audio/ITickableSound.java diff --git a/src/main/java/net/minecraft/client/audio/MovingSound.java b/src/game/java/net/minecraft/client/audio/MovingSound.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/MovingSound.java rename to src/game/java/net/minecraft/client/audio/MovingSound.java diff --git a/src/main/java/net/minecraft/client/audio/MovingSoundMinecart.java b/src/game/java/net/minecraft/client/audio/MovingSoundMinecart.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/MovingSoundMinecart.java rename to src/game/java/net/minecraft/client/audio/MovingSoundMinecart.java diff --git a/src/main/java/net/minecraft/client/audio/MovingSoundMinecartRiding.java b/src/game/java/net/minecraft/client/audio/MovingSoundMinecartRiding.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/MovingSoundMinecartRiding.java rename to src/game/java/net/minecraft/client/audio/MovingSoundMinecartRiding.java diff --git a/src/main/java/net/minecraft/client/audio/MusicTicker.java b/src/game/java/net/minecraft/client/audio/MusicTicker.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/MusicTicker.java rename to src/game/java/net/minecraft/client/audio/MusicTicker.java diff --git a/src/main/java/net/minecraft/client/audio/PositionedSound.java b/src/game/java/net/minecraft/client/audio/PositionedSound.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/PositionedSound.java rename to src/game/java/net/minecraft/client/audio/PositionedSound.java diff --git a/src/main/java/net/minecraft/client/audio/PositionedSoundRecord.java b/src/game/java/net/minecraft/client/audio/PositionedSoundRecord.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/PositionedSoundRecord.java rename to src/game/java/net/minecraft/client/audio/PositionedSoundRecord.java diff --git a/src/main/java/net/minecraft/client/audio/SoundCategory.java b/src/game/java/net/minecraft/client/audio/SoundCategory.java similarity index 98% rename from src/main/java/net/minecraft/client/audio/SoundCategory.java rename to src/game/java/net/minecraft/client/audio/SoundCategory.java index ff7b4af..2b1fcb6 100644 --- a/src/main/java/net/minecraft/client/audio/SoundCategory.java +++ b/src/game/java/net/minecraft/client/audio/SoundCategory.java @@ -35,7 +35,7 @@ import com.google.common.collect.Maps; */ public enum SoundCategory { MASTER("master", 0), MUSIC("music", 1), RECORDS("record", 2), WEATHER("weather", 3), BLOCKS("block", 4), - MOBS("hostile", 5), ANIMALS("neutral", 6), PLAYERS("player", 7), AMBIENT("ambient", 8), VOICE("voice", 9); + MOBS("hostile", 5), ANIMALS("neutral", 6), PLAYERS("player", 7), AMBIENT("ambient", 8); public static final SoundCategory[] _VALUES = values(); diff --git a/src/main/java/net/minecraft/client/audio/SoundEventAccessor.java b/src/game/java/net/minecraft/client/audio/SoundEventAccessor.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/SoundEventAccessor.java rename to src/game/java/net/minecraft/client/audio/SoundEventAccessor.java diff --git a/src/main/java/net/minecraft/client/audio/SoundEventAccessorComposite.java b/src/game/java/net/minecraft/client/audio/SoundEventAccessorComposite.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/SoundEventAccessorComposite.java rename to src/game/java/net/minecraft/client/audio/SoundEventAccessorComposite.java diff --git a/src/main/java/net/minecraft/client/audio/SoundHandler.java b/src/game/java/net/minecraft/client/audio/SoundHandler.java similarity index 98% rename from src/main/java/net/minecraft/client/audio/SoundHandler.java rename to src/game/java/net/minecraft/client/audio/SoundHandler.java index fae594d..07b1c8c 100644 --- a/src/main/java/net/minecraft/client/audio/SoundHandler.java +++ b/src/game/java/net/minecraft/client/audio/SoundHandler.java @@ -9,8 +9,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformAudio; - import com.google.common.collect.Lists; import net.hoosiertransfer.Config; @@ -278,10 +276,6 @@ public class SoundHandler implements IResourceManagerReloadListener, ITickable { this.stopSounds(); } - if (category == SoundCategory.VOICE) { - PlatformAudio.setMicVol(volume); - } - this.sndManager.setSoundCategoryVolume(category, volume); } diff --git a/src/main/java/net/minecraft/client/audio/SoundList.java b/src/game/java/net/minecraft/client/audio/SoundList.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/SoundList.java rename to src/game/java/net/minecraft/client/audio/SoundList.java diff --git a/src/main/java/net/minecraft/client/audio/SoundListSerializer.java b/src/game/java/net/minecraft/client/audio/SoundListSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/SoundListSerializer.java rename to src/game/java/net/minecraft/client/audio/SoundListSerializer.java diff --git a/src/main/java/net/minecraft/client/audio/SoundPoolEntry.java b/src/game/java/net/minecraft/client/audio/SoundPoolEntry.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/SoundPoolEntry.java rename to src/game/java/net/minecraft/client/audio/SoundPoolEntry.java diff --git a/src/main/java/net/minecraft/client/audio/SoundRegistry.java b/src/game/java/net/minecraft/client/audio/SoundRegistry.java similarity index 100% rename from src/main/java/net/minecraft/client/audio/SoundRegistry.java rename to src/game/java/net/minecraft/client/audio/SoundRegistry.java diff --git a/src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java b/src/game/java/net/minecraft/client/entity/AbstractClientPlayer.java similarity index 76% rename from src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java rename to src/game/java/net/minecraft/client/entity/AbstractClientPlayer.java index ba612ee..7f589f8 100644 --- a/src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java +++ b/src/game/java/net/minecraft/client/entity/AbstractClientPlayer.java @@ -1,153 +1,184 @@ -package net.minecraft.client.entity; - -import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; -import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; -import net.minecraft.client.Minecraft; -import net.minecraft.client.network.NetworkPlayerInfo; -import net.minecraft.client.resources.DefaultPlayerSkin; -import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.entity.ai.attributes.IAttributeInstance; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Items; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraft.world.WorldSettings; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public abstract class AbstractClientPlayer extends EntityPlayer { - private NetworkPlayerInfo playerInfo; - public float rotateElytraX; - public float rotateElytraY; - public float rotateElytraZ; - - public long eaglerHighPolyAnimationTick = System.currentTimeMillis(); - public float eaglerHighPolyAnimationFloat1 = 0.0f; - public float eaglerHighPolyAnimationFloat2 = 0.0f; - public float eaglerHighPolyAnimationFloat3 = 0.0f; - public float eaglerHighPolyAnimationFloat4 = 0.0f; - public float eaglerHighPolyAnimationFloat5 = 0.0f; - public float eaglerHighPolyAnimationFloat6 = 0.0f; - - public AbstractClientPlayer(World worldIn, GameProfile playerProfile) { - super(worldIn, playerProfile); - } - - /** - * + - * Returns true if the player is in spectator mode. - */ - public boolean isSpectator() { - NetworkPlayerInfo networkplayerinfo = Minecraft.getMinecraft().getNetHandler() - .getPlayerInfo(this.getGameProfile().getId()); - return networkplayerinfo != null && networkplayerinfo.getGameType() == WorldSettings.GameType.SPECTATOR; - } - - /** - * + - * Checks if this instance of AbstractClientPlayer has any - * associated player data. - */ - public boolean hasPlayerInfo() { - return this.getPlayerInfo() != null; - } - - protected NetworkPlayerInfo getPlayerInfo() { - if (this.playerInfo == null) { - this.playerInfo = Minecraft.getMinecraft().getNetHandler().getPlayerInfo(this.getUniqueID()); - } - - return this.playerInfo; - } - - /** - * + - * Returns true if the player has an associated skin. - */ - public boolean hasSkin() { - NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); - return networkplayerinfo != null && networkplayerinfo.hasLocationSkin(); - } - - /** - * + - * Returns true if the username has an associated skin. - */ - public ResourceLocation getLocationSkin() { - NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); - return networkplayerinfo == null ? DefaultPlayerSkin.getDefaultSkin(this.getUniqueID()) - : networkplayerinfo.getLocationSkin(); - } - - public ResourceLocation getLocationCape() { - NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); - return networkplayerinfo == null ? null : networkplayerinfo.getLocationCape(); - } - - public String getSkinType() { - NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); - return networkplayerinfo == null ? DefaultPlayerSkin.getSkinType(this.getUniqueID()) - : networkplayerinfo.getSkinType(); - } - - public SkinModel getEaglerSkinModel() { - NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); - return networkplayerinfo == null ? SkinModel.STEVE : networkplayerinfo.getEaglerSkinModel(); - } - - public float getFovModifier() { - float f = 1.0F; - if (this.capabilities.isFlying) { - f *= 1.1F; - } - - IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.movementSpeed); - f = (float) ((double) f - * ((iattributeinstance.getAttributeValue() / (double) this.capabilities.getWalkSpeed() + 1.0D) / 2.0D)); - if (this.capabilities.getWalkSpeed() == 0.0F || Float.isNaN(f) || Float.isInfinite(f)) { - f = 1.0F; - } - - if (this.isUsingItem() && this.getItemInUse().getItem() == Items.bow) { - int i = this.getItemInUseDuration(); - float f1 = (float) i / 20.0F; - if (f1 > 1.0F) { - f1 = 1.0F; - } else { - f1 = f1 * f1; - } - - f *= 1.0F - f1 * 0.15F; - } - - return f; - } +package net.minecraft.client.entity; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.minecraft.client.Minecraft; +import net.minecraft.client.network.NetworkPlayerInfo; +import net.minecraft.client.resources.DefaultPlayerSkin; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.event.ClickEvent; +import net.minecraft.init.Items; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraft.world.WorldSettings; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class AbstractClientPlayer extends EntityPlayer { + private NetworkPlayerInfo playerInfo; + public float rotateElytraX; + public float rotateElytraY; + public float rotateElytraZ; + + public long eaglerHighPolyAnimationTick = EagRuntime.steadyTimeMillis(); + public float eaglerHighPolyAnimationFloat1 = 0.0f; + public float eaglerHighPolyAnimationFloat2 = 0.0f; + public float eaglerHighPolyAnimationFloat3 = 0.0f; + public float eaglerHighPolyAnimationFloat4 = 0.0f; + public float eaglerHighPolyAnimationFloat5 = 0.0f; + public float eaglerHighPolyAnimationFloat6 = 0.0f; + public EaglercraftUUID clientBrandUUIDCache = null; + private String nameProfanityFilter = null; + + public AbstractClientPlayer(World worldIn, GameProfile playerProfile) { + super(worldIn, playerProfile); + } + + /** + * + + * Returns true if the player is in spectator mode. + */ + public boolean isSpectator() { + NetworkPlayerInfo networkplayerinfo = Minecraft.getMinecraft().getNetHandler() + .getPlayerInfo(this.getGameProfile().getId()); + return networkplayerinfo != null && networkplayerinfo.getGameType() == WorldSettings.GameType.SPECTATOR; + } + + /** + * + + * Checks if this instance of AbstractClientPlayer has any + * associated player data. + */ + public boolean hasPlayerInfo() { + return this.getPlayerInfo() != null; + } + + protected NetworkPlayerInfo getPlayerInfo() { + if (this.playerInfo == null) { + this.playerInfo = Minecraft.getMinecraft().getNetHandler().getPlayerInfo(this.getUniqueID()); + } + + return this.playerInfo; + } + + /** + * + + * Returns true if the player has an associated skin. + */ + public boolean hasSkin() { + NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); + return networkplayerinfo != null && networkplayerinfo.hasLocationSkin(); + } + + /** + * + + * Returns true if the username has an associated skin. + */ + public ResourceLocation getLocationSkin() { + NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); + return networkplayerinfo == null ? DefaultPlayerSkin.getDefaultSkin(this.getUniqueID()) + : networkplayerinfo.getLocationSkin(); + } + + public ResourceLocation getLocationCape() { + NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); + return networkplayerinfo == null ? null : networkplayerinfo.getLocationCape(); + } + + public String getSkinType() { + NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); + return networkplayerinfo == null ? DefaultPlayerSkin.getSkinType(this.getUniqueID()) + : networkplayerinfo.getSkinType(); + } + + public SkinModel getEaglerSkinModel() { + NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); + return networkplayerinfo == null ? SkinModel.STEVE : networkplayerinfo.getEaglerSkinModel(); + } + + public float getFovModifier() { + float f = 1.0F; + if (this.capabilities.isFlying) { + f *= 1.1F; + } + + IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.movementSpeed); + f = (float) ((double) f + * ((iattributeinstance.getAttributeValue() / (double) this.capabilities.getWalkSpeed() + 1.0D) / 2.0D)); + if (this.capabilities.getWalkSpeed() == 0.0F || Float.isNaN(f) || Float.isInfinite(f)) { + f = 1.0F; + } + + if (this.isUsingItem() && this.getItemInUse().getItem() == Items.bow) { + int i = this.getItemInUseDuration(); + float f1 = (float) i / 20.0F; + if (f1 > 1.0F) { + f1 = 1.0F; + } else { + f1 = f1 * f1; + } + + f *= 1.0F - f1 * 0.15F; + } + + return f; + } + + public String getNameProfanityFilter() { + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (nameProfanityFilter == null) { + nameProfanityFilter = ProfanityFilter.getInstance() + .profanityFilterString(this.getGameProfile().getName()); + } + return nameProfanityFilter; + } else { + return this.getGameProfile().getName(); + } + } + + public IChatComponent getDisplayNameProfanityFilter() { + ChatComponentText chatcomponenttext = new ChatComponentText( + ScorePlayerTeam.formatPlayerName(this.getTeam(), this.getNameProfanityFilter())); + chatcomponenttext.getChatStyle() + .setChatClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/msg " + this.getName() + " ")); + chatcomponenttext.getChatStyle().setChatHoverEvent(this.getHoverEvent()); + chatcomponenttext.getChatStyle().setInsertion(this.getName()); + return chatcomponenttext; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/entity/EntityOtherPlayerMP.java b/src/game/java/net/minecraft/client/entity/EntityOtherPlayerMP.java similarity index 100% rename from src/main/java/net/minecraft/client/entity/EntityOtherPlayerMP.java rename to src/game/java/net/minecraft/client/entity/EntityOtherPlayerMP.java diff --git a/src/main/java/net/minecraft/client/entity/EntityPlayerSP.java b/src/game/java/net/minecraft/client/entity/EntityPlayerSP.java similarity index 99% rename from src/main/java/net/minecraft/client/entity/EntityPlayerSP.java rename to src/game/java/net/minecraft/client/entity/EntityPlayerSP.java index 9041b89..4613b75 100644 --- a/src/main/java/net/minecraft/client/entity/EntityPlayerSP.java +++ b/src/game/java/net/minecraft/client/entity/EntityPlayerSP.java @@ -1,6 +1,7 @@ package net.minecraft.client.entity; import net.hoosiertransfer.EaglerItems; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; import net.minecraft.client.Minecraft; @@ -115,6 +116,7 @@ public class EntityPlayerSP extends AbstractClientPlayer { public EntityPlayerSP(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandler, StatFileWriter statWriter) { super(worldIn, netHandler.getGameProfile()); + this.clientBrandUUIDCache = EaglercraftVersion.clientBrandUUID; this.sendQueue = netHandler; this.mc = mcIn; this.dimension = 0; diff --git a/src/main/java/net/minecraft/client/gui/ChatLine.java b/src/game/java/net/minecraft/client/gui/ChatLine.java similarity index 79% rename from src/main/java/net/minecraft/client/gui/ChatLine.java rename to src/game/java/net/minecraft/client/gui/ChatLine.java index 9d23cc1..d5c5106 100644 --- a/src/main/java/net/minecraft/client/gui/ChatLine.java +++ b/src/game/java/net/minecraft/client/gui/ChatLine.java @@ -1,5 +1,7 @@ package net.minecraft.client.gui; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; +import net.minecraft.client.Minecraft; import net.minecraft.util.IChatComponent; /** @@ -34,6 +36,7 @@ import net.minecraft.util.IChatComponent; public class ChatLine { private final int updateCounterCreated; private final IChatComponent lineString; + private IChatComponent lineStringProfanityFilter; private final int chatLineID; public ChatLine(int parInt1, IChatComponent parIChatComponent, int parInt2) { @@ -43,7 +46,14 @@ public class ChatLine { } public IChatComponent getChatComponent() { - return this.lineString; + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (lineStringProfanityFilter == null) { + lineStringProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(lineString); + } + return lineStringProfanityFilter; + } else { + return lineString; + } } public int getUpdatedCounter() { diff --git a/src/main/java/net/minecraft/client/gui/FontRenderer.java b/src/game/java/net/minecraft/client/gui/FontRenderer.java similarity index 81% rename from src/main/java/net/minecraft/client/gui/FontRenderer.java rename to src/game/java/net/minecraft/client/gui/FontRenderer.java index cbada00..a264ced 100644 --- a/src/main/java/net/minecraft/client/gui/FontRenderer.java +++ b/src/game/java/net/minecraft/client/gui/FontRenderer.java @@ -8,6 +8,7 @@ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.IOUtils; +import net.lax1dude.eaglercraft.v1_8.minecraft.FontMappingHelper; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; @@ -93,6 +94,20 @@ public class FontRenderer implements IResourceManagerReloadListener { protected boolean underlineStyle; protected boolean strikethroughStyle; + protected static char[] codepointLookup = new char[] { 192, 193, 194, 200, 202, 203, 205, 211, 212, 213, 218, 223, + 227, 245, 287, 304, 305, 338, 339, 350, 351, 372, 373, 382, 519, 0, 0, 0, 0, 0, 0, 0, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, 199, 252, 233, 226, 228, 224, 229, 231, + 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 248, 163, + 216, 215, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 174, 172, 189, 188, 161, 171, 187, 9617, 9618, + 9619, 9474, 9508, 9569, 9570, 9558, 9557, 9571, 9553, 9559, 9565, 9564, 9563, 9488, 9492, 9524, 9516, 9500, + 9472, 9532, 9566, 9567, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 9575, 9576, 9572, 9573, 9561, 9560, 9554, + 9555, 9579, 9578, 9496, 9484, 9608, 9604, 9612, 9616, 9600, 945, 946, 915, 960, 931, 963, 956, 964, 934, + 920, 937, 948, 8734, 8709, 8712, 8745, 8801, 177, 8805, 8804, 8992, 8993, 247, 8776, 176, 8729, 183, 8730, + 8319, 178, 9632, 0 }; + public FontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) { this.locationFontTexture = location; @@ -200,8 +215,7 @@ public class FontRenderer implements IResourceManagerReloadListener { if (parChar1 == 32) { return 4.0F; } else { - int i = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000" - .indexOf(parChar1); + int i = FontMappingHelper.lookupChar(parChar1, false); return i != -1 && !this.unicodeFlag ? this.renderDefaultChar(i, parFlag) : this.renderUnicodeChar(parChar1, parFlag); } @@ -412,18 +426,15 @@ public class FontRenderer implements IResourceManagerReloadListener { ++i; } else { - int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000" - .indexOf(c0); + int j = FontMappingHelper.lookupChar(c0, false); if (this.randomStyle && j != -1) { int k = this.getCharWidth(c0); + char[] chars = FontRenderer.codepointLookup; char c1; while (true) { - j = this.fontRandom.nextInt( - "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000" - .length()); - c1 = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000" - .charAt(j); + j = this.fontRandom.nextInt(chars.length); + c1 = chars[j]; if (k == this.getCharWidth(c1)) { break; } @@ -603,8 +614,7 @@ public class FontRenderer implements IResourceManagerReloadListener { } else if (character == 32) { return 4; } else { - int i = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000" - .indexOf(character); + int i = FontMappingHelper.lookupChar(character, false); if (character > 0 && i != -1 && !this.unicodeFlag) { return this.charWidth[i]; } else if (this.glyphWidth[character] != 0) { diff --git a/src/main/java/net/minecraft/client/gui/Gui.java b/src/game/java/net/minecraft/client/gui/Gui.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/Gui.java rename to src/game/java/net/minecraft/client/gui/Gui.java diff --git a/src/main/java/net/minecraft/client/gui/GuiButton.java b/src/game/java/net/minecraft/client/gui/GuiButton.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiButton.java rename to src/game/java/net/minecraft/client/gui/GuiButton.java index bed7d05..71930a4 100644 --- a/src/main/java/net/minecraft/client/gui/GuiButton.java +++ b/src/game/java/net/minecraft/client/gui/GuiButton.java @@ -181,4 +181,9 @@ public class GuiButton extends Gui { public void setWidth(int width) { this.width = width; } + + public boolean isSliderTouchEvents() { + return false; + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiButtonLanguage.java b/src/game/java/net/minecraft/client/gui/GuiButtonLanguage.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiButtonLanguage.java rename to src/game/java/net/minecraft/client/gui/GuiButtonLanguage.java diff --git a/src/main/java/net/minecraft/client/gui/GuiChat.java b/src/game/java/net/minecraft/client/gui/GuiChat.java similarity index 85% rename from src/main/java/net/minecraft/client/gui/GuiChat.java rename to src/game/java/net/minecraft/client/gui/GuiChat.java index 5d9b820..08dafd8 100644 --- a/src/main/java/net/minecraft/client/gui/GuiChat.java +++ b/src/game/java/net/minecraft/client/gui/GuiChat.java @@ -9,8 +9,13 @@ import com.google.common.collect.Lists; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenVisualViewport; +import net.lax1dude.eaglercraft.v1_8.notifications.GuiButtonNotifBell; +import net.lax1dude.eaglercraft.v1_8.notifications.GuiScreenNotifications; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.client.resources.I18n; import net.minecraft.network.play.client.C14PacketTabComplete; @@ -49,7 +54,7 @@ import net.minecraft.util.MovingObjectPosition; * POSSIBILITY OF SUCH DAMAGE. * */ -public class GuiChat extends GuiScreen { +public class GuiChat extends GuiScreenVisualViewport { private static final Logger logger = LogManager.getLogger(); private String historyBuffer = ""; /** @@ -72,6 +77,7 @@ public class GuiChat extends GuiScreen { private String defaultInputFieldText = ""; private GuiButton exitButton; + private GuiButtonNotifBell notifBellButton; public GuiChat() { } @@ -90,6 +96,11 @@ public class GuiChat extends GuiScreen { Keyboard.enableRepeatEvents(true); if (!(this instanceof GuiSleepMP)) { this.buttonList.add(exitButton = new GuiButton(69, this.width - 100, 3, 97, 20, I18n.format("chat.exit"))); + if (!this.mc.isIntegratedServerRunning() && this.mc.thePlayer != null + && this.mc.thePlayer.sendQueue.getEaglerMessageProtocol().ver >= 4) { + this.buttonList.add(notifBellButton = new GuiButtonNotifBell(70, this.width - 122, 3)); + notifBellButton.setUnread(mc.thePlayer.sendQueue.getNotifManager().getUnread()); + } } this.sentHistoryCursor = this.mc.ingameGUI.getChatGUI().getSentMessages().size(); this.inputField = new GuiTextField(0, this.fontRendererObj, 4, this.height - 12, this.width - 4, 12); @@ -110,12 +121,11 @@ public class GuiChat extends GuiScreen { this.mc.ingameGUI.getChatGUI().resetScroll(); } - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { + public void updateScreen0() { this.inputField.updateCursorCounter(); + if (notifBellButton != null && mc.thePlayer != null) { + notifBellButton.setUnread(mc.thePlayer.sendQueue.getNotifManager().getUnread()); + } } /** @@ -185,26 +195,27 @@ public class GuiChat extends GuiScreen { } - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + protected void mouseClicked0(int parInt1, int parInt2, int parInt3) { if (parInt3 == 0) { - IChatComponent ichatcomponent = this.mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); + IChatComponent ichatcomponent = this.mc.ingameGUI.getChatGUI() + .getChatComponent(PointerInputAbstraction.getVCursorX(), PointerInputAbstraction.getVCursorY()); if (this.handleComponentClick(ichatcomponent)) { return; } + if (mc.notifRenderer.handleClicked(this, parInt1, parInt2)) { + return; + } } this.inputField.mouseClicked(parInt1, parInt2, parInt3); - super.mouseClicked(parInt1, parInt2, parInt3); + super.mouseClicked0(parInt1, parInt2, parInt3); } protected void actionPerformed(GuiButton par1GuiButton) { if (par1GuiButton.id == 69) { this.mc.displayGuiScreen(null); + } else if (par1GuiButton.id == 70) { + this.mc.displayGuiScreen(new GuiScreenNotifications(this)); } } @@ -301,19 +312,15 @@ public class GuiChat extends GuiScreen { } } - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { + public void drawScreen0(int i, int j, float f) { drawRect(2, this.height - 14, this.width - 2, this.height - 2, Integer.MIN_VALUE); this.inputField.drawTextBox(); if (this.inputField.isTypingPassword && this.mc.gameSettings.hidePassword) { this.mc.fontRendererObj.drawStringWithShadow("Password Hidden", 2, this.height - 25, 16770425); } GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - IChatComponent ichatcomponent = this.mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); + IChatComponent ichatcomponent = this.mc.ingameGUI.getChatGUI() + .getChatComponent(PointerInputAbstraction.getVCursorX(), PointerInputAbstraction.getVCursorY()); if (ichatcomponent != null && ichatcomponent.getChatStyle().getChatHoverEvent() != null) { this.handleComponentHover(ichatcomponent, i, j); } @@ -322,7 +329,7 @@ public class GuiChat extends GuiScreen { exitButton.yPosition = 3 + mc.guiAchievement.getHeight(); } - super.drawScreen(i, j, f); + super.drawScreen0(i, j, f); } public void onAutocompleteResponse(String[] parArrayOfString) { @@ -365,4 +372,13 @@ public class GuiChat extends GuiScreen { public boolean blockPTTKey() { return true; } + + public boolean showCopyPasteButtons() { + return true; + } + + public void fireInputEvent(EnumInputEvent event, String str) { + inputField.fireInputEvent(event, str); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiClientSettings.java b/src/game/java/net/minecraft/client/gui/GuiClientSettings.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiClientSettings.java rename to src/game/java/net/minecraft/client/gui/GuiClientSettings.java index 28277a2..158d903 100644 --- a/src/main/java/net/minecraft/client/gui/GuiClientSettings.java +++ b/src/game/java/net/minecraft/client/gui/GuiClientSettings.java @@ -5,6 +5,8 @@ import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.resources.I18n; +// TODO: make this work on touchscreens + public class GuiClientSettings extends GuiScreen { private final GuiScreen parentScreen; private String title; diff --git a/src/main/java/net/minecraft/client/gui/GuiCommandBlock.java b/src/game/java/net/minecraft/client/gui/GuiCommandBlock.java similarity index 94% rename from src/main/java/net/minecraft/client/gui/GuiCommandBlock.java rename to src/game/java/net/minecraft/client/gui/GuiCommandBlock.java index f71a021..993f575 100644 --- a/src/main/java/net/minecraft/client/gui/GuiCommandBlock.java +++ b/src/game/java/net/minecraft/client/gui/GuiCommandBlock.java @@ -4,6 +4,7 @@ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.minecraft.client.resources.I18n; import net.minecraft.command.server.CommandBlockLogic; import net.minecraft.network.PacketBuffer; @@ -207,6 +208,18 @@ public class GuiCommandBlock extends GuiScreen { } public boolean blockPTTKey() { - return commandTextField.isFocused(); + return commandTextField.isFocused() || previousOutputTextField.isFocused(); } + + @Override + public boolean showCopyPasteButtons() { + return commandTextField.isFocused() || previousOutputTextField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + commandTextField.fireInputEvent(event, param); + previousOutputTextField.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiConfirmOpenLink.java b/src/game/java/net/minecraft/client/gui/GuiConfirmOpenLink.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiConfirmOpenLink.java rename to src/game/java/net/minecraft/client/gui/GuiConfirmOpenLink.java diff --git a/src/main/java/net/minecraft/client/gui/GuiControls.java b/src/game/java/net/minecraft/client/gui/GuiControls.java similarity index 96% rename from src/main/java/net/minecraft/client/gui/GuiControls.java rename to src/game/java/net/minecraft/client/gui/GuiControls.java index 9e6b330..c4b0958 100644 --- a/src/main/java/net/minecraft/client/gui/GuiControls.java +++ b/src/game/java/net/minecraft/client/gui/GuiControls.java @@ -38,7 +38,8 @@ import net.minecraft.client.settings.KeyBinding; */ public class GuiControls extends GuiScreen { private static final GameSettings.Options[] optionsArr = new GameSettings.Options[] { - GameSettings.Options.INVERT_MOUSE, GameSettings.Options.SENSITIVITY, GameSettings.Options.TOUCHSCREEN }; + GameSettings.Options.INVERT_MOUSE, GameSettings.Options.SENSITIVITY, + GameSettings.Options.EAGLER_TOUCH_CONTROL_OPACITY }; private GuiScreen parentScreen; protected String screenTitle = "Controls"; private GameSettings options; @@ -95,8 +96,12 @@ public class GuiControls extends GuiScreen { this.keyBindingList.handleMouseInput(); } - /** - * + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.keyBindingList.handleTouchInput(); + } + + /**+ * Called by the controls from the buttonList when activated. * (Mouse pressed for buttons) */ diff --git a/src/main/java/net/minecraft/client/gui/GuiCreateFlatWorld.java b/src/game/java/net/minecraft/client/gui/GuiCreateFlatWorld.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiCreateFlatWorld.java rename to src/game/java/net/minecraft/client/gui/GuiCreateFlatWorld.java index 31317cc..ef1d2b8 100644 --- a/src/main/java/net/minecraft/client/gui/GuiCreateFlatWorld.java +++ b/src/game/java/net/minecraft/client/gui/GuiCreateFlatWorld.java @@ -108,8 +108,12 @@ public class GuiCreateFlatWorld extends GuiScreen { this.createFlatWorldListSlotGui.handleMouseInput(); } - /** - * + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.createFlatWorldListSlotGui.handleTouchInput(); + } + + /**+ * Called by the controls from the buttonList when activated. * (Mouse pressed for buttons) */ diff --git a/src/main/java/net/minecraft/client/gui/GuiCreateWorld.java b/src/game/java/net/minecraft/client/gui/GuiCreateWorld.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiCreateWorld.java rename to src/game/java/net/minecraft/client/gui/GuiCreateWorld.java index 6820820..84b89e1 100644 --- a/src/main/java/net/minecraft/client/gui/GuiCreateWorld.java +++ b/src/game/java/net/minecraft/client/gui/GuiCreateWorld.java @@ -3,6 +3,7 @@ package net.minecraft.client.gui; import java.util.Random; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.minecraft.client.resources.I18n; import net.minecraft.util.ChatAllowedCharacters; import net.minecraft.world.WorldSettings; @@ -480,4 +481,16 @@ public class GuiCreateWorld extends GuiScreen { } } + + @Override + public boolean showCopyPasteButtons() { + return field_146333_g.isFocused() || field_146335_h.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + field_146333_g.fireInputEvent(event, param); + field_146335_h.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiCustomizeSkin.java b/src/game/java/net/minecraft/client/gui/GuiCustomizeSkin.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiCustomizeSkin.java rename to src/game/java/net/minecraft/client/gui/GuiCustomizeSkin.java diff --git a/src/main/java/net/minecraft/client/gui/GuiCustomizeWorldScreen.java b/src/game/java/net/minecraft/client/gui/GuiCustomizeWorldScreen.java similarity index 99% rename from src/main/java/net/minecraft/client/gui/GuiCustomizeWorldScreen.java rename to src/game/java/net/minecraft/client/gui/GuiCustomizeWorldScreen.java index 0cc58f3..f39cfd9 100644 --- a/src/main/java/net/minecraft/client/gui/GuiCustomizeWorldScreen.java +++ b/src/game/java/net/minecraft/client/gui/GuiCustomizeWorldScreen.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.util.Random; import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.minecraft.client.renderer.Tessellator; @@ -141,6 +142,11 @@ public class GuiCustomizeWorldScreen extends GuiScreen this.field_175349_r.handleMouseInput(); } + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.field_175349_r.handleTouchInput(); + } + private void func_175325_f() { GuiPageButtonList.GuiListEntry[] aguipagebuttonlist$guilistentry = new GuiPageButtonList.GuiListEntry[] { new GuiPageButtonList.GuiSlideEntry(160, @@ -1211,4 +1217,15 @@ public class GuiCustomizeWorldScreen extends GuiScreen } } + + @Override + public boolean showCopyPasteButtons() { + return field_175349_r.isTextFieldFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + field_175349_r.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiDisconnected.java b/src/game/java/net/minecraft/client/gui/GuiDisconnected.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiDisconnected.java rename to src/game/java/net/minecraft/client/gui/GuiDisconnected.java diff --git a/src/main/java/net/minecraft/client/gui/GuiDownloadTerrain.java b/src/game/java/net/minecraft/client/gui/GuiDownloadTerrain.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiDownloadTerrain.java rename to src/game/java/net/minecraft/client/gui/GuiDownloadTerrain.java index cc73667..ce6fd38 100644 --- a/src/main/java/net/minecraft/client/gui/GuiDownloadTerrain.java +++ b/src/game/java/net/minecraft/client/gui/GuiDownloadTerrain.java @@ -97,4 +97,9 @@ public class GuiDownloadTerrain extends GuiScreen { public boolean shouldHangupIntegratedServer() { return false; } + + public boolean canCloseGui() { + return false; + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiEnchantment.java b/src/game/java/net/minecraft/client/gui/GuiEnchantment.java similarity index 99% rename from src/main/java/net/minecraft/client/gui/GuiEnchantment.java rename to src/game/java/net/minecraft/client/gui/GuiEnchantment.java index 33c6468..689fc94 100644 --- a/src/main/java/net/minecraft/client/gui/GuiEnchantment.java +++ b/src/game/java/net/minecraft/client/gui/GuiEnchantment.java @@ -154,7 +154,7 @@ public class GuiEnchantment extends GuiContainer { GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.pushMatrix(); GlStateManager.loadIdentity(); - ScaledResolution scaledresolution = new ScaledResolution(this.mc); + ScaledResolution scaledresolution = mc.scaledResolution; GlStateManager.viewport((scaledresolution.getScaledWidth() - 290 - 12) / 2 * scaledresolution.getScaleFactor(), (scaledresolution.getScaledHeight() - 220 + 10) / 2 * scaledresolution.getScaleFactor(), 290 * scaledresolution.getScaleFactor(), 220 * scaledresolution.getScaleFactor()); diff --git a/src/main/java/net/minecraft/client/gui/GuiErrorScreen.java b/src/game/java/net/minecraft/client/gui/GuiErrorScreen.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiErrorScreen.java rename to src/game/java/net/minecraft/client/gui/GuiErrorScreen.java diff --git a/src/main/java/net/minecraft/client/gui/GuiFlatPresets.java b/src/game/java/net/minecraft/client/gui/GuiFlatPresets.java similarity index 97% rename from src/main/java/net/minecraft/client/gui/GuiFlatPresets.java rename to src/game/java/net/minecraft/client/gui/GuiFlatPresets.java index 42094a8..471cc19 100644 --- a/src/main/java/net/minecraft/client/gui/GuiFlatPresets.java +++ b/src/game/java/net/minecraft/client/gui/GuiFlatPresets.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.List; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.minecraft.block.BlockTallGrass; @@ -97,8 +98,12 @@ public class GuiFlatPresets extends GuiScreen { this.field_146435_s.handleMouseInput(); } - /** - * + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.field_146435_s.handleTouchInput(); + } + + /**+ * Called when the screen is unloaded. Used to disable keyboard * repeat events */ @@ -334,4 +339,15 @@ public class GuiFlatPresets extends GuiScreen { 16777215); } } + + @Override + public boolean showCopyPasteButtons() { + return field_146433_u.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + field_146433_u.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiGameOver.java b/src/game/java/net/minecraft/client/gui/GuiGameOver.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiGameOver.java rename to src/game/java/net/minecraft/client/gui/GuiGameOver.java diff --git a/src/main/java/net/minecraft/client/gui/GuiHopper.java b/src/game/java/net/minecraft/client/gui/GuiHopper.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiHopper.java rename to src/game/java/net/minecraft/client/gui/GuiHopper.java diff --git a/src/main/java/net/minecraft/client/gui/GuiIngame.java b/src/game/java/net/minecraft/client/gui/GuiIngame.java similarity index 79% rename from src/main/java/net/minecraft/client/gui/GuiIngame.java rename to src/game/java/net/minecraft/client/gui/GuiIngame.java index 20498dc..9f7f8bd 100644 --- a/src/main/java/net/minecraft/client/gui/GuiIngame.java +++ b/src/game/java/net/minecraft/client/gui/GuiIngame.java @@ -1,1162 +1,1387 @@ -package net.minecraft.client.gui; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import java.util.ArrayList; -import java.util.Collection; -import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; - -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; -import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; -import net.minecraft.block.material.Material; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.entity.RenderItem; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.resources.I18n; -import net.minecraft.client.settings.GameSettings; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.entity.ai.attributes.IAttributeInstance; -import net.minecraft.entity.boss.BossStatus; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.inventory.IInventory; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.potion.Potion; -import net.minecraft.scoreboard.Score; -import net.minecraft.scoreboard.ScoreObjective; -import net.minecraft.scoreboard.ScorePlayerTeam; -import net.minecraft.scoreboard.Scoreboard; -import net.minecraft.util.BlockPos; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.FoodStats; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.MathHelper; -import net.minecraft.util.MovingObjectPosition; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.StringUtils; -import net.minecraft.world.border.WorldBorder; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiIngame extends Gui { - private static final ResourceLocation vignetteTexPath = new ResourceLocation("textures/misc/vignette.png"); - private static final ResourceLocation widgetsTexPath = new ResourceLocation("textures/gui/widgets.png"); - private static final ResourceLocation pumpkinBlurTexPath = new ResourceLocation("textures/misc/pumpkinblur.png"); - private final EaglercraftRandom rand = new EaglercraftRandom(); - private final Minecraft mc; - private final RenderItem itemRenderer; - private final GuiNewChat persistantChatGUI; - private int updateCounter; - /** - * + - * The string specifying which record music is playing - */ - private String recordPlaying = ""; - private int recordPlayingUpFor; - private boolean recordIsPlaying; - /** - * + - * Previous frame vignette brightness (slowly changes by 1% each - * frame) - */ - public float prevVignetteBrightness = 1.0F; - private int remainingHighlightTicks; - private ItemStack highlightingItemStack; - public final GuiOverlayDebug overlayDebug; - private final GuiSpectator spectatorGui; - private final GuiPlayerTabOverlay overlayPlayerList; - private int field_175195_w; - private String field_175201_x = ""; - private String field_175200_y = ""; - private int field_175199_z; - private int field_175192_A; - private int field_175193_B; - private int playerHealth = 0; - private int lastPlayerHealth = 0; - /** - * + - * The last recorded system time - */ - private long lastSystemTime = 0L; - /** - * + - * Used with updateCounter to make the heart bar flash - */ - private long healthUpdateCounter = 0L; - - public GuiIngame(Minecraft mcIn) { - this.mc = mcIn; - this.itemRenderer = mcIn.getRenderItem(); - this.overlayDebug = new GuiOverlayDebug(mcIn); - this.spectatorGui = new GuiSpectator(mcIn); - this.persistantChatGUI = new GuiNewChat(mcIn); - this.overlayPlayerList = new GuiPlayerTabOverlay(mcIn, this); - this.func_175177_a(); - } - - public void func_175177_a() { - this.field_175199_z = 10; - this.field_175192_A = 70; - this.field_175193_B = 20; - } - - public void renderGameOverlay(float partialTicks) { - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - int i = scaledresolution.getScaledWidth(); - int j = scaledresolution.getScaledHeight(); - this.mc.entityRenderer.setupOverlayRendering(); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - GlStateManager.enableDepth(); - GlStateManager.disableLighting(); - - ItemStack itemstack = this.mc.thePlayer.inventory.armorItemInSlot(3); - if (this.mc.gameSettings.thirdPersonView == 0 && itemstack != null - && itemstack.getItem() == Item.getItemFromBlock(Blocks.pumpkin)) { - this.renderPumpkinOverlay(scaledresolution); - } - - if (!this.mc.thePlayer.isPotionActive(Potion.confusion)) { - float f = this.mc.thePlayer.prevTimeInPortal - + (this.mc.thePlayer.timeInPortal - this.mc.thePlayer.prevTimeInPortal) * partialTicks; - if (f > 0.0F) { - this.func_180474_b(f, scaledresolution); - } - } - - if (this.mc.playerController.isSpectator()) { - this.spectatorGui.renderTooltip(scaledresolution, partialTicks); - } else { - this.renderTooltip(scaledresolution, partialTicks); - } - - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.mc.getTextureManager().bindTexture(icons); - GlStateManager.enableBlend(); - this.renderAttackIndicator(partialTicks, scaledresolution); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - this.mc.mcProfiler.startSection("bossHealth"); - this.renderBossHealth(); - this.mc.mcProfiler.endSection(); - if (this.mc.playerController.shouldDrawHUD()) { - this.renderPlayerStats(scaledresolution); - } - - GlStateManager.disableBlend(); - if (this.mc.thePlayer.getSleepTimer() > 0) { - this.mc.mcProfiler.startSection("sleep"); - GlStateManager.disableDepth(); - GlStateManager.disableAlpha(); - int j1 = this.mc.thePlayer.getSleepTimer(); - float f1 = (float) j1 / 100.0F; - if (f1 > 1.0F) { - f1 = 1.0F - (float) (j1 - 100) / 10.0F; - } - - int k = (int) (220.0F * f1) << 24 | 1052704; - drawRect(0, 0, i, j, k); - GlStateManager.enableAlpha(); - GlStateManager.enableDepth(); - this.mc.mcProfiler.endSection(); - } - - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - int k1 = i / 2 - 91; - if (this.mc.thePlayer.isRidingHorse()) { - this.renderHorseJumpBar(scaledresolution, k1); - } else if (this.mc.playerController.gameIsSurvivalOrAdventure()) { - this.renderExpBar(scaledresolution, k1); - } - - if (this.mc.gameSettings.heldItemTooltips && !this.mc.playerController.isSpectator()) { - this.func_181551_a(scaledresolution); - } else if (this.mc.thePlayer.isSpectator()) { - this.spectatorGui.func_175263_a(scaledresolution); - } - - if (this.mc.isDemo()) { - this.renderDemo(scaledresolution); - } - - this.overlayDebug.renderDebugInfo(scaledresolution); - - if (this.recordPlayingUpFor > 0) { - this.mc.mcProfiler.startSection("overlayMessage"); - float f2 = (float) this.recordPlayingUpFor - partialTicks; - int l1 = (int) (f2 * 255.0F / 20.0F); - if (l1 > 255) { - l1 = 255; - } - - if (l1 > 8) { - GlStateManager.pushMatrix(); - GlStateManager.translate((float) (i / 2), (float) (j - 68), 0.0F); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - int l = 16777215; - if (this.recordIsPlaying) { - l = MathHelper.func_181758_c(f2 / 50.0F, 0.7F, 0.6F) & 16777215; - } - - this.getFontRenderer().drawString(this.recordPlaying, - -this.getFontRenderer().getStringWidth(this.recordPlaying) / 2, -4, l + (l1 << 24 & -16777216)); - GlStateManager.disableBlend(); - GlStateManager.popMatrix(); - } - - this.mc.mcProfiler.endSection(); - } - - if (this.field_175195_w > 0) { - this.mc.mcProfiler.startSection("titleAndSubtitle"); - float f3 = (float) this.field_175195_w - partialTicks; - int i2 = 255; - if (this.field_175195_w > this.field_175193_B + this.field_175192_A) { - float f4 = (float) (this.field_175199_z + this.field_175192_A + this.field_175193_B) - f3; - i2 = (int) (f4 * 255.0F / (float) this.field_175199_z); - } - - if (this.field_175195_w <= this.field_175193_B) { - i2 = (int) (f3 * 255.0F / (float) this.field_175193_B); - } - - i2 = MathHelper.clamp_int(i2, 0, 255); - if (i2 > 8) { - GlStateManager.pushMatrix(); - GlStateManager.translate((float) (i / 2), (float) (j / 2), 0.0F); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - GlStateManager.pushMatrix(); - GlStateManager.scale(4.0F, 4.0F, 4.0F); - int j2 = i2 << 24 & -16777216; - this.getFontRenderer().drawString(this.field_175201_x, - (float) (-this.getFontRenderer().getStringWidth(this.field_175201_x) / 2), -10.0F, - 16777215 | j2, true); - GlStateManager.popMatrix(); - GlStateManager.pushMatrix(); - GlStateManager.scale(2.0F, 2.0F, 2.0F); - this.getFontRenderer().drawString(this.field_175200_y, - (float) (-this.getFontRenderer().getStringWidth(this.field_175200_y) / 2), 5.0F, 16777215 | j2, - true); - GlStateManager.popMatrix(); - GlStateManager.disableBlend(); - GlStateManager.popMatrix(); - } - - this.mc.mcProfiler.endSection(); - } - - Scoreboard scoreboard = this.mc.theWorld.getScoreboard(); - ScoreObjective scoreobjective = null; - ScorePlayerTeam scoreplayerteam = scoreboard.getPlayersTeam(this.mc.thePlayer.getName()); - if (scoreplayerteam != null) { - int i1 = scoreplayerteam.getChatFormat().getColorIndex(); - if (i1 >= 0) { - scoreobjective = scoreboard.getObjectiveInDisplaySlot(3 + i1); - } - } - - ScoreObjective scoreobjective1 = scoreobjective != null ? scoreobjective - : scoreboard.getObjectiveInDisplaySlot(1); - if (scoreobjective1 != null) { - this.renderScoreboard(scoreobjective1, scaledresolution); - } - - if (this.mc.currentScreen == null) { - this.mc.voiceOverlay.drawOverlay(); - } - - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - GlStateManager.disableAlpha(); - GlStateManager.pushMatrix(); - if (this.mc.gameSettings.hudWorld && (mc.currentScreen == null || !(mc.currentScreen instanceof GuiChat))) { - j -= 10; - } - GlStateManager.translate(0.0F, (float) (j - 48), 0.0F); - this.mc.mcProfiler.startSection("chat"); - this.persistantChatGUI.drawChat(this.updateCounter); - this.mc.mcProfiler.endSection(); - GlStateManager.popMatrix(); - scoreobjective1 = scoreboard.getObjectiveInDisplaySlot(0); - if (!this.mc.gameSettings.keyBindPlayerList.isKeyDown() || this.mc.isIntegratedServerRunning() - && this.mc.thePlayer.sendQueue.getPlayerInfoMap().size() <= 1 && scoreobjective1 == null) { - this.overlayPlayerList.updatePlayerList(false); - } else { - this.overlayPlayerList.updatePlayerList(true); - this.overlayPlayerList.renderPlayerlist(i, scoreboard, scoreobjective1); - } - - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.disableLighting(); - GlStateManager.enableAlpha(); - } - - private void renderAttackIndicator(float p_184045_1_, ScaledResolution p_184045_2_) - { - GameSettings gamesettings = this.mc.gameSettings; - - if (gamesettings.thirdPersonView == 0) - { - if (this.mc.playerController.isSpectator() && this.mc.pointedEntity == null) - { - MovingObjectPosition raytraceresult = this.mc.objectMouseOver; - - if (raytraceresult == null || raytraceresult.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) - { - return; - } - - BlockPos blockpos = raytraceresult.getBlockPos(); - - if (!this.mc.theWorld.getBlockState(blockpos).getBlock().hasTileEntity() || !(this.mc.theWorld.getTileEntity(blockpos) instanceof IInventory)) - { - return; - } - } - - int l = p_184045_2_.getScaledWidth(); - int i1 = p_184045_2_.getScaledHeight(); - - if (gamesettings.showDebugInfo && !gamesettings.hideGUI && !this.mc.thePlayer.hasReducedDebug() && !gamesettings.reducedDebugInfo) - { - // GlStateManager.pushMatrix(); - // GlStateManager.translate((float)(l / 2), (float)(i1 / 2), this.zLevel); - // Entity entity = this.mc.getRenderViewEntity(); - // GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * p_184045_1_, -1.0F, 0.0F, 0.0F); - // GlStateManager.rotate(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * p_184045_1_, 0.0F, 1.0F, 0.0F); - // GlStateManager.scale(-1.0F, -1.0F, -1.0F); - // OpenGlHelper.renderDirections(10); - // GlStateManager.popMatrix(); - } - else - { - GlStateManager.tryBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ZERO); - GlStateManager.enableAlpha(); - // this.drawTexturedModalRect(l / 2 - 7, i1 / 2 - 7, 0, 0, 16, 16); - - if (this.mc.gameSettings.attackIndicator == 1) - { - float f = this.mc.thePlayer.getCooledAttackStrength(0.0F); - if (f < 1.0F) - { - int i = i1 / 2 - 7 + 16; - int j = l / 2 - 7; - int k = (int)(f * 17.0F); - this.drawTexturedModalRect(j, i, 36, 94, 16, 4); - this.drawTexturedModalRect(j, i, 52, 94, k, 4); - } - } - } - } - } - - public void renderGameOverlayCrosshairs(int scaledResWidth, int scaledResHeight) { - if (this.showCrosshair()) { - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.mc.getTextureManager().bindTexture(icons); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, 1, 0); - GlStateManager.enableAlpha(); - this.drawTexturedModalRect(scaledResWidth / 2 - 7, scaledResHeight / 2 - 7, 0, 0, 16, 16); - } - } - - protected void renderTooltip(ScaledResolution sr, float partialTicks) { - if (this.mc.getRenderViewEntity() instanceof EntityPlayer) { - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.mc.getTextureManager().bindTexture(widgetsTexPath); - EntityPlayer entityplayer = (EntityPlayer) this.mc.getRenderViewEntity(); - int i = sr.getScaledWidth() / 2; - float f = this.zLevel; - this.zLevel = -90.0F; - this.drawTexturedModalRect(i - 91, sr.getScaledHeight() - 22, 0, 0, 182, 22); - this.drawTexturedModalRect(i - 91 - 1 + entityplayer.inventory.currentItem * 20, - sr.getScaledHeight() - 22 - 1, 0, 22, 24, 22); - this.zLevel = f; - GlStateManager.enableRescaleNormal(); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - RenderHelper.enableGUIStandardItemLighting(); - - for (int j = 0; j < 9; ++j) { - int k = sr.getScaledWidth() / 2 - 90 + j * 20 + 2; - int l = sr.getScaledHeight() - 16 - 3; - this.renderHotbarItem(j, k, l, partialTicks, entityplayer); - } - - if (this.mc.gameSettings.attackIndicator == 2) - { - float f1 = this.mc.thePlayer.getCooledAttackStrength(0.0F); - - if (f1 < 1.0F) - { - int i2 = sr.getScaledHeight() - 20; - int j2 = i + 91 + 6; - - this.mc.getTextureManager().bindTexture(icons); - int k1 = (int)(f1 * 19.0F); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.drawTexturedModalRect(j2, i2, 0, 94, 18, 18); - this.drawTexturedModalRect(j2, i2 + 18 - k1, 18, 112 - k1, 18, k1); - } - } - - RenderHelper.disableStandardItemLighting(); - GlStateManager.disableRescaleNormal(); - GlStateManager.disableBlend(); - } - } - - public void renderHorseJumpBar(ScaledResolution parScaledResolution, int parInt1) { - this.mc.mcProfiler.startSection("jumpBar"); - this.mc.getTextureManager().bindTexture(Gui.icons); - float f = this.mc.thePlayer.getHorseJumpPower(); - short short1 = 182; - int i = (int) (f * (float) (short1 + 1)); - int j = parScaledResolution.getScaledHeight() - 32 + 3; - this.drawTexturedModalRect(parInt1, j, 0, 84, short1, 5); - if (i > 0) { - this.drawTexturedModalRect(parInt1, j, 0, 89, i, 5); - } - - this.mc.mcProfiler.endSection(); - } - - public void renderExpBar(ScaledResolution parScaledResolution, int parInt1) { - this.mc.mcProfiler.startSection("expBar"); - this.mc.getTextureManager().bindTexture(Gui.icons); - int i = this.mc.thePlayer.xpBarCap(); - if (i > 0) { - short short1 = 182; - int j = (int) (this.mc.thePlayer.experience * (float) (short1 + 1)); - int k = parScaledResolution.getScaledHeight() - 32 + 3; - this.drawTexturedModalRect(parInt1, k, 0, 64, short1, 5); - if (j > 0) { - this.drawTexturedModalRect(parInt1, k, 0, 69, j, 5); - } - } - - this.mc.mcProfiler.endSection(); - if (this.mc.thePlayer.experienceLevel > 0) { - this.mc.mcProfiler.startSection("expLevel"); - int i1 = 8453920; - String s = "" + this.mc.thePlayer.experienceLevel; - int j1 = (parScaledResolution.getScaledWidth() - this.getFontRenderer().getStringWidth(s)) / 2; - int l = parScaledResolution.getScaledHeight() - 31 - 4; - boolean flag = false; - this.getFontRenderer().drawString(s, j1 + 1, l, 0); - this.getFontRenderer().drawString(s, j1 - 1, l, 0); - this.getFontRenderer().drawString(s, j1, l + 1, 0); - this.getFontRenderer().drawString(s, j1, l - 1, 0); - this.getFontRenderer().drawString(s, j1, l, i1); - this.mc.mcProfiler.endSection(); - } - - } - - public void func_181551_a(ScaledResolution parScaledResolution) { - this.mc.mcProfiler.startSection("selectedItemName"); - if (this.remainingHighlightTicks > 0 && this.highlightingItemStack != null) { - String s = this.highlightingItemStack.getDisplayName(); - if (this.highlightingItemStack.hasDisplayName()) { - s = EnumChatFormatting.ITALIC + s; - } - - int i = (parScaledResolution.getScaledWidth() - this.getFontRenderer().getStringWidth(s)) / 2; - int j = parScaledResolution.getScaledHeight() - 59; - if (!this.mc.playerController.shouldDrawHUD()) { - j += 14; - } - - int k = (int) ((float) this.remainingHighlightTicks * 256.0F / 10.0F); - if (k > 255) { - k = 255; - } - - if (k > 0) { - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - this.getFontRenderer().drawStringWithShadow(s, (float) i, (float) j, 16777215 + (k << 24)); - GlStateManager.disableBlend(); - GlStateManager.popMatrix(); - } - } - - this.mc.mcProfiler.endSection(); - } - - public void renderDemo(ScaledResolution parScaledResolution) { - this.mc.mcProfiler.startSection("demo"); - String s = ""; - if (this.mc.theWorld.getTotalWorldTime() >= 120500L) { - s = I18n.format("demo.demoExpired", new Object[0]); - } else { - s = I18n.format("demo.remainingTime", new Object[] { - StringUtils.ticksToElapsedTime((int) (120500L - this.mc.theWorld.getTotalWorldTime())) }); - } - - int i = this.getFontRenderer().getStringWidth(s); - this.getFontRenderer().drawStringWithShadow(s, (float) (parScaledResolution.getScaledWidth() - i - 10), 5.0F, - 16777215); - this.mc.mcProfiler.endSection(); - } - - protected boolean showCrosshair() { - if (this.mc.gameSettings.showDebugInfo && !this.mc.thePlayer.hasReducedDebug() - && !this.mc.gameSettings.reducedDebugInfo) { - return false; - } else if (this.mc.playerController.isSpectator()) { - if (this.mc.pointedEntity != null) { - return true; - } else { - if (this.mc.objectMouseOver != null - && this.mc.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { - BlockPos blockpos = this.mc.objectMouseOver.getBlockPos(); - if (this.mc.theWorld.getTileEntity(blockpos) instanceof IInventory) { - return true; - } - } - - return false; - } - } else { - return true; - } - } - - private void renderScoreboard(ScoreObjective parScoreObjective, ScaledResolution parScaledResolution) { - Scoreboard scoreboard = parScoreObjective.getScoreboard(); - Collection collection = scoreboard.getSortedScores(parScoreObjective); - ArrayList arraylist = Lists.newArrayList(Iterables.filter(collection, new Predicate() { - public boolean apply(Score score2) { - return score2.getPlayerName() != null && !score2.getPlayerName().startsWith("#"); - } - })); - ArrayList arraylist1; - if (arraylist.size() > 15) { - arraylist1 = Lists.newArrayList(Iterables.skip(arraylist, collection.size() - 15)); - } else { - arraylist1 = arraylist; - } - - int i = this.getFontRenderer().getStringWidth(parScoreObjective.getDisplayName()); - - for (int m = 0, n = arraylist1.size(); m < n; ++m) { - Score score = (Score) arraylist1.get(m); - ScorePlayerTeam scoreplayerteam = scoreboard.getPlayersTeam(score.getPlayerName()); - String s = ScorePlayerTeam.formatPlayerName(scoreplayerteam, score.getPlayerName()) + ": " - + EnumChatFormatting.RED + score.getScorePoints(); - i = Math.max(i, this.getFontRenderer().getStringWidth(s)); - } - - int i1 = arraylist1.size() * this.getFontRenderer().FONT_HEIGHT; - int j1 = parScaledResolution.getScaledHeight() / 2 + i1 / 3; - byte b0 = 3; - int k1 = parScaledResolution.getScaledWidth() - i - b0; - int j = 0; - - for (int m = 0, n = arraylist1.size(); m < n; ++m) { - Score score1 = (Score) arraylist1.get(m); - ++j; - ScorePlayerTeam scoreplayerteam1 = scoreboard.getPlayersTeam(score1.getPlayerName()); - String s1 = ScorePlayerTeam.formatPlayerName(scoreplayerteam1, score1.getPlayerName()); - String s2 = EnumChatFormatting.RED + "" + score1.getScorePoints(); - int k = j1 - j * this.getFontRenderer().FONT_HEIGHT; - int l = parScaledResolution.getScaledWidth() - b0 + 2; - drawRect(k1 - 2, k, l, k + this.getFontRenderer().FONT_HEIGHT, 1342177280); - this.getFontRenderer().drawString(s1, k1, k, 0xFFFFFFFF); - this.getFontRenderer().drawString(s2, l - this.getFontRenderer().getStringWidth(s2), k, 0xFFFFFFFF); - if (j == arraylist1.size()) { - String s3 = parScoreObjective.getDisplayName(); - drawRect(k1 - 2, k - this.getFontRenderer().FONT_HEIGHT - 1, l, k - 1, 1610612736); - drawRect(k1 - 2, k - 1, l, k, 1342177280); - this.getFontRenderer().drawString(s3, k1 + i / 2 - this.getFontRenderer().getStringWidth(s3) / 2, - k - this.getFontRenderer().FONT_HEIGHT, 0xFFFFFFFF); - } - } - - } - - private void renderPlayerStats(ScaledResolution parScaledResolution) { - if (this.mc.getRenderViewEntity() instanceof EntityPlayer) { - EntityPlayer entityplayer = (EntityPlayer) this.mc.getRenderViewEntity(); - int i = MathHelper.ceiling_float_int(entityplayer.getHealth()); - boolean flag = this.healthUpdateCounter > (long) this.updateCounter - && (this.healthUpdateCounter - (long) this.updateCounter) / 3L % 2L == 1L; - if (i < this.playerHealth && entityplayer.hurtResistantTime > 0) { - this.lastSystemTime = Minecraft.getSystemTime(); - this.healthUpdateCounter = (long) (this.updateCounter + 20); - } else if (i > this.playerHealth && entityplayer.hurtResistantTime > 0) { - this.lastSystemTime = Minecraft.getSystemTime(); - this.healthUpdateCounter = (long) (this.updateCounter + 10); - } - - if (Minecraft.getSystemTime() - this.lastSystemTime > 1000L) { - this.playerHealth = i; - this.lastPlayerHealth = i; - this.lastSystemTime = Minecraft.getSystemTime(); - } - - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - this.playerHealth = i; - int j = this.lastPlayerHealth; - this.rand.setSeed((long) (this.updateCounter * 312871)); - boolean flag1 = false; - FoodStats foodstats = entityplayer.getFoodStats(); - int k = foodstats.getFoodLevel(); - int l = foodstats.getPrevFoodLevel(); - IAttributeInstance iattributeinstance = entityplayer.getEntityAttribute(SharedMonsterAttributes.maxHealth); - int i1 = parScaledResolution.getScaledWidth() / 2 - 91; - int j1 = parScaledResolution.getScaledWidth() / 2 + 91; - int k1 = parScaledResolution.getScaledHeight() - 39; - float f = (float) iattributeinstance.getAttributeValue(); - float f1 = entityplayer.getAbsorptionAmount(); - int l1 = MathHelper.ceiling_float_int((f + f1) / 2.0F / 10.0F); - int i2 = Math.max(10 - (l1 - 2), 3); - int j2 = k1 - (l1 - 1) * i2 - 10; - float f2 = f1; - int k2 = entityplayer.getTotalArmorValue(); - int l2 = -1; - if (entityplayer.isPotionActive(Potion.regeneration)) { - l2 = this.updateCounter % MathHelper.ceiling_float_int(f + 5.0F); - } - - this.mc.mcProfiler.startSection("armor"); - - for (int i3 = 0; i3 < 10; ++i3) { - if (k2 > 0) { - int j3 = i1 + i3 * 8; - if (i3 * 2 + 1 < k2) { - this.drawTexturedModalRect(j3, j2, 34, 9, 9, 9); - } - - if (i3 * 2 + 1 == k2) { - this.drawTexturedModalRect(j3, j2, 25, 9, 9, 9); - } - - if (i3 * 2 + 1 > k2) { - this.drawTexturedModalRect(j3, j2, 16, 9, 9, 9); - } - } - } - - this.mc.mcProfiler.endStartSection("health"); - - for (int i5 = MathHelper.ceiling_float_int((f + f1) / 2.0F) - 1; i5 >= 0; --i5) { - int j5 = 16; - if (entityplayer.isPotionActive(Potion.poison)) { - j5 += 36; - } else if (entityplayer.isPotionActive(Potion.wither)) { - j5 += 72; - } - - byte b0 = 0; - if (flag) { - b0 = 1; - } - - int k3 = MathHelper.ceiling_float_int((float) (i5 + 1) / 10.0F) - 1; - int l3 = i1 + i5 % 10 * 8; - int i4 = k1 - k3 * i2; - if (i <= 4) { - i4 += this.rand.nextInt(2); - } - - if (i5 == l2) { - i4 -= 2; - } - - byte b1 = 0; - if (entityplayer.worldObj.getWorldInfo().isHardcoreModeEnabled()) { - b1 = 5; - } - - this.drawTexturedModalRect(l3, i4, 16 + b0 * 9, 9 * b1, 9, 9); - if (flag) { - if (i5 * 2 + 1 < j) { - this.drawTexturedModalRect(l3, i4, j5 + 54, 9 * b1, 9, 9); - } - - if (i5 * 2 + 1 == j) { - this.drawTexturedModalRect(l3, i4, j5 + 63, 9 * b1, 9, 9); - } - } - - if (f2 > 0.0F) { - if (f2 == f1 && f1 % 2.0F == 1.0F) { - this.drawTexturedModalRect(l3, i4, j5 + 153, 9 * b1, 9, 9); - } else { - this.drawTexturedModalRect(l3, i4, j5 + 144, 9 * b1, 9, 9); - } - - f2 -= 2.0F; - } else { - if (i5 * 2 + 1 < i) { - this.drawTexturedModalRect(l3, i4, j5 + 36, 9 * b1, 9, 9); - } - - if (i5 * 2 + 1 == i) { - this.drawTexturedModalRect(l3, i4, j5 + 45, 9 * b1, 9, 9); - } - } - } - - Entity entity = entityplayer.ridingEntity; - if (entity == null) { - this.mc.mcProfiler.endStartSection("food"); - - for (int k5 = 0; k5 < 10; ++k5) { - int i6 = k1; - int l6 = 16; - byte b4 = 0; - if (entityplayer.isPotionActive(Potion.hunger)) { - l6 += 36; - b4 = 13; - } - - if (entityplayer.getFoodStats().getSaturationLevel() <= 0.0F - && this.updateCounter % (k * 3 + 1) == 0) { - i6 = k1 + (this.rand.nextInt(3) - 1); - } - - if (flag1) { - b4 = 1; - } - - int l7 = j1 - k5 * 8 - 9; - this.drawTexturedModalRect(l7, i6, 16 + b4 * 9, 27, 9, 9); - if (flag1) { - if (k5 * 2 + 1 < l) { - this.drawTexturedModalRect(l7, i6, l6 + 54, 27, 9, 9); - } - - if (k5 * 2 + 1 == l) { - this.drawTexturedModalRect(l7, i6, l6 + 63, 27, 9, 9); - } - } - - if (k5 * 2 + 1 < k) { - this.drawTexturedModalRect(l7, i6, l6 + 36, 27, 9, 9); - } - - if (k5 * 2 + 1 == k) { - this.drawTexturedModalRect(l7, i6, l6 + 45, 27, 9, 9); - } - } - } else if (entity instanceof EntityLivingBase) { - this.mc.mcProfiler.endStartSection("mountHealth"); - EntityLivingBase entitylivingbase = (EntityLivingBase) entity; - int j6 = (int) Math.ceil((double) entitylivingbase.getHealth()); - float f3 = entitylivingbase.getMaxHealth(); - int j7 = (int) (f3 + 0.5F) / 2; - if (j7 > 30) { - j7 = 30; - } - - int i8 = k1; - - for (int j8 = 0; j7 > 0; j8 += 20) { - int j4 = Math.min(j7, 10); - j7 -= j4; - - for (int k4 = 0; k4 < j4; ++k4) { - byte b2 = 52; - byte b3 = 0; - if (flag1) { - b3 = 1; - } - - int l4 = j1 - k4 * 8 - 9; - this.drawTexturedModalRect(l4, i8, b2 + b3 * 9, 9, 9, 9); - if (k4 * 2 + 1 + j8 < j6) { - this.drawTexturedModalRect(l4, i8, b2 + 36, 9, 9, 9); - } - - if (k4 * 2 + 1 + j8 == j6) { - this.drawTexturedModalRect(l4, i8, b2 + 45, 9, 9, 9); - } - } - - i8 -= 10; - } - } - - this.mc.mcProfiler.endStartSection("air"); - if (entityplayer.isInsideOfMaterial(Material.water)) { - int l5 = this.mc.thePlayer.getAir(); - int k6 = MathHelper.ceiling_double_int((double) (l5 - 2) * 10.0D / 300.0D); - int i7 = MathHelper.ceiling_double_int((double) l5 * 10.0D / 300.0D) - k6; - - for (int k7 = 0; k7 < k6 + i7; ++k7) { - if (k7 < k6) { - this.drawTexturedModalRect(j1 - k7 * 8 - 9, j2, 16, 18, 9, 9); - } else { - this.drawTexturedModalRect(j1 - k7 * 8 - 9, j2, 25, 18, 9, 9); - } - } - } - - this.mc.mcProfiler.endSection(); - } - } - - /** - * + - * Renders dragon's (boss) health on the HUD - */ - private void renderBossHealth() { - if (BossStatus.bossName != null && BossStatus.statusBarTime > 0) { - --BossStatus.statusBarTime; - FontRenderer fontrenderer = this.mc.fontRendererObj; - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - int i = scaledresolution.getScaledWidth(); - short short1 = 182; - int j = i / 2 - short1 / 2; - int k = (int) (BossStatus.healthScale * (float) (short1 + 1)); - byte b0 = 12; - this.drawTexturedModalRect(j, b0, 0, 74, short1, 5); - this.drawTexturedModalRect(j, b0, 0, 74, short1, 5); - if (k > 0) { - this.drawTexturedModalRect(j, b0, 0, 79, k, 5); - } - - String s = BossStatus.bossName; - this.getFontRenderer().drawStringWithShadow(s, - (float) (i / 2 - this.getFontRenderer().getStringWidth(s) / 2), (float) (b0 - 10), 16777215); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.mc.getTextureManager().bindTexture(icons); - } - } - - private void renderPumpkinOverlay(ScaledResolution parScaledResolution) { - GlStateManager.disableDepth(); - GlStateManager.depthMask(false); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.disableAlpha(); - this.mc.getTextureManager().bindTexture(pumpkinBlurTexPath); - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); - worldrenderer.pos(0.0D, (double) parScaledResolution.getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex(); - worldrenderer.pos((double) parScaledResolution.getScaledWidth(), (double) parScaledResolution.getScaledHeight(), - -90.0D).tex(1.0D, 1.0D).endVertex(); - worldrenderer.pos((double) parScaledResolution.getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); - worldrenderer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); - tessellator.draw(); - GlStateManager.depthMask(true); - GlStateManager.enableDepth(); - GlStateManager.enableAlpha(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - } - - /** - * + - * Renders a Vignette arount the entire screen that changes with - * light level. - */ - public void renderVignette(float parFloat1, int scaledWidth, int scaledHeight) { - parFloat1 = 1.0F - parFloat1; - parFloat1 = MathHelper.clamp_float(parFloat1, 0.0F, 1.0F); - WorldBorder worldborder = this.mc.theWorld.getWorldBorder(); - float f = (float) worldborder.getClosestDistance(this.mc.thePlayer); - double d0 = Math.min(worldborder.getResizeSpeed() * (double) worldborder.getWarningTime() * 1000.0D, - Math.abs(worldborder.getTargetSize() - worldborder.getDiameter())); - double d1 = Math.max((double) worldborder.getWarningDistance(), d0); - if ((double) f < d1) { - f = 1.0F - (float) ((double) f / d1); - } else { - f = 0.0F; - } - - this.prevVignetteBrightness = (float) ((double) this.prevVignetteBrightness - + (double) (parFloat1 - this.prevVignetteBrightness) * 0.01D); - GlStateManager.disableDepth(); - GlStateManager.depthMask(false); - GlStateManager.tryBlendFuncSeparate(0, GL_ONE_MINUS_SRC_COLOR, 1, 0); - if (f > 0.0F) { - GlStateManager.color(0.0F, f, f, 1.0F); - } else { - GlStateManager.color(this.prevVignetteBrightness, this.prevVignetteBrightness, this.prevVignetteBrightness, - 1.0F); - } - - this.mc.getTextureManager().bindTexture(vignetteTexPath); - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); - worldrenderer.pos(0.0D, (double) scaledHeight, -90.0D).tex(0.0D, 1.0D).endVertex(); - worldrenderer.pos((double) scaledWidth, scaledHeight, -90.0D).tex(1.0D, 1.0D).endVertex(); - worldrenderer.pos((double) scaledWidth, 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); - worldrenderer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); - tessellator.draw(); - GlStateManager.depthMask(true); - GlStateManager.enableDepth(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - } - - private void func_180474_b(float parFloat1, ScaledResolution parScaledResolution) { - if (parFloat1 < 1.0F) { - parFloat1 = parFloat1 * parFloat1; - parFloat1 = parFloat1 * parFloat1; - parFloat1 = parFloat1 * 0.8F + 0.2F; - } - - GlStateManager.disableAlpha(); - GlStateManager.disableDepth(); - GlStateManager.depthMask(false); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); - GlStateManager.color(1.0F, 1.0F, 1.0F, parFloat1); - this.mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); - EaglerTextureAtlasSprite textureatlassprite = this.mc.getBlockRendererDispatcher().getBlockModelShapes() - .getTexture(Blocks.portal.getDefaultState()); - float f = textureatlassprite.getMinU(); - float f1 = textureatlassprite.getMinV(); - float f2 = textureatlassprite.getMaxU(); - float f3 = textureatlassprite.getMaxV(); - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); - worldrenderer.pos(0.0D, (double) parScaledResolution.getScaledHeight(), -90.0D).tex((double) f, (double) f3) - .endVertex(); - worldrenderer.pos((double) parScaledResolution.getScaledWidth(), (double) parScaledResolution.getScaledHeight(), - -90.0D).tex((double) f2, (double) f3).endVertex(); - worldrenderer.pos((double) parScaledResolution.getScaledWidth(), 0.0D, -90.0D).tex((double) f2, (double) f1) - .endVertex(); - worldrenderer.pos(0.0D, 0.0D, -90.0D).tex((double) f, (double) f1).endVertex(); - tessellator.draw(); - GlStateManager.depthMask(true); - GlStateManager.enableDepth(); - GlStateManager.enableAlpha(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - } - - private void renderHotbarItem(int index, int xPos, int yPos, float partialTicks, EntityPlayer parEntityPlayer) { - ItemStack itemstack = parEntityPlayer.inventory.mainInventory[index]; - if (itemstack != null) { - float f = (float) itemstack.animationsToGo - partialTicks; - if (f > 0.0F) { - GlStateManager.pushMatrix(); - float f1 = 1.0F + f / 5.0F; - GlStateManager.translate((float) (xPos + 8), (float) (yPos + 12), 0.0F); - GlStateManager.scale(1.0F / f1, (f1 + 1.0F) / 2.0F, 1.0F); - GlStateManager.translate((float) (-(xPos + 8)), (float) (-(yPos + 12)), 0.0F); - } - - this.itemRenderer.renderItemAndEffectIntoGUI(itemstack, xPos, yPos); - if (f > 0.0F) { - GlStateManager.popMatrix(); - } - - this.itemRenderer.renderItemOverlays(this.mc.fontRendererObj, itemstack, xPos, yPos); - } - } - - /** - * + - * The update tick for the ingame UI - */ - public void updateTick() { - if (this.recordPlayingUpFor > 0) { - --this.recordPlayingUpFor; - } - - if (this.field_175195_w > 0) { - --this.field_175195_w; - if (this.field_175195_w <= 0) { - this.field_175201_x = ""; - this.field_175200_y = ""; - } - } - - ++this.updateCounter; - if (this.mc.thePlayer != null) { - ItemStack itemstack = this.mc.thePlayer.inventory.getCurrentItem(); - if (itemstack == null) { - this.remainingHighlightTicks = 0; - } else if (this.highlightingItemStack != null && itemstack.getItem() == this.highlightingItemStack.getItem() - && ItemStack.areItemStackTagsEqual(itemstack, this.highlightingItemStack) - && (itemstack.isItemStackDamageable() - || itemstack.getMetadata() == this.highlightingItemStack.getMetadata())) { - if (this.remainingHighlightTicks > 0) { - --this.remainingHighlightTicks; - } - } else { - this.remainingHighlightTicks = 40; - } - - this.highlightingItemStack = itemstack; - } - - } - - public void setRecordPlayingMessage(String parString1) { - this.setRecordPlaying(I18n.format("record.nowPlaying", new Object[] { parString1 }), true); - } - - public void setRecordPlaying(String parString1, boolean parFlag) { - this.recordPlaying = parString1; - this.recordPlayingUpFor = 60; - this.recordIsPlaying = parFlag; - } - - public void displayTitle(String parString1, String parString2, int parInt1, int parInt2, int parInt3) { - if (parString1 == null && parString2 == null && parInt1 < 0 && parInt2 < 0 && parInt3 < 0) { - this.field_175201_x = ""; - this.field_175200_y = ""; - this.field_175195_w = 0; - } else if (parString1 != null) { - this.field_175201_x = parString1; - this.field_175195_w = this.field_175199_z + this.field_175192_A + this.field_175193_B; - } else if (parString2 != null) { - this.field_175200_y = parString2; - } else { - if (parInt1 >= 0) { - this.field_175199_z = parInt1; - } - - if (parInt2 >= 0) { - this.field_175192_A = parInt2; - } - - if (parInt3 >= 0) { - this.field_175193_B = parInt3; - } - - if (this.field_175195_w > 0) { - this.field_175195_w = this.field_175199_z + this.field_175192_A + this.field_175193_B; - } - - } - } - - public void drawEaglerPlayerOverlay(int x, int y, float partialTicks) { - Entity e = mc.getRenderViewEntity(); - if (e != null && e instanceof EntityLivingBase) { - EntityLivingBase ent = (EntityLivingBase) e; - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - GlStateManager.enableDepth(); - GlStateManager.enableColorMaterial(); - GlStateManager.pushMatrix(); - GlStateManager.translate((float) x - 10, (float) y + 36, 50.0F); - GlStateManager.scale(-17.0F, 17.0F, 17.0F); - GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F); - float f = ent.renderYawOffset; - float f1 = ent.rotationYaw; - float f2 = ent.prevRotationYaw; - float f3 = ent.prevRotationYawHead; - float f4 = ent.rotationYawHead; - float f5 = ent.prevRenderYawOffset; - GlStateManager.rotate(115.0F, 0.0F, 1.0F, 0.0F); - RenderHelper.enableStandardItemLighting(); - float f6 = ent.prevRenderYawOffset + (ent.renderYawOffset - ent.prevRenderYawOffset) * partialTicks; - ent.rotationYawHead -= f6; - ent.prevRotationYawHead -= f6; - ent.rotationYawHead *= 0.5f; - ent.prevRotationYawHead *= 0.5f; - ent.renderYawOffset = 0.0f; - ent.prevRenderYawOffset = 0.0f; - ent.prevRotationYaw = 0.0f; - ent.rotationYaw = 0.0f; - GlStateManager.rotate(-135.0F - - (ent.prevRotationYawHead + (ent.rotationYawHead - ent.prevRotationYawHead) * partialTicks) * 0.5F, - 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(ent.rotationPitch * 0.2f, 1.0F, 0.0F, 0.0F); - RenderManager rendermanager = Minecraft.getMinecraft().getRenderManager(); - rendermanager.setPlayerViewY(180.0F); - rendermanager.setRenderShadow(false); - rendermanager.renderEntityWithPosYaw(ent, 0.0D, 0.0D, 0.0D, 0.0F, partialTicks); - rendermanager.setRenderShadow(true); - ent.renderYawOffset = f; - ent.rotationYaw = f1; - ent.prevRotationYaw = f2; - ent.prevRotationYawHead = f3; - ent.rotationYawHead = f4; - ent.prevRenderYawOffset = f5; - GlStateManager.popMatrix(); - RenderHelper.disableStandardItemLighting(); - // GlStateManager.disableDepth(); - GlStateManager.disableRescaleNormal(); - GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); - GlStateManager.disableTexture2D(); - GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); - } - } - - public void setRecordPlaying(IChatComponent parIChatComponent, boolean parFlag) { - this.setRecordPlaying(parIChatComponent.getUnformattedText(), parFlag); - } - - /** - * + - * returns a pointer to the persistant Chat GUI, containing all - * previous chat messages and such - */ - public GuiNewChat getChatGUI() { - return this.persistantChatGUI; - } - - public int getUpdateCounter() { - return this.updateCounter; - } - - public FontRenderer getFontRenderer() { - return this.mc.fontRendererObj; - } - - public GuiSpectator getSpectatorGui() { - return this.spectatorGui; - } - - public GuiPlayerTabOverlay getTabList() { - return this.overlayPlayerList; - } - - public void func_181029_i() { - this.overlayPlayerList.func_181030_a(); - } +package net.minecraft.client.gui; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.util.ArrayList; +import java.util.Collection; + +import net.lax1dude.eaglercraft.v1_8.Display; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchControls; +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchOverlayRenderer; +import net.minecraft.block.material.Material; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.entity.boss.BossStatus; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.network.play.client.C16PacketClientStatus; +import net.minecraft.potion.Potion; +import net.minecraft.scoreboard.Score; +import net.minecraft.scoreboard.ScoreObjective; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.FoodStats; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StringUtils; +import net.minecraft.world.border.WorldBorder; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiIngame extends Gui { + private static final ResourceLocation vignetteTexPath = new ResourceLocation("textures/misc/vignette.png"); + private static final ResourceLocation widgetsTexPath = new ResourceLocation("textures/gui/widgets.png"); + private static final ResourceLocation pumpkinBlurTexPath = new ResourceLocation("textures/misc/pumpkinblur.png"); + private final EaglercraftRandom rand = new EaglercraftRandom(); + private final Minecraft mc; + private final RenderItem itemRenderer; + private final GuiNewChat persistantChatGUI; + private int updateCounter; + /** + * + + * The string specifying which record music is playing + */ + private String recordPlaying = ""; + private int recordPlayingUpFor; + private boolean recordIsPlaying; + /** + * + + * Previous frame vignette brightness (slowly changes by 1% each + * frame) + */ + public float prevVignetteBrightness = 1.0F; + private int remainingHighlightTicks; + private ItemStack highlightingItemStack; + public final GuiOverlayDebug overlayDebug; + private final GuiSpectator spectatorGui; + private final GuiPlayerTabOverlay overlayPlayerList; + private int field_175195_w; + private String field_175201_x = ""; + private String field_175200_y = ""; + private int field_175199_z; + private int field_175192_A; + private int field_175193_B; + private int playerHealth = 0; + private int lastPlayerHealth = 0; + /** + * + + * The last recorded system time + */ + private long lastSystemTime = 0L; + /** + * + + * Used with updateCounter to make the heart bar flash + */ + private long healthUpdateCounter = 0L; + + public GuiIngame(Minecraft mcIn) { + this.mc = mcIn; + this.itemRenderer = mcIn.getRenderItem(); + this.overlayDebug = new GuiOverlayDebug(mcIn); + this.spectatorGui = new GuiSpectator(mcIn); + this.persistantChatGUI = new GuiNewChat(mcIn); + this.overlayPlayerList = new GuiPlayerTabOverlay(mcIn, this); + this.func_175177_a(); + } + + public void func_175177_a() { + this.field_175199_z = 10; + this.field_175192_A = 70; + this.field_175193_B = 20; + } + + public void renderGameOverlay(float partialTicks) { + ScaledResolution scaledresolution = mc.scaledResolution; + int i = scaledresolution.getScaledWidth(); + int j = scaledresolution.getScaledHeight(); + this.mc.entityRenderer.setupOverlayRendering(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + GlStateManager.enableDepth(); + GlStateManager.disableLighting(); + + ItemStack itemstack = this.mc.thePlayer.inventory.armorItemInSlot(3); + if (this.mc.gameSettings.thirdPersonView == 0 && itemstack != null + && itemstack.getItem() == Item.getItemFromBlock(Blocks.pumpkin)) { + this.renderPumpkinOverlay(scaledresolution); + } + + if (!this.mc.thePlayer.isPotionActive(Potion.confusion)) { + float f = this.mc.thePlayer.prevTimeInPortal + + (this.mc.thePlayer.timeInPortal - this.mc.thePlayer.prevTimeInPortal) * partialTicks; + if (f > 0.0F) { + this.func_180474_b(f, scaledresolution); + } + } + + onBeginHotbarDraw(); + + if (this.mc.playerController.isSpectator()) { + this.spectatorGui.renderTooltip(scaledresolution, partialTicks); + } else { + this.renderTooltip(scaledresolution, partialTicks); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.mc.getTextureManager().bindTexture(icons); + GlStateManager.enableBlend(); + this.renderAttackIndicator(partialTicks, scaledresolution); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + this.renderBossHealth(); + if (this.mc.playerController.shouldDrawHUD()) { + this.renderPlayerStats(scaledresolution); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableBlend(); + int k1 = i / 2 - 91; + if (this.mc.thePlayer.isRidingHorse()) { + this.renderHorseJumpBar(scaledresolution, k1); + } else if (this.mc.playerController.gameIsSurvivalOrAdventure()) { + this.renderExpBar(scaledresolution, k1); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableBlend(); + if (this.mc.gameSettings.heldItemTooltips && !this.mc.playerController.isSpectator()) { + this.func_181551_a(scaledresolution); + } else if (this.mc.thePlayer.isSpectator()) { + this.spectatorGui.func_175263_a(scaledresolution); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableBlend(); + if (this.recordPlayingUpFor > 0) { + float f2 = (float) this.recordPlayingUpFor - partialTicks; + int l1 = (int) (f2 * 255.0F / 20.0F); + if (l1 > 255) { + l1 = 255; + } + + if (l1 > 8) { + GlStateManager.pushMatrix(); + GlStateManager.translate((float) (i / 2), (float) (j - 68), 0.0F); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + int l = 16777215; + if (this.recordIsPlaying) { + l = MathHelper.func_181758_c(f2 / 50.0F, 0.7F, 0.6F) & 16777215; + } + + this.getFontRenderer().drawString(this.recordPlaying, + -this.getFontRenderer().getStringWidth(this.recordPlaying) / 2, -4, l + (l1 << 24 & -16777216)); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + } + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + drawEaglerInteractButton(scaledresolution); + + onEndHotbarDraw(); + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + if (this.mc.thePlayer.getSleepTimer() > 0) { + GlStateManager.disableDepth(); + GlStateManager.disableAlpha(); + int j1 = this.mc.thePlayer.getSleepTimer(); + float f1 = (float) j1 / 100.0F; + if (f1 > 1.0F) { + f1 = 1.0F - (float) (j1 - 100) / 10.0F; + } + + int k = (int) (220.0F * f1) << 24 | 1052704; + drawRect(0, 0, i, j, k); + GlStateManager.enableAlpha(); + GlStateManager.enableDepth(); + } + + if (this.mc.isDemo()) { + this.renderDemo(scaledresolution); + } + + this.overlayDebug.renderDebugInfo(scaledresolution); + + if (this.field_175195_w > 0) { + float f3 = (float) this.field_175195_w - partialTicks; + int i2 = 255; + if (this.field_175195_w > this.field_175193_B + this.field_175192_A) { + float f4 = (float) (this.field_175199_z + this.field_175192_A + this.field_175193_B) - f3; + i2 = (int) (f4 * 255.0F / (float) this.field_175199_z); + } + + if (this.field_175195_w <= this.field_175193_B) { + i2 = (int) (f3 * 255.0F / (float) this.field_175193_B); + } + + i2 = MathHelper.clamp_int(i2, 0, 255); + if (i2 > 8) { + GlStateManager.pushMatrix(); + GlStateManager.translate((float) (i / 2), (float) (j / 2), 0.0F); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + GlStateManager.pushMatrix(); + GlStateManager.scale(4.0F, 4.0F, 4.0F); + int j2 = i2 << 24 & -16777216; + this.getFontRenderer().drawString(this.field_175201_x, + (float) (-this.getFontRenderer().getStringWidth(this.field_175201_x) / 2), -10.0F, + 16777215 | j2, true); + GlStateManager.popMatrix(); + GlStateManager.pushMatrix(); + GlStateManager.scale(2.0F, 2.0F, 2.0F); + this.getFontRenderer().drawString(this.field_175200_y, + (float) (-this.getFontRenderer().getStringWidth(this.field_175200_y) / 2), 5.0F, 16777215 | j2, + true); + GlStateManager.popMatrix(); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + } + } + + Scoreboard scoreboard = this.mc.theWorld.getScoreboard(); + ScoreObjective scoreobjective = null; + ScorePlayerTeam scoreplayerteam = scoreboard.getPlayersTeam(this.mc.thePlayer.getName()); + if (scoreplayerteam != null) { + int i1 = scoreplayerteam.getChatFormat().getColorIndex(); + if (i1 >= 0) { + scoreobjective = scoreboard.getObjectiveInDisplaySlot(3 + i1); + } + } + + ScoreObjective scoreobjective1 = scoreobjective != null ? scoreobjective + : scoreboard.getObjectiveInDisplaySlot(1); + if (scoreobjective1 != null) { + this.renderScoreboard(scoreobjective1, scaledresolution); + } + + if (this.mc.currentScreen == null) { + this.mc.voiceOverlay.drawOverlay(); + } + + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + GlStateManager.disableAlpha(); + GlStateManager.pushMatrix(); + if (this.mc.gameSettings.hudWorld && (mc.currentScreen == null || !(mc.currentScreen instanceof GuiChat))) { + j -= 10; + } + j -= (this.mc.displayHeight - (Display.getVisualViewportX() + Display.getVisualViewportH())) * j + / this.mc.displayHeight; + GlStateManager.translate(0.0F, (float) (j - 48), 0.0F); + this.persistantChatGUI.drawChat(this.updateCounter); + GlStateManager.popMatrix(); + scoreobjective1 = scoreboard.getObjectiveInDisplaySlot(0); + if (!this.mc.gameSettings.keyBindPlayerList.isKeyDown() || this.mc.isIntegratedServerRunning() + && this.mc.thePlayer.sendQueue.getPlayerInfoMap().size() <= 1 && scoreobjective1 == null) { + this.overlayPlayerList.updatePlayerList(false); + } else { + this.overlayPlayerList.updatePlayerList(true); + this.overlayPlayerList.renderPlayerlist(i, scoreboard, scoreobjective1); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + GlStateManager.disableLighting(); + GlStateManager.enableAlpha(); + } + + private void renderAttackIndicator(float p_184045_1_, ScaledResolution p_184045_2_) + { + GameSettings gamesettings = this.mc.gameSettings; + + if (gamesettings.thirdPersonView == 0) + { + if (this.mc.playerController.isSpectator() && this.mc.pointedEntity == null) + { + MovingObjectPosition raytraceresult = this.mc.objectMouseOver; + + if (raytraceresult == null || raytraceresult.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) + { + return; + } + + BlockPos blockpos = raytraceresult.getBlockPos(); + + if (!this.mc.theWorld.getBlockState(blockpos).getBlock().hasTileEntity() || !(this.mc.theWorld.getTileEntity(blockpos) instanceof IInventory)) + { + return; + } + } + + int l = p_184045_2_.getScaledWidth(); + int i1 = p_184045_2_.getScaledHeight(); + + if (gamesettings.showDebugInfo && !gamesettings.hideGUI && !this.mc.thePlayer.hasReducedDebug() && !gamesettings.reducedDebugInfo) + { + // GlStateManager.pushMatrix(); + // GlStateManager.translate((float)(l / 2), (float)(i1 / 2), this.zLevel); + // Entity entity = this.mc.getRenderViewEntity(); + // GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * p_184045_1_, -1.0F, 0.0F, 0.0F); + // GlStateManager.rotate(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * p_184045_1_, 0.0F, 1.0F, 0.0F); + // GlStateManager.scale(-1.0F, -1.0F, -1.0F); + // OpenGlHelper.renderDirections(10); + // GlStateManager.popMatrix(); + } + else + { + GlStateManager.tryBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ZERO); + GlStateManager.enableAlpha(); + // this.drawTexturedModalRect(l / 2 - 7, i1 / 2 - 7, 0, 0, 16, 16); + + if (this.mc.gameSettings.attackIndicator == 1) + { + float f = this.mc.thePlayer.getCooledAttackStrength(0.0F); + if (f < 1.0F) + { + int i = i1 / 2 - 7 + 16; + int j = l / 2 - 7; + int k = (int)(f * 17.0F); + this.drawTexturedModalRect(j, i, 36, 94, 16, 4); + this.drawTexturedModalRect(j, i, 52, 94, k, 4); + } + } + } + } + } + + public void renderGameOverlayCrosshairs(int scaledResWidth, int scaledResHeight) { + if (this.showCrosshair()) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.mc.getTextureManager().bindTexture(icons); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, 1, 0); + GlStateManager.enableAlpha(); + this.drawTexturedModalRect(scaledResWidth / 2 - 7, scaledResHeight / 2 - 7, 0, 0, 16, 16); + } + } + + protected void renderTooltip(ScaledResolution sr, float partialTicks) { + if (this.mc.getRenderViewEntity() instanceof EntityPlayer) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.mc.getTextureManager().bindTexture(widgetsTexPath); + EntityPlayer entityplayer = (EntityPlayer) this.mc.getRenderViewEntity(); + int i = sr.getScaledWidth() / 2; + float f = this.zLevel; + this.zLevel = -90.0F; + this.drawTexturedModalRect(i - 91, sr.getScaledHeight() - 22, 0, 0, 182, 22); + + if (PointerInputAbstraction.isTouchMode()) { + this.mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + this.drawTexturedModalRect(i + 89, sr.getScaledHeight() - 22, 234, 0, 22, 22); + int areaHAdd = 12; + hotbarAreaX = (i - 91) * mc.displayWidth / sr.getScaledWidth(); + hotbarAreaY = (sr.getScaledHeight() - 22 - areaHAdd) * mc.displayHeight / sr.getScaledHeight(); + hotbarAreaW = 203 * mc.displayWidth / sr.getScaledWidth(); + hotbarAreaH = (22 + areaHAdd) * mc.displayHeight / sr.getScaledHeight(); + } else { + hotbarAreaX = -1; + hotbarAreaY = -1; + hotbarAreaW = -1; + hotbarAreaH = -1; + } + + this.mc.getTextureManager().bindTexture(widgetsTexPath); + this.drawTexturedModalRect(i - 91 - 1 + entityplayer.inventory.currentItem * 20, + sr.getScaledHeight() - 22 - 1, 0, 22, 24, 22); + this.zLevel = f; + GlStateManager.enableRescaleNormal(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + RenderHelper.enableGUIStandardItemLighting(); + + for (int j = 0; j < 9; ++j) { + int k = sr.getScaledWidth() / 2 - 90 + j * 20 + 2; + int l = sr.getScaledHeight() - 16 - 3; + this.renderHotbarItem(j, k, l, partialTicks, entityplayer); + } + + if (this.mc.gameSettings.attackIndicator == 2) + { + float f1 = this.mc.thePlayer.getCooledAttackStrength(0.0F); + + if (f1 < 1.0F) + { + int i2 = sr.getScaledHeight() - 20; + int j2 = i + 91 + 6; + + this.mc.getTextureManager().bindTexture(icons); + int k1 = (int)(f1 * 19.0F); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.drawTexturedModalRect(j2, i2, 0, 94, 18, 18); + this.drawTexturedModalRect(j2, i2 + 18 - k1, 18, 112 - k1, 18, k1); + } + } + + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableRescaleNormal(); + + GlStateManager.disableBlend(); + } + } + + public void renderHorseJumpBar(ScaledResolution parScaledResolution, int parInt1) { + this.mc.getTextureManager().bindTexture(Gui.icons); + float f = this.mc.thePlayer.getHorseJumpPower(); + short short1 = 182; + int i = (int) (f * (float) (short1 + 1)); + int j = parScaledResolution.getScaledHeight() - 32 + 3; + this.drawTexturedModalRect(parInt1, j, 0, 84, short1, 5); + if (i > 0) { + this.drawTexturedModalRect(parInt1, j, 0, 89, i, 5); + } + } + + public void renderExpBar(ScaledResolution parScaledResolution, int parInt1) { + this.mc.getTextureManager().bindTexture(Gui.icons); + int i = this.mc.thePlayer.xpBarCap(); + if (i > 0) { + short short1 = 182; + int j = (int) (this.mc.thePlayer.experience * (float) (short1 + 1)); + int k = parScaledResolution.getScaledHeight() - 32 + 3; + this.drawTexturedModalRect(parInt1, k, 0, 64, short1, 5); + if (j > 0) { + this.drawTexturedModalRect(parInt1, k, 0, 69, j, 5); + } + } + + if (this.mc.thePlayer.experienceLevel > 0) { + int i1 = 8453920; + String s = "" + this.mc.thePlayer.experienceLevel; + int j1 = (parScaledResolution.getScaledWidth() - this.getFontRenderer().getStringWidth(s)) / 2; + int l = parScaledResolution.getScaledHeight() - 31 - 4; + boolean flag = false; + this.getFontRenderer().drawString(s, j1 + 1, l, 0); + this.getFontRenderer().drawString(s, j1 - 1, l, 0); + this.getFontRenderer().drawString(s, j1, l + 1, 0); + this.getFontRenderer().drawString(s, j1, l - 1, 0); + this.getFontRenderer().drawString(s, j1, l, i1); + } + + } + + public void func_181551_a(ScaledResolution parScaledResolution) { + if (this.remainingHighlightTicks > 0 && this.highlightingItemStack != null) { + String s = this.highlightingItemStack.getDisplayNameProfanityFilter(); + if (this.highlightingItemStack.hasDisplayName()) { + s = EnumChatFormatting.ITALIC + s; + } + + int i = (parScaledResolution.getScaledWidth() - this.getFontRenderer().getStringWidth(s)) / 2; + int j = parScaledResolution.getScaledHeight() - 59; + if (!this.mc.playerController.shouldDrawHUD()) { + j += 14; + } + + int k = (int) ((float) this.remainingHighlightTicks * 256.0F / 10.0F); + if (k > 255) { + k = 255; + } + + if (k > 0) { + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + this.getFontRenderer().drawStringWithShadow(s, (float) i, (float) j, 16777215 + (k << 24)); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + } + } + + } + + public void renderDemo(ScaledResolution parScaledResolution) { + String s = ""; + if (this.mc.theWorld.getTotalWorldTime() >= 120500L) { + s = I18n.format("demo.demoExpired", new Object[0]); + } else { + s = I18n.format("demo.remainingTime", new Object[] { + StringUtils.ticksToElapsedTime((int) (120500L - this.mc.theWorld.getTotalWorldTime())) }); + } + + int i = this.getFontRenderer().getStringWidth(s); + this.getFontRenderer().drawStringWithShadow(s, (float) (parScaledResolution.getScaledWidth() - i - 10), 5.0F, + 16777215); + } + + protected boolean showCrosshair() { + if (this.mc.gameSettings.showDebugInfo && !this.mc.thePlayer.hasReducedDebug() + && !this.mc.gameSettings.reducedDebugInfo) { + return false; + } else if (this.mc.playerController.isSpectator()) { + if (this.mc.pointedEntity != null) { + return true; + } else { + if (this.mc.objectMouseOver != null + && this.mc.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + BlockPos blockpos = this.mc.objectMouseOver.getBlockPos(); + if (this.mc.theWorld.getTileEntity(blockpos) instanceof IInventory) { + return true; + } + } + + return false; + } + } else { + return true; + } + } + + private void renderScoreboard(ScoreObjective parScoreObjective, ScaledResolution parScaledResolution) { + Scoreboard scoreboard = parScoreObjective.getScoreboard(); + Collection collection = scoreboard.getSortedScores(parScoreObjective); + ArrayList arraylist = Lists.newArrayList(Iterables.filter(collection, new Predicate() { + public boolean apply(Score score2) { + return score2.getPlayerName() != null && !score2.getPlayerName().startsWith("#"); + } + })); + ArrayList arraylist1; + if (arraylist.size() > 15) { + arraylist1 = Lists.newArrayList(Iterables.skip(arraylist, collection.size() - 15)); + } else { + arraylist1 = arraylist; + } + + int i = this.getFontRenderer().getStringWidth(parScoreObjective.getDisplayName()); + + for (int m = 0, n = arraylist1.size(); m < n; ++m) { + Score score = (Score) arraylist1.get(m); + ScorePlayerTeam scoreplayerteam = scoreboard.getPlayersTeam(score.getPlayerName()); + String s = ScorePlayerTeam.formatPlayerName(scoreplayerteam, score.getPlayerName()) + ": " + + EnumChatFormatting.RED + score.getScorePoints(); + i = Math.max(i, this.getFontRenderer().getStringWidth(s)); + } + + int i1 = arraylist1.size() * this.getFontRenderer().FONT_HEIGHT; + int j1 = parScaledResolution.getScaledHeight() / 2 + i1 / 3; + byte b0 = 3; + int k1 = parScaledResolution.getScaledWidth() - i - b0; + int j = 0; + + for (int m = 0, n = arraylist1.size(); m < n; ++m) { + Score score1 = (Score) arraylist1.get(m); + ++j; + ScorePlayerTeam scoreplayerteam1 = scoreboard.getPlayersTeam(score1.getPlayerName()); + String s1 = ScorePlayerTeam.formatPlayerName(scoreplayerteam1, score1.getPlayerName()); + String s2 = EnumChatFormatting.RED + "" + score1.getScorePoints(); + int k = j1 - j * this.getFontRenderer().FONT_HEIGHT; + int l = parScaledResolution.getScaledWidth() - b0 + 2; + drawRect(k1 - 2, k, l, k + this.getFontRenderer().FONT_HEIGHT, 1342177280); + this.getFontRenderer().drawString(s1, k1, k, 0xFFFFFFFF); + this.getFontRenderer().drawString(s2, l - this.getFontRenderer().getStringWidth(s2), k, 0xFFFFFFFF); + if (j == arraylist1.size()) { + String s3 = parScoreObjective.getDisplayName(); + drawRect(k1 - 2, k - this.getFontRenderer().FONT_HEIGHT - 1, l, k - 1, 1610612736); + drawRect(k1 - 2, k - 1, l, k, 1342177280); + this.getFontRenderer().drawString(s3, k1 + i / 2 - this.getFontRenderer().getStringWidth(s3) / 2, + k - this.getFontRenderer().FONT_HEIGHT, 0xFFFFFFFF); + } + } + + } + + private void renderPlayerStats(ScaledResolution parScaledResolution) { + if (this.mc.getRenderViewEntity() instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) this.mc.getRenderViewEntity(); + int i = MathHelper.ceiling_float_int(entityplayer.getHealth()); + boolean flag = this.healthUpdateCounter > (long) this.updateCounter + && (this.healthUpdateCounter - (long) this.updateCounter) / 3L % 2L == 1L; + if (i < this.playerHealth && entityplayer.hurtResistantTime > 0) { + this.lastSystemTime = Minecraft.getSystemTime(); + this.healthUpdateCounter = (long) (this.updateCounter + 20); + } else if (i > this.playerHealth && entityplayer.hurtResistantTime > 0) { + this.lastSystemTime = Minecraft.getSystemTime(); + this.healthUpdateCounter = (long) (this.updateCounter + 10); + } + + if (Minecraft.getSystemTime() - this.lastSystemTime > 1000L) { + this.playerHealth = i; + this.lastPlayerHealth = i; + this.lastSystemTime = Minecraft.getSystemTime(); + } + + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + this.playerHealth = i; + int j = this.lastPlayerHealth; + this.rand.setSeed((long) (this.updateCounter * 312871)); + boolean flag1 = false; + FoodStats foodstats = entityplayer.getFoodStats(); + int k = foodstats.getFoodLevel(); + int l = foodstats.getPrevFoodLevel(); + IAttributeInstance iattributeinstance = entityplayer.getEntityAttribute(SharedMonsterAttributes.maxHealth); + int i1 = parScaledResolution.getScaledWidth() / 2 - 91; + int j1 = parScaledResolution.getScaledWidth() / 2 + 91; + int k1 = parScaledResolution.getScaledHeight() - 39; + float f = (float) iattributeinstance.getAttributeValue(); + float f1 = entityplayer.getAbsorptionAmount(); + int l1 = MathHelper.ceiling_float_int((f + f1) / 2.0F / 10.0F); + int i2 = Math.max(10 - (l1 - 2), 3); + int j2 = k1 - (l1 - 1) * i2 - 10; + float f2 = f1; + int k2 = entityplayer.getTotalArmorValue(); + int l2 = -1; + if (entityplayer.isPotionActive(Potion.regeneration)) { + l2 = this.updateCounter % MathHelper.ceiling_float_int(f + 5.0F); + } + + for (int i3 = 0; i3 < 10; ++i3) { + if (k2 > 0) { + int j3 = i1 + i3 * 8; + if (i3 * 2 + 1 < k2) { + this.drawTexturedModalRect(j3, j2, 34, 9, 9, 9); + } + + if (i3 * 2 + 1 == k2) { + this.drawTexturedModalRect(j3, j2, 25, 9, 9, 9); + } + + if (i3 * 2 + 1 > k2) { + this.drawTexturedModalRect(j3, j2, 16, 9, 9, 9); + } + } + } + + for (int i5 = MathHelper.ceiling_float_int((f + f1) / 2.0F) - 1; i5 >= 0; --i5) { + int j5 = 16; + if (entityplayer.isPotionActive(Potion.poison)) { + j5 += 36; + } else if (entityplayer.isPotionActive(Potion.wither)) { + j5 += 72; + } + + byte b0 = 0; + if (flag) { + b0 = 1; + } + + int k3 = MathHelper.ceiling_float_int((float) (i5 + 1) / 10.0F) - 1; + int l3 = i1 + i5 % 10 * 8; + int i4 = k1 - k3 * i2; + if (i <= 4) { + i4 += this.rand.nextInt(2); + } + + if (i5 == l2) { + i4 -= 2; + } + + byte b1 = 0; + if (entityplayer.worldObj.getWorldInfo().isHardcoreModeEnabled()) { + b1 = 5; + } + + this.drawTexturedModalRect(l3, i4, 16 + b0 * 9, 9 * b1, 9, 9); + if (flag) { + if (i5 * 2 + 1 < j) { + this.drawTexturedModalRect(l3, i4, j5 + 54, 9 * b1, 9, 9); + } + + if (i5 * 2 + 1 == j) { + this.drawTexturedModalRect(l3, i4, j5 + 63, 9 * b1, 9, 9); + } + } + + if (f2 > 0.0F) { + if (f2 == f1 && f1 % 2.0F == 1.0F) { + this.drawTexturedModalRect(l3, i4, j5 + 153, 9 * b1, 9, 9); + } else { + this.drawTexturedModalRect(l3, i4, j5 + 144, 9 * b1, 9, 9); + } + + f2 -= 2.0F; + } else { + if (i5 * 2 + 1 < i) { + this.drawTexturedModalRect(l3, i4, j5 + 36, 9 * b1, 9, 9); + } + + if (i5 * 2 + 1 == i) { + this.drawTexturedModalRect(l3, i4, j5 + 45, 9 * b1, 9, 9); + } + } + } + + Entity entity = entityplayer.ridingEntity; + if (entity == null) { + for (int k5 = 0; k5 < 10; ++k5) { + int i6 = k1; + int l6 = 16; + byte b4 = 0; + if (entityplayer.isPotionActive(Potion.hunger)) { + l6 += 36; + b4 = 13; + } + + if (entityplayer.getFoodStats().getSaturationLevel() <= 0.0F + && this.updateCounter % (k * 3 + 1) == 0) { + i6 = k1 + (this.rand.nextInt(3) - 1); + } + + if (flag1) { + b4 = 1; + } + + int l7 = j1 - k5 * 8 - 9; + this.drawTexturedModalRect(l7, i6, 16 + b4 * 9, 27, 9, 9); + if (flag1) { + if (k5 * 2 + 1 < l) { + this.drawTexturedModalRect(l7, i6, l6 + 54, 27, 9, 9); + } + + if (k5 * 2 + 1 == l) { + this.drawTexturedModalRect(l7, i6, l6 + 63, 27, 9, 9); + } + } + + if (k5 * 2 + 1 < k) { + this.drawTexturedModalRect(l7, i6, l6 + 36, 27, 9, 9); + } + + if (k5 * 2 + 1 == k) { + this.drawTexturedModalRect(l7, i6, l6 + 45, 27, 9, 9); + } + } + } else if (entity instanceof EntityLivingBase) { + EntityLivingBase entitylivingbase = (EntityLivingBase) entity; + int j6 = (int) Math.ceil((double) entitylivingbase.getHealth()); + float f3 = entitylivingbase.getMaxHealth(); + int j7 = (int) (f3 + 0.5F) / 2; + if (j7 > 30) { + j7 = 30; + } + + int i8 = k1; + + for (int j8 = 0; j7 > 0; j8 += 20) { + int j4 = Math.min(j7, 10); + j7 -= j4; + + for (int k4 = 0; k4 < j4; ++k4) { + byte b2 = 52; + byte b3 = 0; + if (flag1) { + b3 = 1; + } + + int l4 = j1 - k4 * 8 - 9; + this.drawTexturedModalRect(l4, i8, b2 + b3 * 9, 9, 9, 9); + if (k4 * 2 + 1 + j8 < j6) { + this.drawTexturedModalRect(l4, i8, b2 + 36, 9, 9, 9); + } + + if (k4 * 2 + 1 + j8 == j6) { + this.drawTexturedModalRect(l4, i8, b2 + 45, 9, 9, 9); + } + } + + i8 -= 10; + } + } + + if (entityplayer.isInsideOfMaterial(Material.water)) { + int l5 = this.mc.thePlayer.getAir(); + int k6 = MathHelper.ceiling_double_int((double) (l5 - 2) * 10.0D / 300.0D); + int i7 = MathHelper.ceiling_double_int((double) l5 * 10.0D / 300.0D) - k6; + + for (int k7 = 0; k7 < k6 + i7; ++k7) { + if (k7 < k6) { + this.drawTexturedModalRect(j1 - k7 * 8 - 9, j2, 16, 18, 9, 9); + } else { + this.drawTexturedModalRect(j1 - k7 * 8 - 9, j2, 25, 18, 9, 9); + } + } + } + + } + } + + /** + * + + * Renders dragon's (boss) health on the HUD + */ + private void renderBossHealth() { + if (BossStatus.bossName != null && BossStatus.statusBarTime > 0) { + --BossStatus.statusBarTime; + int i = mc.scaledResolution.getScaledWidth(); + short short1 = 182; + int j = i / 2 - short1 / 2; + int k = (int) (BossStatus.healthScale * (float) (short1 + 1)); + byte b0 = 12; + this.drawTexturedModalRect(j, b0, 0, 74, short1, 5); + this.drawTexturedModalRect(j, b0, 0, 74, short1, 5); + if (k > 0) { + this.drawTexturedModalRect(j, b0, 0, 79, k, 5); + } + + String s = BossStatus.bossName; + this.getFontRenderer().drawStringWithShadow(s, + (float) (i / 2 - this.getFontRenderer().getStringWidth(s) / 2), (float) (b0 - 10), 16777215); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.mc.getTextureManager().bindTexture(icons); + } + } + + private void renderPumpkinOverlay(ScaledResolution parScaledResolution) { + GlStateManager.disableDepth(); + GlStateManager.depthMask(false); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableAlpha(); + this.mc.getTextureManager().bindTexture(pumpkinBlurTexPath); + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(0.0D, (double) parScaledResolution.getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex(); + worldrenderer.pos((double) parScaledResolution.getScaledWidth(), (double) parScaledResolution.getScaledHeight(), + -90.0D).tex(1.0D, 1.0D).endVertex(); + worldrenderer.pos((double) parScaledResolution.getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); + worldrenderer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); + tessellator.draw(); + GlStateManager.depthMask(true); + GlStateManager.enableDepth(); + GlStateManager.enableAlpha(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + } + + /** + * + + * Renders a Vignette arount the entire screen that changes with + * light level. + */ + public void renderVignette(float parFloat1, int scaledWidth, int scaledHeight) { + parFloat1 = 1.0F - parFloat1; + parFloat1 = MathHelper.clamp_float(parFloat1, 0.0F, 1.0F); + WorldBorder worldborder = this.mc.theWorld.getWorldBorder(); + float f = (float) worldborder.getClosestDistance(this.mc.thePlayer); + double d0 = Math.min(worldborder.getResizeSpeed() * (double) worldborder.getWarningTime() * 1000.0D, + Math.abs(worldborder.getTargetSize() - worldborder.getDiameter())); + double d1 = Math.max((double) worldborder.getWarningDistance(), d0); + if ((double) f < d1) { + f = 1.0F - (float) ((double) f / d1); + } else { + f = 0.0F; + } + + this.prevVignetteBrightness = (float) ((double) this.prevVignetteBrightness + + (double) (parFloat1 - this.prevVignetteBrightness) * 0.01D); + GlStateManager.disableDepth(); + GlStateManager.depthMask(false); + GlStateManager.tryBlendFuncSeparate(0, GL_ONE_MINUS_SRC_COLOR, 1, 0); + if (f > 0.0F) { + GlStateManager.color(0.0F, f, f, 1.0F); + } else { + GlStateManager.color(this.prevVignetteBrightness, this.prevVignetteBrightness, this.prevVignetteBrightness, + 1.0F); + } + + this.mc.getTextureManager().bindTexture(vignetteTexPath); + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(0.0D, (double) scaledHeight, -90.0D).tex(0.0D, 1.0D).endVertex(); + worldrenderer.pos((double) scaledWidth, scaledHeight, -90.0D).tex(1.0D, 1.0D).endVertex(); + worldrenderer.pos((double) scaledWidth, 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); + worldrenderer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); + tessellator.draw(); + GlStateManager.depthMask(true); + GlStateManager.enableDepth(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + } + + private void func_180474_b(float parFloat1, ScaledResolution parScaledResolution) { + if (parFloat1 < 1.0F) { + parFloat1 = parFloat1 * parFloat1; + parFloat1 = parFloat1 * parFloat1; + parFloat1 = parFloat1 * 0.8F + 0.2F; + } + + GlStateManager.disableAlpha(); + GlStateManager.disableDepth(); + GlStateManager.depthMask(false); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + GlStateManager.color(1.0F, 1.0F, 1.0F, parFloat1); + this.mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); + EaglerTextureAtlasSprite textureatlassprite = this.mc.getBlockRendererDispatcher().getBlockModelShapes() + .getTexture(Blocks.portal.getDefaultState()); + float f = textureatlassprite.getMinU(); + float f1 = textureatlassprite.getMinV(); + float f2 = textureatlassprite.getMaxU(); + float f3 = textureatlassprite.getMaxV(); + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(0.0D, (double) parScaledResolution.getScaledHeight(), -90.0D).tex((double) f, (double) f3) + .endVertex(); + worldrenderer.pos((double) parScaledResolution.getScaledWidth(), (double) parScaledResolution.getScaledHeight(), + -90.0D).tex((double) f2, (double) f3).endVertex(); + worldrenderer.pos((double) parScaledResolution.getScaledWidth(), 0.0D, -90.0D).tex((double) f2, (double) f1) + .endVertex(); + worldrenderer.pos(0.0D, 0.0D, -90.0D).tex((double) f, (double) f1).endVertex(); + tessellator.draw(); + GlStateManager.depthMask(true); + GlStateManager.enableDepth(); + GlStateManager.enableAlpha(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + } + + private void renderHotbarItem(int index, int xPos, int yPos, float partialTicks, EntityPlayer parEntityPlayer) { + ItemStack itemstack = parEntityPlayer.inventory.mainInventory[index]; + if (itemstack != null) { + float f = (float) itemstack.animationsToGo - partialTicks; + if (f > 0.0F) { + GlStateManager.pushMatrix(); + float f1 = 1.0F + f / 5.0F; + GlStateManager.translate((float) (xPos + 8), (float) (yPos + 12), 0.0F); + GlStateManager.scale(1.0F / f1, (f1 + 1.0F) / 2.0F, 1.0F); + GlStateManager.translate((float) (-(xPos + 8)), (float) (-(yPos + 12)), 0.0F); + } + + this.itemRenderer.renderItemAndEffectIntoGUI(itemstack, xPos, yPos); + if (f > 0.0F) { + GlStateManager.popMatrix(); + } + + this.itemRenderer.renderItemOverlays(this.mc.fontRendererObj, itemstack, xPos, yPos); + } + } + + /** + * + + * The update tick for the ingame UI + */ + public void updateTick() { + if (this.recordPlayingUpFor > 0) { + --this.recordPlayingUpFor; + } + + if (this.field_175195_w > 0) { + --this.field_175195_w; + if (this.field_175195_w <= 0) { + this.field_175201_x = ""; + this.field_175200_y = ""; + } + } + + ++this.updateCounter; + if (this.mc.thePlayer != null) { + ItemStack itemstack = this.mc.thePlayer.inventory.getCurrentItem(); + if (itemstack == null) { + this.remainingHighlightTicks = 0; + } else if (this.highlightingItemStack != null && itemstack.getItem() == this.highlightingItemStack.getItem() + && ItemStack.areItemStackTagsEqual(itemstack, this.highlightingItemStack) + && (itemstack.isItemStackDamageable() + || itemstack.getMetadata() == this.highlightingItemStack.getMetadata())) { + if (this.remainingHighlightTicks > 0) { + --this.remainingHighlightTicks; + } + } else { + this.remainingHighlightTicks = 40; + } + + this.highlightingItemStack = itemstack; + } + + } + + public void setRecordPlayingMessage(String parString1) { + this.setRecordPlaying(I18n.format("record.nowPlaying", new Object[] { parString1 }), true); + } + + public void setRecordPlaying(String parString1, boolean parFlag) { + this.recordPlaying = parString1; + this.recordPlayingUpFor = 60; + this.recordIsPlaying = parFlag; + } + + public void displayTitle(String parString1, String parString2, int parInt1, int parInt2, int parInt3) { + if (parString1 == null && parString2 == null && parInt1 < 0 && parInt2 < 0 && parInt3 < 0) { + this.field_175201_x = ""; + this.field_175200_y = ""; + this.field_175195_w = 0; + } else if (parString1 != null) { + this.field_175201_x = parString1; + this.field_175195_w = this.field_175199_z + this.field_175192_A + this.field_175193_B; + } else if (parString2 != null) { + this.field_175200_y = parString2; + } else { + if (parInt1 >= 0) { + this.field_175199_z = parInt1; + } + + if (parInt2 >= 0) { + this.field_175192_A = parInt2; + } + + if (parInt3 >= 0) { + this.field_175193_B = parInt3; + } + + if (this.field_175195_w > 0) { + this.field_175195_w = this.field_175199_z + this.field_175192_A + this.field_175193_B; + } + + } + } + + public void drawEaglerPlayerOverlay(int x, int y, float partialTicks) { + Entity e = mc.getRenderViewEntity(); + if (e != null && e instanceof EntityLivingBase) { + EntityLivingBase ent = (EntityLivingBase) e; + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + GlStateManager.enableDepth(); + GlStateManager.enableColorMaterial(); + GlStateManager.pushMatrix(); + GlStateManager.translate((float) x - 10, (float) y + 36, 50.0F); + GlStateManager.scale(-17.0F, 17.0F, 17.0F); + GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F); + float f = ent.renderYawOffset; + float f1 = ent.rotationYaw; + float f2 = ent.prevRotationYaw; + float f3 = ent.prevRotationYawHead; + float f4 = ent.rotationYawHead; + float f5 = ent.prevRenderYawOffset; + GlStateManager.rotate(115.0F, 0.0F, 1.0F, 0.0F); + RenderHelper.enableStandardItemLighting(); + float f6 = ent.prevRenderYawOffset + (ent.renderYawOffset - ent.prevRenderYawOffset) * partialTicks; + ent.rotationYawHead -= f6; + ent.prevRotationYawHead -= f6; + ent.rotationYawHead *= 0.5f; + ent.prevRotationYawHead *= 0.5f; + ent.renderYawOffset = 0.0f; + ent.prevRenderYawOffset = 0.0f; + ent.prevRotationYaw = 0.0f; + ent.rotationYaw = 0.0f; + GlStateManager.rotate(-135.0F + - (ent.prevRotationYawHead + (ent.rotationYawHead - ent.prevRotationYawHead) * partialTicks) * 0.5F, + 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(ent.rotationPitch * 0.2f, 1.0F, 0.0F, 0.0F); + RenderManager rendermanager = Minecraft.getMinecraft().getRenderManager(); + rendermanager.setPlayerViewY(180.0F); + rendermanager.setRenderShadow(false); + rendermanager.renderEntityWithPosYaw(ent, 0.0D, 0.0D, 0.0D, 0.0F, partialTicks); + rendermanager.setRenderShadow(true); + ent.renderYawOffset = f; + ent.rotationYaw = f1; + ent.prevRotationYaw = f2; + ent.prevRotationYawHead = f3; + ent.rotationYawHead = f4; + ent.prevRenderYawOffset = f5; + GlStateManager.popMatrix(); + RenderHelper.disableStandardItemLighting(); + // GlStateManager.disableDepth(); + GlStateManager.disableRescaleNormal(); + GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GlStateManager.disableTexture2D(); + GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); + } + } + + public void setRecordPlaying(IChatComponent parIChatComponent, boolean parFlag) { + this.setRecordPlaying(parIChatComponent.getUnformattedText(), parFlag); + } + + /** + * + + * returns a pointer to the persistant Chat GUI, containing all + * previous chat messages and such + */ + public GuiNewChat getChatGUI() { + return this.persistantChatGUI; + } + + public int getUpdateCounter() { + return this.updateCounter; + } + + public FontRenderer getFontRenderer() { + return this.mc.fontRendererObj; + } + + public GuiSpectator getSpectatorGui() { + return this.spectatorGui; + } + + public GuiPlayerTabOverlay getTabList() { + return this.overlayPlayerList; + } + + public void func_181029_i() { + this.overlayPlayerList.func_181030_a(); + } + + private int hotbarAreaX = -1; + private int hotbarAreaY = -1; + private int hotbarAreaW = -1; + private int hotbarAreaH = -1; + private int currentHotbarSlotTouch = -1; + private long hotbarSlotTouchStart = -1l; + private boolean hotbarSlotTouchAlreadySelected = false; + private int interactButtonX = -1; + private int interactButtonY = -1; + private int interactButtonW = -1; + private int interactButtonH = -1; + private int touchVPosX = -1; + private int touchVPosY = -1; + private int touchEventUID = -1; + + private void drawEaglerInteractButton(ScaledResolution parScaledResolution) { + if (PointerInputAbstraction.isTouchMode() && mc.objectMouseOver != null + && mc.objectMouseOver.typeOfHit == MovingObjectType.ENTITY) { + int scale = parScaledResolution.getScaleFactor(); + interactButtonW = 118 * scale; + interactButtonH = 20 * scale; + int xx = (parScaledResolution.getScaledWidth() - 118) / 2; + int yy = parScaledResolution.getScaledHeight() - 70; + interactButtonX = xx * scale; + interactButtonY = yy * scale; + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + boolean hover = touchVPosX >= interactButtonX && touchVPosY >= interactButtonY + && touchVPosX < interactButtonX + interactButtonW && touchVPosY < interactButtonY + interactButtonH; + float f = MathHelper.clamp_float(mc.gameSettings.touchControlOpacity, 0.0f, 1.0f); + if (f > 0.0f) { + GlStateManager.color(1.0f, 1.0f, 1.0f, f); + drawTexturedModalRect(xx, yy, 0, hover ? 216 : 236, 118, 20); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + drawCenteredString(mc.fontRendererObj, I18n.format("touch.interact.entity"), + parScaledResolution.getScaledWidth() / 2, yy + 6, + (hover ? 16777120 : 14737632) | ((int) (f * 255.0f) << 24)); + } + } else { + interactButtonX = -1; + interactButtonY = -1; + interactButtonW = -1; + interactButtonH = -1; + } + } + + private int applyTouchHotbarTransformX(int posX, boolean scaled) { + if (scaled) { + return (posX + mc.scaledResolution.getScaledWidth() / 4) * 2 / 3; + } else { + return (posX + mc.displayWidth / 4) * 2 / 3; + } + } + + private int applyTouchHotbarTransformY(int posY, boolean scaled) { + if (scaled) { + return (posY + mc.scaledResolution.getScaledHeight() / 2) * 2 / 3; + } else { + return (posY + mc.displayHeight / 2) * 2 / 3; + } + } + + private void onBeginHotbarDraw() { + if (PointerInputAbstraction.isTouchMode()) { + GlStateManager.pushMatrix(); + ScaledResolution res = mc.scaledResolution; + GlStateManager.translate(res.getScaledWidth() / -4, res.getScaledHeight() / -2, field_175199_z); + GlStateManager.scale(1.5f, 1.5f, 1.5f); + } + } + + private void onEndHotbarDraw() { + if (PointerInputAbstraction.isTouchMode()) { + GlStateManager.popMatrix(); + } + } + + private int getHotbarSlotTouched(int pointX) { + int xx = pointX - hotbarAreaX - 2; + xx /= 20 * mc.scaledResolution.getScaleFactor(); + if (xx < 0) + xx = 0; + if (xx > 9) + xx = 9; + return xx; + } + + public boolean handleTouchBeginEagler(int uid, int pointX, int pointY) { + if (mc.thePlayer == null) { + return false; + } + if (touchEventUID == -1) { + pointX = applyTouchHotbarTransformX(pointX, false); + pointY = applyTouchHotbarTransformY(pointY, false); + if (pointX >= hotbarAreaX && pointY >= hotbarAreaY && pointX < hotbarAreaX + hotbarAreaW + && pointY < hotbarAreaY + hotbarAreaH) { + touchEventUID = uid; + currentHotbarSlotTouch = getHotbarSlotTouched(pointX); + hotbarSlotTouchStart = EagRuntime.steadyTimeMillis(); + if (currentHotbarSlotTouch >= 0 && currentHotbarSlotTouch < 9) { + if (mc.thePlayer.isSpectator()) { + hotbarSlotTouchAlreadySelected = false; + mc.ingameGUI.getSpectatorGui().func_175260_a(currentHotbarSlotTouch); + } else { + hotbarSlotTouchAlreadySelected = (mc.thePlayer.inventory.currentItem == currentHotbarSlotTouch); + mc.thePlayer.inventory.currentItem = currentHotbarSlotTouch; + } + } else if (currentHotbarSlotTouch == 9) { + hotbarSlotTouchAlreadySelected = false; + currentHotbarSlotTouch = 69; + if (mc.playerController.isRidingHorse()) { + mc.thePlayer.sendHorseInventory(); + } else { + mc.getNetHandler().addToSendQueue( + new C16PacketClientStatus(C16PacketClientStatus.EnumState.OPEN_INVENTORY_ACHIEVEMENT)); + mc.displayGuiScreen(new GuiInventory(mc.thePlayer)); + } + } + return true; + } + if (pointX >= interactButtonX && pointY >= interactButtonY && pointX < interactButtonX + interactButtonW + && pointY < interactButtonY + interactButtonH) { + touchEventUID = uid; + mc.rightClickMouse(); + return true; + } + } + return false; + } + + public boolean handleTouchEndEagler(int uid, int pointX, int pointY) { + if (uid == touchEventUID) { + if (hotbarSlotTouchStart != -1l && currentHotbarSlotTouch != 69) { + if (EagRuntime.steadyTimeMillis() - hotbarSlotTouchStart < 350l) { + if (hotbarSlotTouchAlreadySelected) { + if (mc.thePlayer != null) { + mc.thePlayer.dropOneItem(false); + } + } + } + } + touchVPosX = -1; + touchVPosY = -1; + touchEventUID = -1; + currentHotbarSlotTouch = -1; + hotbarSlotTouchStart = -1l; + hotbarSlotTouchAlreadySelected = false; + return true; + } + return false; + } + + public void updateTouchEagler(boolean screenTouched) { + if (screenTouched) { + int pointCount = Touch.touchPointCount(); + for (int i = 0; i < pointCount; ++i) { + int uid = Touch.touchPointUID(i); + if (TouchControls.touchControls.containsKey(uid)) { + continue; + } + if (touchEventUID == -1 || touchEventUID == uid) { + touchVPosX = applyTouchHotbarTransformX(Touch.touchPointX(i), false); + touchVPosY = applyTouchHotbarTransformY(mc.displayHeight - Touch.touchPointY(i) - 1, false); + long millis = EagRuntime.steadyTimeMillis(); + if (touchEventUID != -1 && hotbarSlotTouchStart != -1l) { + if (currentHotbarSlotTouch != 69) { + int slot = getHotbarSlotTouched(touchVPosX); + if (slot != currentHotbarSlotTouch) { + hotbarSlotTouchAlreadySelected = false; + currentHotbarSlotTouch = slot; + hotbarSlotTouchStart = millis; + if (slot >= 0 && slot < 9) { + if (mc.thePlayer.isSpectator()) { + mc.ingameGUI.getSpectatorGui().func_175260_a(slot); + } else { + mc.thePlayer.inventory.currentItem = slot; + } + } + } else { + if (millis - hotbarSlotTouchStart > 1200l) { + if (!mc.thePlayer.isSpectator()) { + hotbarSlotTouchStart = millis; + this.mc.thePlayer.dropOneItem(true); + } + } + } + } + } + return; + } + } + } + if (touchEventUID != -1) { + handleTouchEndEagler(touchEventUID, touchVPosX, touchVPosY); + } + touchVPosX = -1; + touchVPosY = -1; + touchEventUID = -1; + currentHotbarSlotTouch = -1; + hotbarSlotTouchStart = -1l; + hotbarSlotTouchAlreadySelected = false; + } + + public boolean isTouchOverlapEagler(int uid, int tx, int ty) { + if (touchEventUID == uid) { + return true; + } + ty = mc.displayHeight - ty - 1; + tx = applyTouchHotbarTransformX(tx, false); + ty = applyTouchHotbarTransformY(ty, false); + return (tx >= hotbarAreaX && ty >= hotbarAreaY && tx < hotbarAreaX + hotbarAreaW + && ty < hotbarAreaY + hotbarAreaH) + || (tx >= interactButtonX && ty >= interactButtonY && tx < interactButtonX + interactButtonW + && ty < interactButtonY + interactButtonH); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiIngameMenu.java b/src/game/java/net/minecraft/client/gui/GuiIngameMenu.java similarity index 52% rename from src/main/java/net/minecraft/client/gui/GuiIngameMenu.java rename to src/game/java/net/minecraft/client/gui/GuiIngameMenu.java index 5d42e81..6f221ea 100644 --- a/src/main/java/net/minecraft/client/gui/GuiIngameMenu.java +++ b/src/game/java/net/minecraft/client/gui/GuiIngameMenu.java @@ -1,313 +1,416 @@ -package net.minecraft.client.gui; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.Mouse; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANInfo; -import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANNotSupported; -import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiShareToLan; -import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; -import net.lax1dude.eaglercraft.v1_8.update.GuiUpdateCheckerOverlay; -import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceMenu; -import net.minecraft.client.Minecraft; -import net.minecraft.client.audio.PositionedSoundRecord; -import net.minecraft.client.gui.achievement.GuiAchievements; -import net.minecraft.client.gui.achievement.GuiStats; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.client.resources.I18n; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.ResourceLocation; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiIngameMenu extends GuiScreen { - - private GuiButton lanButton; - - boolean hasSentAutoSave = !SingleplayerServerController.isWorldRunning(); - - private GuiUpdateCheckerOverlay updateCheckerOverlay; - private GuiVoiceMenu voiceMenu; - - public GuiIngameMenu() { - updateCheckerOverlay = new GuiUpdateCheckerOverlay(true, this); - if (EagRuntime.getConfiguration().isAllowVoiceClient()) { - voiceMenu = new GuiVoiceMenu(this); - } - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - this.buttonList.clear(); - this.updateCheckerOverlay.setResolution(mc, width, height); - byte b0 = -16; - boolean flag = true; - this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + b0, - I18n.format("menu.returnToMenu", new Object[0]))); - if (!this.mc.isIntegratedServerRunning()) { - ((GuiButton) this.buttonList.get(0)).displayString = I18n.format("menu.disconnect", new Object[0]); - } - - this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 4 + 24 + b0, - I18n.format("menu.returnToGame", new Object[0]))); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + b0, 98, 20, - I18n.format("menu.options", new Object[0]))); - this.buttonList.add(lanButton = new GuiButton(7, this.width / 2 + 2, this.height / 4 + 96 + b0, 98, 20, - I18n.format(LANServerController.isLANOpen() ? "menu.closeLan" : "menu.openToLan", new Object[0]))); - this.buttonList.add(new GuiButton(5, this.width / 2 - 100, this.height / 4 + 48 + b0, 98, 20, - I18n.format("gui.achievements", new Object[0]))); - this.buttonList.add(new GuiButton(6, this.width / 2 + 2, this.height / 4 + 48 + b0, 98, 20, - I18n.format("gui.stats", new Object[0]))); - lanButton.enabled = SingleplayerServerController.isWorldRunning(); - if (!hasSentAutoSave) { - hasSentAutoSave = true; - SingleplayerServerController.autoSave(); - } - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - switch (parGuiButton.id) { - case 0: - this.mc.displayGuiScreen(new GuiOptions(this, this.mc.gameSettings)); - break; - case 1: - boolean flag = this.mc.isIntegratedServerRunning() || this.mc.isDemo(); - parGuiButton.enabled = false; - this.mc.theWorld.sendQuittingDisconnectingPacket(); - this.mc.loadWorld((WorldClient) null); - if (flag) { - this.mc.shutdownIntegratedServer(new GuiMainMenu()); - } else { - this.mc.shutdownIntegratedServer(new GuiMultiplayer(new GuiMainMenu())); - } - case 2: - case 3: - default: - break; - case 4: - this.mc.displayGuiScreen((GuiScreen) null); - this.mc.setIngameFocus(); - break; - case 5: - this.mc.displayGuiScreen(new GuiAchievements(this, this.mc.thePlayer.getStatFileWriter())); - break; - case 6: - this.mc.displayGuiScreen(new GuiStats(this, this.mc.thePlayer.getStatFileWriter())); - break; - case 7: - if (!LANServerController.supported()) { - mc.displayGuiScreen(new GuiScreenLANNotSupported(this)); - } else if (LANServerController.isLANOpen()) { - if (LANServerController.hasPeers()) { - mc.displayGuiScreen(new GuiYesNo(this, I18n.format("networkSettings.delete"), - I18n.format("lanServer.wouldYouLikeToKick"), 0)); - } else { - confirmClicked(false, 0); - } - } else { - this.mc.displayGuiScreen(GuiScreenLANInfo.showLANInfoScreen( - new GuiShareToLan(this, this.mc.playerController.getCurrentGameType().getName()))); - } - break; - } - - } - - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - super.updateScreen(); - if (EagRuntime.getConfiguration().isAllowVoiceClient() - && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { - voiceMenu.updateScreen(); - } - if (Mouse.isActuallyGrabbed()) { - Mouse.setGrabbed(false); - } - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - this.drawDefaultBackground(); - this.drawCenteredString(this.fontRendererObj, I18n.format("menu.game", new Object[0]), this.width / 2, 20, - 16777215); - - this.updateCheckerOverlay.drawScreen(i, j, f); - - if (LANServerController.isLANOpen()) { - int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); - String str = I18n.format("lanServer.pauseMenu0"); - drawString(fontRendererObj, str, 6, 10 + offset, 0xFFFF55); - - if (mc.gameSettings.hideJoinCode) { - GlStateManager.pushMatrix(); - GlStateManager.translate(7.0f, 25.0f + offset, 0.0f); - GlStateManager.scale(0.75f, 0.75f, 0.75f); - str = I18n.format("lanServer.showCode"); - int w = fontRendererObj.getStringWidth(str); - boolean hover = i > 4 && i < 8 + w * 3 / 4 && j > 24 + offset && j < 25 + offset + 8; - drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); - GlStateManager.popMatrix(); - } else { - int w = fontRendererObj.getStringWidth(str); - GlStateManager.pushMatrix(); - GlStateManager.translate(6 + w + 3, 11 + offset, 0.0f); - GlStateManager.scale(0.75f, 0.75f, 0.75f); - str = I18n.format("lanServer.hideCode"); - int w2 = fontRendererObj.getStringWidth(str); - boolean hover = i > 6 + w + 2 && i < 6 + w + 3 + w2 * 3 / 4 && j > 11 + offset - 1 - && j < 11 + offset + 6; - drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); - GlStateManager.popMatrix(); - - drawString( - fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu1") + " " - + EnumChatFormatting.RESET + LANServerController.getCurrentURI(), - 6, 25 + offset, 0xFFFFFF); - drawString( - fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu2") + " " - + EnumChatFormatting.RESET + LANServerController.getCurrentCode(), - 6, 35 + offset, 0xFFFFFF); - } - } - - try { - if (EagRuntime.getConfiguration().isAllowVoiceClient() - && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { - if (voiceMenu.isBlockingInput()) { - super.drawScreen(0, 0, f); - } else { - super.drawScreen(i, j, f); - } - voiceMenu.drawScreen(i, j, f); - } else { - super.drawScreen(i, j, f); - } - } catch (GuiVoiceMenu.AbortedException ex) { - } - } - - protected void keyTyped(char par1, int par2) { - try { - if (EagRuntime.getConfiguration().isAllowVoiceClient() - && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { - voiceMenu.keyTyped(par1, par2); - } - super.keyTyped(par1, par2); - } catch (GuiVoiceMenu.AbortedException ex) { - } - } - - public void confirmClicked(boolean par1, int par2) { - mc.displayGuiScreen(this); - LANServerController.closeLANNoKick(); - if (par1) { - LANServerController.cleanupLAN(); - SingleplayerServerController.configureLAN(this.mc.theWorld.getWorldInfo().getGameType(), false); - } - this.mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(I18n.format("lanServer.closed"))); - this.lanButton.displayString = I18n.format("menu.openToLan"); - } - - protected void mouseClicked(int par1, int par2, int par3) { - try { - if (EagRuntime.getConfiguration().isAllowVoiceClient() - && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { - voiceMenu.mouseClicked(par1, par2, par3); - } - } catch (GuiVoiceMenu.AbortedException ex) { - return; - } - if (par3 == 0) { - int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); - if (mc.gameSettings.hideJoinCode) { - String str = I18n.format("lanServer.showCode"); - int w = fontRendererObj.getStringWidth(str); - if (par1 > 4 && par1 < 8 + w * 3 / 4 && par2 > 24 + offset && par2 < 25 + offset + 8) { - mc.gameSettings.hideJoinCode = false; - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - mc.gameSettings.saveOptions(); - } - } else { - String str = I18n.format("lanServer.pauseMenu0"); - int w = fontRendererObj.getStringWidth(str); - str = I18n.format("lanServer.hideCode"); - int w2 = fontRendererObj.getStringWidth(str); - if (par1 > 6 + w + 2 && par1 < 6 + w + 3 + w2 * 3 / 4 && par2 > 11 + offset - 1 - && par2 < 11 + offset + 6) { - mc.gameSettings.hideJoinCode = true; - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - mc.gameSettings.saveOptions(); - } - } - - } - this.updateCheckerOverlay.mouseClicked(par1, par2, par3); - super.mouseClicked(par1, par2, par3); - } - - public void setWorldAndResolution(Minecraft par1Minecraft, int par2, int par3) { - super.setWorldAndResolution(par1Minecraft, par2, par3); - if (EagRuntime.getConfiguration().isAllowVoiceClient()) { - voiceMenu.setResolution(par1Minecraft, par2, par3); - } - } - - protected void mouseReleased(int par1, int par2, int par3) { - try { - if (EagRuntime.getConfiguration().isAllowVoiceClient() - && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { - voiceMenu.mouseReleased(par1, par2, par3); - } - super.mouseReleased(par1, par2, par3); - } catch (GuiVoiceMenu.AbortedException ex) { - } - } +package net.minecraft.client.gui; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiButtonWithStupidIcons; +import net.lax1dude.eaglercraft.v1_8.notifications.GuiButtonNotifBell; +import net.lax1dude.eaglercraft.v1_8.notifications.GuiScreenNotifications; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANInfo; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANNotSupported; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiShareToLan; +import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; +import net.lax1dude.eaglercraft.v1_8.update.GuiUpdateCheckerOverlay; +import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceMenu; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenPhishingWaring; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenRecieveServerInfo; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenServerInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.achievement.GuiAchievements; +import net.minecraft.client.gui.achievement.GuiStats; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiIngameMenu extends GuiScreen { + + private GuiButton lanButton; + + boolean hasSentAutoSave = !SingleplayerServerController.isWorldRunning(); + + private GuiUpdateCheckerOverlay updateCheckerOverlay; + private GuiVoiceMenu voiceMenu; + private GuiButtonNotifBell notifBellButton; + + public GuiIngameMenu() { + updateCheckerOverlay = new GuiUpdateCheckerOverlay(true, this); + if (EagRuntime.getConfiguration().isAllowVoiceClient()) { + voiceMenu = new GuiVoiceMenu(this); + } + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + this.buttonList.clear(); + this.updateCheckerOverlay.setResolution(mc, width, height); + byte b0 = -16; + boolean flag = true; + this.buttonList.add(new GuiButtonWithStupidIcons(1, this.width / 2 - 100, this.height / 4 + 120 + b0, + I18n.format("menu.returnToMenu", new Object[0]), PauseMenuCustomizeState.icon_disconnect_L, + PauseMenuCustomizeState.icon_disconnect_L_aspect, PauseMenuCustomizeState.icon_disconnect_R, + PauseMenuCustomizeState.icon_disconnect_R_aspect)); + if (!this.mc.isIntegratedServerRunning()) { + ((GuiButton) this.buttonList.get(0)).displayString = I18n.format("menu.disconnect", new Object[0]); + if (this.mc.thePlayer != null && this.mc.thePlayer.sendQueue.getEaglerMessageProtocol().ver >= 4) { + this.buttonList.add(notifBellButton = new GuiButtonNotifBell(11, width - 22, height - 22)); + notifBellButton.setUnread(mc.thePlayer.sendQueue.getNotifManager().getUnread()); + } + } + + this.buttonList.add(new GuiButtonWithStupidIcons(4, this.width / 2 - 100, this.height / 4 + 24 + b0, + I18n.format("menu.returnToGame", new Object[0]), PauseMenuCustomizeState.icon_backToGame_L, + PauseMenuCustomizeState.icon_backToGame_L_aspect, PauseMenuCustomizeState.icon_backToGame_R, + PauseMenuCustomizeState.icon_backToGame_R_aspect)); + this.buttonList.add(new GuiButtonWithStupidIcons(0, this.width / 2 - 100, this.height / 4 + 96 + b0, 98, 20, + I18n.format("menu.options", new Object[0]), PauseMenuCustomizeState.icon_options_L, + PauseMenuCustomizeState.icon_options_L_aspect, PauseMenuCustomizeState.icon_options_R, + PauseMenuCustomizeState.icon_options_R_aspect)); + this.buttonList + .add(lanButton = new GuiButtonWithStupidIcons(7, this.width / 2 + 2, this.height / 4 + 96 + b0, 98, 20, + I18n.format(LANServerController.isLANOpen() ? "menu.closeLan" : "menu.openToLan", + new Object[0]), + PauseMenuCustomizeState.icon_discord_L, PauseMenuCustomizeState.icon_discord_L_aspect, + PauseMenuCustomizeState.icon_discord_R, PauseMenuCustomizeState.icon_discord_R_aspect)); + this.buttonList.add(new GuiButtonWithStupidIcons(5, this.width / 2 - 100, this.height / 4 + 48 + b0, 98, 20, + I18n.format("gui.achievements", new Object[0]), PauseMenuCustomizeState.icon_achievements_L, + PauseMenuCustomizeState.icon_achievements_L_aspect, PauseMenuCustomizeState.icon_achievements_R, + PauseMenuCustomizeState.icon_achievements_R_aspect)); + this.buttonList.add(new GuiButtonWithStupidIcons(6, this.width / 2 + 2, this.height / 4 + 48 + b0, 98, 20, + I18n.format("gui.stats", new Object[0]), PauseMenuCustomizeState.icon_statistics_L, + PauseMenuCustomizeState.icon_statistics_L_aspect, PauseMenuCustomizeState.icon_statistics_R, + PauseMenuCustomizeState.icon_statistics_R_aspect)); + lanButton.enabled = SingleplayerServerController.isWorldRunning(); + if (PauseMenuCustomizeState.discordButtonMode != PauseMenuCustomizeState.DISCORD_MODE_NONE) { + lanButton.enabled = true; + lanButton.id = 8; + lanButton.displayString = "" + PauseMenuCustomizeState.discordButtonText; + } + if (PauseMenuCustomizeState.serverInfoMode != PauseMenuCustomizeState.DISCORD_MODE_NONE) { + this.buttonList.add(new GuiButtonWithStupidIcons(9, this.width / 2 - 100, this.height / 4 + 72 + b0, + PauseMenuCustomizeState.serverInfoButtonText, PauseMenuCustomizeState.icon_serverInfo_L, + PauseMenuCustomizeState.icon_serverInfo_L_aspect, PauseMenuCustomizeState.icon_serverInfo_R, + PauseMenuCustomizeState.icon_serverInfo_R_aspect)); + } + if (!hasSentAutoSave) { + hasSentAutoSave = true; + SingleplayerServerController.autoSave(); + } + } + + /** + * + + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + switch (parGuiButton.id) { + case 0: + this.mc.displayGuiScreen(new GuiOptions(this, this.mc.gameSettings)); + break; + case 1: + boolean flag = this.mc.isIntegratedServerRunning() || this.mc.isDemo(); + parGuiButton.enabled = false; + this.mc.theWorld.sendQuittingDisconnectingPacket(); + this.mc.loadWorld((WorldClient) null); + if (flag) { + this.mc.shutdownIntegratedServer(new GuiMainMenu()); + } else { + this.mc.shutdownIntegratedServer(new GuiMultiplayer(new GuiMainMenu())); + } + case 2: + case 3: + default: + break; + case 4: + this.mc.displayGuiScreen((GuiScreen) null); + this.mc.setIngameFocus(); + break; + case 5: + this.mc.displayGuiScreen(new GuiAchievements(this, this.mc.thePlayer.getStatFileWriter())); + break; + case 6: + this.mc.displayGuiScreen(new GuiStats(this, this.mc.thePlayer.getStatFileWriter())); + break; + case 7: + if (!LANServerController.supported()) { + mc.displayGuiScreen(new GuiScreenLANNotSupported(this)); + } else if (LANServerController.isLANOpen()) { + if (LANServerController.hasPeers()) { + mc.displayGuiScreen(new GuiYesNo(this, I18n.format("networkSettings.delete"), + I18n.format("lanServer.wouldYouLikeToKick"), 0)); + } else { + confirmClicked(false, 0); + } + } else { + this.mc.displayGuiScreen(GuiScreenLANInfo.showLANInfoScreen( + new GuiShareToLan(this, this.mc.playerController.getCurrentGameType().getName()))); + } + break; + case 8: + if (PauseMenuCustomizeState.discordButtonMode == PauseMenuCustomizeState.DISCORD_MODE_INVITE_URL + && PauseMenuCustomizeState.discordInviteURL != null) { + EagRuntime.openLink(PauseMenuCustomizeState.discordInviteURL); + } + break; + case 9: + switch (PauseMenuCustomizeState.serverInfoMode) { + case PauseMenuCustomizeState.SERVER_INFO_MODE_EXTERNAL_URL: + if (PauseMenuCustomizeState.serverInfoURL != null) { + EagRuntime.openLink(PauseMenuCustomizeState.serverInfoURL); + } + break; + case PauseMenuCustomizeState.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP: + if (PauseMenuCustomizeState.serverInfoURL != null) { + GuiScreen screen = GuiScreenServerInfo.createForCurrentState(this, + PauseMenuCustomizeState.serverInfoURL); + if (!this.mc.gameSettings.hasHiddenPhishWarning && !GuiScreenPhishingWaring.hasShownMessage) { + screen = new GuiScreenPhishingWaring(screen); + } + this.mc.displayGuiScreen(screen); + } + break; + case PauseMenuCustomizeState.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS: + if (PauseMenuCustomizeState.serverInfoHash != null) { + GuiScreen screen = new GuiScreenRecieveServerInfo(this, PauseMenuCustomizeState.serverInfoHash); + if (!this.mc.gameSettings.hasHiddenPhishWarning && !GuiScreenPhishingWaring.hasShownMessage) { + screen = new GuiScreenPhishingWaring(screen); + } + this.mc.displayGuiScreen(screen); + } + break; + default: + break; + } + break; + case 11: + this.mc.displayGuiScreen(new GuiScreenNotifications(this)); + break; + } + + } + + /** + * + + * Called from the main game loop to update the screen. + */ + public void updateScreen() { + super.updateScreen(); + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.updateScreen(); + } + if (Mouse.isActuallyGrabbed()) { + Mouse.setGrabbed(false); + } + if (notifBellButton != null && mc.thePlayer != null) { + notifBellButton.setUnread(mc.thePlayer.sendQueue.getNotifManager().getUnread()); + } + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + this.drawDefaultBackground(); + String titleStr = I18n.format("menu.game", new Object[0]); + int titleStrWidth = fontRendererObj.getStringWidth(titleStr); + this.drawString(this.fontRendererObj, titleStr, (this.width - titleStrWidth) / 2, 20, 16777215); + if (PauseMenuCustomizeState.icon_title_L != null) { + mc.getTextureManager().bindTexture(PauseMenuCustomizeState.icon_title_L); + GlStateManager.pushMatrix(); + GlStateManager.translate( + (this.width - titleStrWidth) / 2 - 6 - 16 * PauseMenuCustomizeState.icon_title_L_aspect, 16, 0.0f); + float f2 = 16.0f / 256.0f; + GlStateManager.scale(f2 * PauseMenuCustomizeState.icon_title_L_aspect, f2, f2); + this.drawTexturedModalRect(0, 0, 0, 0, 256, 256); + GlStateManager.popMatrix(); + } + if (PauseMenuCustomizeState.icon_title_R != null) { + mc.getTextureManager().bindTexture(PauseMenuCustomizeState.icon_title_L); + GlStateManager.pushMatrix(); + GlStateManager.translate((this.width - titleStrWidth) / 2 + titleStrWidth + 6, 16, 0.0f); + float f2 = 16.0f / 256.0f; + GlStateManager.scale(f2 * PauseMenuCustomizeState.icon_title_R_aspect, f2, f2); + this.drawTexturedModalRect(0, 0, 0, 0, 256, 256); + GlStateManager.popMatrix(); + } + + this.updateCheckerOverlay.drawScreen(i, j, f); + + if (LANServerController.isLANOpen()) { + int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); + String str = I18n.format("lanServer.pauseMenu0"); + drawString(fontRendererObj, str, 6, 10 + offset, 0xFFFF55); + + if (mc.gameSettings.hideJoinCode) { + GlStateManager.pushMatrix(); + GlStateManager.translate(7.0f, 25.0f + offset, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + str = I18n.format("lanServer.showCode"); + int w = fontRendererObj.getStringWidth(str); + boolean hover = i > 4 && i < 8 + w * 3 / 4 && j > 24 + offset && j < 25 + offset + 8; + drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); + GlStateManager.popMatrix(); + } else { + int w = fontRendererObj.getStringWidth(str); + GlStateManager.pushMatrix(); + GlStateManager.translate(6 + w + 3, 11 + offset, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + str = I18n.format("lanServer.hideCode"); + int w2 = fontRendererObj.getStringWidth(str); + boolean hover = i > 6 + w + 2 && i < 6 + w + 3 + w2 * 3 / 4 && j > 11 + offset - 1 + && j < 11 + offset + 6; + drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); + GlStateManager.popMatrix(); + + drawString( + fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu1") + " " + + EnumChatFormatting.RESET + LANServerController.getCurrentURI(), + 6, 25 + offset, 0xFFFFFF); + drawString( + fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu2") + " " + + EnumChatFormatting.RESET + LANServerController.getCurrentCode(), + 6, 35 + offset, 0xFFFFFF); + } + } + + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + if (voiceMenu.isBlockingInput()) { + super.drawScreen(0, 0, f); + } else { + super.drawScreen(i, j, f); + } + voiceMenu.drawScreen(i, j, f); + } else { + super.drawScreen(i, j, f); + } + } catch (GuiVoiceMenu.AbortedException ex) { + } + } + + protected void keyTyped(char par1, int par2) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.keyTyped(par1, par2); + } + super.keyTyped(par1, par2); + } catch (GuiVoiceMenu.AbortedException ex) { + } + } + + public void confirmClicked(boolean par1, int par2) { + mc.displayGuiScreen(this); + LANServerController.closeLANNoKick(); + if (par1) { + LANServerController.cleanupLAN(); + SingleplayerServerController.configureLAN(this.mc.theWorld.getWorldInfo().getGameType(), false); + } + this.mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(I18n.format("lanServer.closed"))); + this.lanButton.displayString = I18n.format("menu.openToLan"); + } + + protected void mouseClicked(int par1, int par2, int par3) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.mouseClicked(par1, par2, par3); + } + } catch (GuiVoiceMenu.AbortedException ex) { + return; + } + if (par3 == 0) { + int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); + if (mc.gameSettings.hideJoinCode) { + String str = I18n.format("lanServer.showCode"); + int w = fontRendererObj.getStringWidth(str); + if (par1 > 4 && par1 < 8 + w * 3 / 4 && par2 > 24 + offset && par2 < 25 + offset + 8) { + mc.gameSettings.hideJoinCode = false; + this.mc.getSoundHandler() + .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + mc.gameSettings.saveOptions(); + } + } else { + String str = I18n.format("lanServer.pauseMenu0"); + int w = fontRendererObj.getStringWidth(str); + str = I18n.format("lanServer.hideCode"); + int w2 = fontRendererObj.getStringWidth(str); + if (par1 > 6 + w + 2 && par1 < 6 + w + 3 + w2 * 3 / 4 && par2 > 11 + offset - 1 + && par2 < 11 + offset + 6) { + mc.gameSettings.hideJoinCode = true; + this.mc.getSoundHandler() + .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + mc.gameSettings.saveOptions(); + } + } + + } + this.updateCheckerOverlay.mouseClicked(par1, par2, par3); + super.mouseClicked(par1, par2, par3); + } + + public void setWorldAndResolution(Minecraft par1Minecraft, int par2, int par3) { + super.setWorldAndResolution(par1Minecraft, par2, par3); + if (EagRuntime.getConfiguration().isAllowVoiceClient()) { + voiceMenu.setResolution(par1Minecraft, par2, par3); + } + } + + protected void mouseReleased(int par1, int par2, int par3) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.mouseReleased(par1, par2, par3); + } + super.mouseReleased(par1, par2, par3); + } catch (GuiVoiceMenu.AbortedException ex) { + } + } + + protected boolean isPartOfPauseMenu() { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiKeyBindingList.java b/src/game/java/net/minecraft/client/gui/GuiKeyBindingList.java similarity index 87% rename from src/main/java/net/minecraft/client/gui/GuiKeyBindingList.java rename to src/game/java/net/minecraft/client/gui/GuiKeyBindingList.java index 1f54443..8af7e11 100644 --- a/src/main/java/net/minecraft/client/gui/GuiKeyBindingList.java +++ b/src/game/java/net/minecraft/client/gui/GuiKeyBindingList.java @@ -3,6 +3,7 @@ package net.minecraft.client.gui; import java.util.Arrays; import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; import net.minecraft.client.settings.GameSettings; @@ -45,7 +46,7 @@ public class GuiKeyBindingList extends GuiListExtended { private int maxListLabelWidth = 0; public GuiKeyBindingList(GuiControls controls, Minecraft mcIn) { - super(mcIn, controls.width, controls.height, 63, controls.height - 32, 20); + super(mcIn, controls.width, controls.height, 66, controls.height - 32, 20); this.field_148191_k = controls; this.mc = mcIn; KeyBinding[] akeybinding = (KeyBinding[]) ArrayUtils.clone(mcIn.gameSettings.keyBindings); @@ -172,10 +173,15 @@ public class GuiKeyBindingList extends GuiListExtended { } public boolean mousePressed(int var1, int i, int j, int var4, int var5, int var6) { - if (this.btnChangeKeyBinding.mousePressed(GuiKeyBindingList.this.mc, i, j)) { + if (var4 != 0 && var4 != 12345) + return false; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if ((!touchMode || (this.btnChangeKeyBinding.isSliderTouchEvents() == (var4 == 12345))) + && this.btnChangeKeyBinding.mousePressed(GuiKeyBindingList.this.mc, i, j)) { GuiKeyBindingList.this.field_148191_k.buttonId = this.keybinding; return true; - } else if (this.btnReset.mousePressed(GuiKeyBindingList.this.mc, i, j)) { + } else if ((!touchMode || (this.btnReset.isSliderTouchEvents() == (var4 == 12345))) + && this.btnReset.mousePressed(GuiKeyBindingList.this.mc, i, j)) { GuiKeyBindingList.this.mc.gameSettings.setOptionKeyBinding(this.keybinding, this.keybinding.getKeyCodeDefault()); KeyBinding.resetKeyBindingArrayAndHash(); @@ -186,8 +192,13 @@ public class GuiKeyBindingList extends GuiListExtended { } public void mouseReleased(int var1, int i, int j, int var4, int var5, int var6) { - this.btnChangeKeyBinding.mouseReleased(i, j); - this.btnReset.mouseReleased(i, j); + if (var4 != 0 && var4 != 12345) + return; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if (!touchMode || (this.btnChangeKeyBinding.isSliderTouchEvents() == (var4 == 12345))) + this.btnChangeKeyBinding.mouseReleased(i, j); + if (!touchMode || (this.btnReset.isSliderTouchEvents() == (var4 == 12345))) + this.btnReset.mouseReleased(i, j); } public void setSelected(int var1, int var2, int var3) { diff --git a/src/main/java/net/minecraft/client/gui/GuiLabel.java b/src/game/java/net/minecraft/client/gui/GuiLabel.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiLabel.java rename to src/game/java/net/minecraft/client/gui/GuiLabel.java diff --git a/src/main/java/net/minecraft/client/gui/GuiLanguage.java b/src/game/java/net/minecraft/client/gui/GuiLanguage.java similarity index 88% rename from src/main/java/net/minecraft/client/gui/GuiLanguage.java rename to src/game/java/net/minecraft/client/gui/GuiLanguage.java index c0b0fda..1add8e9 100644 --- a/src/main/java/net/minecraft/client/gui/GuiLanguage.java +++ b/src/game/java/net/minecraft/client/gui/GuiLanguage.java @@ -80,32 +80,36 @@ public class GuiLanguage extends GuiScreen { this.list.handleMouseInput(); } - /** - * + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.list.handleTouchInput(); + } + + /**+ * Called by the controls from the buttonList when activated. * (Mouse pressed for buttons) */ protected void actionPerformed(GuiButton parGuiButton) { if (parGuiButton.enabled) { switch (parGuiButton.id) { - case 5: - break; - case 6: - this.mc.displayGuiScreen(this.parentScreen); - break; - case 100: - if (parGuiButton instanceof GuiOptionButton) { - this.game_settings_3.setOptionValue(((GuiOptionButton) parGuiButton).returnEnumOptions(), 1); - parGuiButton.displayString = this.game_settings_3 - .getKeyBinding(GameSettings.Options.FORCE_UNICODE_FONT); - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - int i = scaledresolution.getScaledWidth(); - int j = scaledresolution.getScaledHeight(); - this.setWorldAndResolution(this.mc, i, j); - } - break; - default: - this.list.actionPerformed(parGuiButton); + case 5: + break; + case 6: + this.mc.displayGuiScreen(this.parentScreen); + break; + case 100: + if (parGuiButton instanceof GuiOptionButton) { + this.game_settings_3.setOptionValue(((GuiOptionButton) parGuiButton).returnEnumOptions(), 1); + parGuiButton.displayString = this.game_settings_3 + .getKeyBinding(GameSettings.Options.FORCE_UNICODE_FONT); + ScaledResolution scaledresolution = this.mc.scaledResolution; + int i = scaledresolution.getScaledWidth(); + int j = scaledresolution.getScaledHeight(); + this.setWorldAndResolution(this.mc, i, j); + } + break; + default: + this.list.actionPerformed(parGuiButton); } } diff --git a/src/main/java/net/minecraft/client/gui/GuiListButton.java b/src/game/java/net/minecraft/client/gui/GuiListButton.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiListButton.java rename to src/game/java/net/minecraft/client/gui/GuiListButton.java diff --git a/src/main/java/net/minecraft/client/gui/GuiListExtended.java b/src/game/java/net/minecraft/client/gui/GuiListExtended.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiListExtended.java rename to src/game/java/net/minecraft/client/gui/GuiListExtended.java diff --git a/src/main/java/net/minecraft/client/gui/GuiLockIconButton.java b/src/game/java/net/minecraft/client/gui/GuiLockIconButton.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiLockIconButton.java rename to src/game/java/net/minecraft/client/gui/GuiLockIconButton.java diff --git a/src/main/java/net/minecraft/client/gui/GuiMainMenu.java b/src/game/java/net/minecraft/client/gui/GuiMainMenu.java similarity index 99% rename from src/main/java/net/minecraft/client/gui/GuiMainMenu.java rename to src/game/java/net/minecraft/client/gui/GuiMainMenu.java index 69b9f33..ca0388a 100644 --- a/src/main/java/net/minecraft/client/gui/GuiMainMenu.java +++ b/src/game/java/net/minecraft/client/gui/GuiMainMenu.java @@ -271,7 +271,7 @@ public class GuiMainMenu extends GuiScreen implements GuiYesNoCallback { backgroundTexture = this.mc.getTextureManager().getDynamicTextureLocation("background", viewportTexture); } this.updateCheckerOverlay.setResolution(mc, width, height); - Calendar calendar = EagRuntime.getLocaleCalendar(); + Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); if (calendar.get(2) + 1 == 12 && calendar.get(5) == 24) { this.splashText = "Merry X-mas!"; diff --git a/src/main/java/net/minecraft/client/gui/GuiMemoryErrorScreen.java b/src/game/java/net/minecraft/client/gui/GuiMemoryErrorScreen.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiMemoryErrorScreen.java rename to src/game/java/net/minecraft/client/gui/GuiMemoryErrorScreen.java diff --git a/src/main/java/net/minecraft/client/gui/GuiMerchant.java b/src/game/java/net/minecraft/client/gui/GuiMerchant.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiMerchant.java rename to src/game/java/net/minecraft/client/gui/GuiMerchant.java diff --git a/src/main/java/net/minecraft/client/gui/GuiMultiplayer.java b/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java similarity index 96% rename from src/main/java/net/minecraft/client/gui/GuiMultiplayer.java rename to src/game/java/net/minecraft/client/gui/GuiMultiplayer.java index 50e1276..9a99941 100644 --- a/src/main/java/net/minecraft/client/gui/GuiMultiplayer.java +++ b/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java @@ -5,9 +5,11 @@ import java.io.IOException; import com.google.common.base.Splitter; import com.google.common.collect.Lists; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -127,6 +129,11 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { this.serverListSelector.handleMouseInput(); } + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.serverListSelector.handleTouchInput(); + } + public void createButtons() { this.buttonList.add(this.btnEditServer = new GuiButton(7, this.width / 2 - 154, this.height - 28, 70, 20, I18n.format("selectServer.edit", new Object[0]))); @@ -210,7 +217,7 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { } else if (parGuiButton.id == 0) { this.mc.displayGuiScreen(this.parentScreen); } else if (parGuiButton.id == 8) { - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); if (millis - lastRefreshCommit > 700l) { lastRefreshCommit = millis; this.refreshServerList(); @@ -235,7 +242,7 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { this.serverListSelector.setSelectedSlotIndex(-1); this.serverListSelector.func_148195_a(this.savedServerList); } - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); if (millis - lastRefreshCommit > 700l) { lastRefreshCommit = millis; this.refreshServerList(); @@ -250,12 +257,15 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { } else if (this.addingServer) { this.addingServer = false; if (flag) { + if (!this.selectedServer.enableCookies) { + ServerCookieDataStore.clearCookie(this.selectedServer.serverIP); + } this.savedServerList.addServerData(this.selectedServer); this.savedServerList.saveServerList(); this.serverListSelector.setSelectedSlotIndex(-1); this.serverListSelector.func_148195_a(this.savedServerList); } - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); if (millis - lastRefreshCommit > 700l) { lastRefreshCommit = millis; this.refreshServerList(); @@ -266,11 +276,14 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { ServerData serverdata = ((ServerListEntryNormal) guilistextended$iguilistentry).getServerData(); serverdata.serverName = this.selectedServer.serverName; serverdata.serverIP = this.selectedServer.serverIP; + if (serverdata.enableCookies && !this.selectedServer.enableCookies) { + ServerCookieDataStore.clearCookie(this.selectedServer.serverIP); + } serverdata.copyFrom(this.selectedServer); this.savedServerList.saveServerList(); this.serverListSelector.func_148195_a(this.savedServerList); } - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); if (millis - lastRefreshCommit > 700l) { lastRefreshCommit = millis; this.refreshServerList(); @@ -354,6 +367,7 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { // drawPluginDownloadLink(i, j); if (this.hoveringText != null) { this.drawHoveringText(Lists.newArrayList(Splitter.on("\n").split(this.hoveringText)), i, j); + GlStateManager.disableLighting(); } } // HoosierTransfer mod diff --git a/src/main/java/net/minecraft/client/gui/GuiNewChat.java b/src/game/java/net/minecraft/client/gui/GuiNewChat.java similarity index 99% rename from src/main/java/net/minecraft/client/gui/GuiNewChat.java rename to src/game/java/net/minecraft/client/gui/GuiNewChat.java index 399bf8c..5c99c92 100644 --- a/src/main/java/net/minecraft/client/gui/GuiNewChat.java +++ b/src/game/java/net/minecraft/client/gui/GuiNewChat.java @@ -259,7 +259,7 @@ public class GuiNewChat extends Gui { if (!this.getChatOpen()) { return null; } else { - ScaledResolution scaledresolution = new ScaledResolution(this.mc); + ScaledResolution scaledresolution = mc.scaledResolution; int i = scaledresolution.getScaleFactor(); float f = this.getChatScale(); int j = parInt1 / i - 3; diff --git a/src/main/java/net/minecraft/client/gui/GuiOptionButton.java b/src/game/java/net/minecraft/client/gui/GuiOptionButton.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiOptionButton.java rename to src/game/java/net/minecraft/client/gui/GuiOptionButton.java diff --git a/src/main/java/net/minecraft/client/gui/GuiOptionSlider.java b/src/game/java/net/minecraft/client/gui/GuiOptionSlider.java similarity index 96% rename from src/main/java/net/minecraft/client/gui/GuiOptionSlider.java rename to src/game/java/net/minecraft/client/gui/GuiOptionSlider.java index e5f51bb..a24774e 100644 --- a/src/main/java/net/minecraft/client/gui/GuiOptionSlider.java +++ b/src/game/java/net/minecraft/client/gui/GuiOptionSlider.java @@ -35,7 +35,7 @@ import net.minecraft.util.MathHelper; * */ public class GuiOptionSlider extends GuiButton { - private float sliderValue; + public float sliderValue; public boolean dragging; private GameSettings.Options options; private final float field_146132_r; @@ -57,8 +57,11 @@ public class GuiOptionSlider extends GuiButton { this.displayString = minecraft.gameSettings.getKeyBinding(parOptions); } - /** - * + + public GameSettings.Options getEnumOptions() { + return options; + } + + /**+ * Returns 0 if the button is disabled, 1 if the mouse is NOT * hovering over this button and 2 if it IS hovering over this * button. @@ -118,4 +121,8 @@ public class GuiOptionSlider extends GuiButton { public void mouseReleased(int var1, int var2) { this.dragging = false; } + + public boolean isSliderTouchEvents() { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiOptions.java b/src/game/java/net/minecraft/client/gui/GuiOptions.java similarity index 79% rename from src/main/java/net/minecraft/client/gui/GuiOptions.java rename to src/game/java/net/minecraft/client/gui/GuiOptions.java index 4bc4e88..0c51686 100644 --- a/src/main/java/net/minecraft/client/gui/GuiOptions.java +++ b/src/game/java/net/minecraft/client/gui/GuiOptions.java @@ -1,311 +1,371 @@ -package net.minecraft.client.gui; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.Mouse; -import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; -import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.EaglerDeferredPipeline; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui.GuiShaderConfig; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui.GuiShadersNotSupported; -import net.lax1dude.eaglercraft.v1_8.profile.GuiScreenImportExportProfile; -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.minecraft.client.audio.PositionedSoundRecord; -import net.minecraft.client.resources.I18n; -import net.minecraft.client.settings.GameSettings; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.ChatComponentTranslation; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.EnumDifficulty; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiOptions extends GuiScreen implements GuiYesNoCallback { - private static final GameSettings.Options[] field_146440_f = new GameSettings.Options[] { - GameSettings.Options.FOV }; - private final GuiScreen field_146441_g; - private final GameSettings game_settings_1; - private GuiButton field_175357_i; - private GuiLockIconButton field_175356_r; - protected String field_146442_a = "Options"; - private GuiButton broadcastSettings; - - public GuiOptions(GuiScreen parGuiScreen, GameSettings parGameSettings) { - this.field_146441_g = parGuiScreen; - this.game_settings_1 = parGameSettings; - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - int i = 0; - this.field_146442_a = I18n.format("options.title", new Object[0]); - - for (int j = 0; j < field_146440_f.length; ++j) { - GameSettings.Options gamesettings$options = field_146440_f[j]; - if (gamesettings$options.getEnumFloat()) { - this.buttonList.add(new GuiOptionSlider(gamesettings$options.returnEnumOrdinal(), - this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), - gamesettings$options)); - } else { - GuiOptionButton guioptionbutton = new GuiOptionButton(gamesettings$options.returnEnumOrdinal(), - this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), gamesettings$options, - this.game_settings_1.getKeyBinding(gamesettings$options)); - this.buttonList.add(guioptionbutton); - } - - ++i; - } - - // this.buttonList.add(new - // GuiOptionSlider(GameSettings.Options.RENDER_SCALE.returnEnumOrdinal(), - // this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), - // GameSettings.Options.RENDER_SCALE)); - i++; - - this.buttonList.add( - new GuiButton(420, this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), 150, 20, - "Client Settings")); - i++; - - if (this.mc.theWorld != null) { - EnumDifficulty enumdifficulty = this.mc.theWorld.getDifficulty(); - this.field_175357_i = new GuiButton(108, this.width / 2 - 155 + i % 2 * 160, - this.height / 6 - 12 + 24 * (i >> 1), 150, 20, this.func_175355_a(enumdifficulty)); - this.buttonList.add(this.field_175357_i); - if (this.mc.isSingleplayer() && !this.mc.theWorld.getWorldInfo().isHardcoreModeEnabled()) { - this.field_175357_i.setWidth(this.field_175357_i.getButtonWidth() - 20); - this.field_175356_r = new GuiLockIconButton(109, - this.field_175357_i.xPosition + this.field_175357_i.getButtonWidth(), - this.field_175357_i.yPosition); - this.buttonList.add(this.field_175356_r); - this.field_175356_r.func_175229_b(this.mc.theWorld.getWorldInfo().isDifficultyLocked()); - this.field_175356_r.enabled = !this.field_175356_r.func_175230_c(); - this.field_175357_i.enabled = !this.field_175356_r.func_175230_c(); - } else { - this.field_175357_i.enabled = false; - } - } - - this.buttonList.add(new GuiButton(110, this.width / 2 - 155, this.height / 6 + 48 - 6, 150, 20, - I18n.format("options.skinCustomisation", new Object[0]))); - this.buttonList.add(new GuiButton(8675309, this.width / 2 + 5, this.height / 6 + 48 - 6, 150, 20, - I18n.format("shaders.gui.optionsButton"))); - this.buttonList.add(new GuiButton(106, this.width / 2 - 155, this.height / 6 + 72 - 6, 150, 20, - I18n.format("options.sounds", new Object[0]))); - this.buttonList.add(broadcastSettings = new GuiButton(107, this.width / 2 + 5, this.height / 6 + 72 - 6, 150, - 20, I18n.format(EagRuntime.getRecText(), new Object[0]))); - broadcastSettings.enabled = EagRuntime.recSupported(); - this.buttonList.add(new GuiButton(101, this.width / 2 - 155, this.height / 6 + 96 - 6, 150, 20, - I18n.format("options.video", new Object[0]))); - this.buttonList.add(new GuiButton(100, this.width / 2 + 5, this.height / 6 + 96 - 6, 150, 20, - I18n.format("options.controls", new Object[0]))); - this.buttonList.add(new GuiButton(102, this.width / 2 - 155, this.height / 6 + 120 - 6, 150, 20, - I18n.format("options.language", new Object[0]))); - this.buttonList.add(new GuiButton(103, this.width / 2 + 5, this.height / 6 + 120 - 6, 150, 20, - I18n.format("options.chat.title", new Object[0]))); - GuiButton btn; - this.buttonList.add(btn = new GuiButton(105, this.width / 2 - 155, this.height / 6 + 144 - 6, 150, 20, - I18n.format("options.resourcepack", new Object[0]))); - btn.enabled = EaglerFolderResourcePack.isSupported(); - this.buttonList.add(btn = new GuiButton(104, this.width / 2 + 5, this.height / 6 + 144 - 6, 150, 20, - I18n.format("options.debugConsoleButton", new Object[0]))); - btn.enabled = EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP; - this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height / 6 + 168, - I18n.format("gui.done", new Object[0]))); - } - - public String func_175355_a(EnumDifficulty parEnumDifficulty) { - ChatComponentText chatcomponenttext = new ChatComponentText(""); - chatcomponenttext.appendSibling(new ChatComponentTranslation("options.difficulty", new Object[0])); - chatcomponenttext.appendText(": "); - chatcomponenttext.appendSibling( - new ChatComponentTranslation(parEnumDifficulty.getDifficultyResourceKey(), new Object[0])); - return chatcomponenttext.getUnformattedText(); - } - - public void confirmClicked(boolean flag, int i) { - this.mc.displayGuiScreen(this); - if (i == 109 && flag && this.mc.theWorld != null) { - this.mc.theWorld.getWorldInfo().setDifficultyLocked(true); - SingleplayerServerController.setDifficulty(-1); - this.field_175356_r.func_175229_b(true); - this.field_175356_r.enabled = false; - this.field_175357_i.enabled = false; - } - - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.enabled) { - if (parGuiButton.id < 100 && parGuiButton instanceof GuiOptionButton) { - GameSettings.Options gamesettings$options = ((GuiOptionButton) parGuiButton).returnEnumOptions(); - this.game_settings_1.setOptionValue(gamesettings$options, 1); - parGuiButton.displayString = this.game_settings_1 - .getKeyBinding(GameSettings.Options.getEnumOptions(parGuiButton.id)); - } - - if (parGuiButton.id == 108) { - this.mc.theWorld.getWorldInfo().setDifficulty( - EnumDifficulty.getDifficultyEnum(this.mc.theWorld.getDifficulty().getDifficultyId() + 1)); - SingleplayerServerController - .setDifficulty(this.mc.theWorld.getWorldInfo().getDifficulty().getDifficultyId()); - this.field_175357_i.displayString = this.func_175355_a(this.mc.theWorld.getDifficulty()); - } - - if (parGuiButton.id == 109) { - this.mc.displayGuiScreen( - new GuiYesNo(this, - (new ChatComponentTranslation("difficulty.lock.title", new Object[0])) - .getFormattedText(), - (new ChatComponentTranslation("difficulty.lock.question", - new Object[] { new ChatComponentTranslation(this.mc.theWorld.getWorldInfo() - .getDifficulty().getDifficultyResourceKey(), new Object[0]) })) - .getFormattedText(), - 109)); - } - - if (parGuiButton.id == 110) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiCustomizeSkin(this)); - } - - if (parGuiButton.id == 8675309) { - if (EaglerDeferredPipeline.isSupported()) { - this.mc.displayGuiScreen(new GuiShaderConfig(this)); - } else { - this.mc.displayGuiScreen(new GuiShadersNotSupported(this, - I18n.format(EaglerDeferredPipeline.getReasonUnsupported()))); - } - } - - if (parGuiButton.id == 420) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiClientSettings(this, this.game_settings_1)); - } - - if (parGuiButton.id == 101) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiVideoSettings(this, this.game_settings_1)); - } - - if (parGuiButton.id == 100) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiControls(this, this.game_settings_1)); - } - - if (parGuiButton.id == 102) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiLanguage(this, this.game_settings_1, this.mc.getLanguageManager())); - } - - if (parGuiButton.id == 103) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new ScreenChatOptions(this, this.game_settings_1)); - } - - if (parGuiButton.id == 200) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(this.field_146441_g); - } - - if (parGuiButton.id == 105) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiScreenResourcePacks(this)); - } - - if (parGuiButton.id == 106) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(new GuiScreenOptionsSounds(this, this.game_settings_1)); - } - - if (parGuiButton.id == 107) { - EagRuntime.toggleRec(); - broadcastSettings.displayString = I18n.format(EagRuntime.getRecText(), new Object[0]); - } - - if (parGuiButton.id == 104) { - EagRuntime.showDebugConsole(); - } - } - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - this.drawDefaultBackground(); - this.drawCenteredString(this.fontRendererObj, this.field_146442_a, this.width / 2, 15, 16777215); - - if (mc.theWorld == null && !EagRuntime.getConfiguration().isDemo()) { - GlStateManager.pushMatrix(); - GlStateManager.scale(0.75f, 0.75f, 0.75f); - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - String text = I18n.format("editProfile.importExport"); - - int w = mc.fontRendererObj.getStringWidth(text); - boolean hover = i > 1 && j > 1 && i < (w * 3 / 4) + 7 && j < 12; - if (hover) { - Mouse.showCursor(EnumCursorType.HAND); - } - - drawString(mc.fontRendererObj, EnumChatFormatting.UNDERLINE + text, 5, 5, hover ? 0xFFEEEE22 : 0xFFCCCCCC); - - GlStateManager.popMatrix(); - } - - super.drawScreen(i, j, f); - } - - protected void mouseClicked(int mx, int my, int button) { - super.mouseClicked(mx, my, button); - if (mc.theWorld == null && !EagRuntime.getConfiguration().isDemo()) { - int w = mc.fontRendererObj.getStringWidth(I18n.format("editProfile.importExport")); - if (mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12) { - mc.displayGuiScreen(new GuiScreenImportExportProfile(this)); - mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - } - } - } +package net.minecraft.client.gui; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.boot_menu.GuiScreenEnterBootMenu; +import net.lax1dude.eaglercraft.v1_8.cookie.GuiScreenRevokeSessionToken; +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; +import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.EaglerDeferredPipeline; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui.GuiShaderConfig; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui.GuiShadersNotSupported; +import net.lax1dude.eaglercraft.v1_8.profile.GuiScreenImportExportProfile; +import net.lax1dude.eaglercraft.v1_8.recording.GuiScreenRecordingNote; +import net.lax1dude.eaglercraft.v1_8.recording.GuiScreenRecordingSettings; +import net.lax1dude.eaglercraft.v1_8.recording.ScreenRecordingController; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.EnumDifficulty; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiOptions extends GuiScreen implements GuiYesNoCallback { + private static final GameSettings.Options[] field_146440_f = new GameSettings.Options[] { + GameSettings.Options.FOV }; + private final GuiScreen field_146441_g; + private final GameSettings game_settings_1; + private GuiButton field_175357_i; + private GuiLockIconButton field_175356_r; + protected String field_146442_a = "Options"; + private GuiButton broadcastSettings; + + public GuiOptions(GuiScreen parGuiScreen, GameSettings parGameSettings) { + this.field_146441_g = parGuiScreen; + this.game_settings_1 = parGameSettings; + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + int i = 0; + this.field_146442_a = I18n.format("options.title", new Object[0]); + + for (int j = 0; j < field_146440_f.length; ++j) { + GameSettings.Options gamesettings$options = field_146440_f[j]; + if (gamesettings$options.getEnumFloat()) { + this.buttonList.add(new GuiOptionSlider(gamesettings$options.returnEnumOrdinal(), + this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), + gamesettings$options)); + } else { + GuiOptionButton guioptionbutton = new GuiOptionButton(gamesettings$options.returnEnumOrdinal(), + this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), gamesettings$options, + this.game_settings_1.getKeyBinding(gamesettings$options)); + this.buttonList.add(guioptionbutton); + } + + ++i; + } + + // this.buttonList.add(new + // GuiOptionSlider(GameSettings.Options.RENDER_SCALE.returnEnumOrdinal(), + // this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), + // GameSettings.Options.RENDER_SCALE)); + i++; + + this.buttonList.add( + new GuiButton(420, this.width / 2 - 155 + i % 2 * 160, this.height / 6 - 12 + 24 * (i >> 1), 150, 20, + "Client Settings")); + i++; + + if (this.mc.theWorld != null) { + EnumDifficulty enumdifficulty = this.mc.theWorld.getDifficulty(); + this.field_175357_i = new GuiButton(108, this.width / 2 - 155 + i % 2 * 160, + this.height / 6 - 12 + 24 * (i >> 1), 150, 20, this.func_175355_a(enumdifficulty)); + this.buttonList.add(this.field_175357_i); + if (this.mc.isSingleplayer() && !this.mc.theWorld.getWorldInfo().isHardcoreModeEnabled()) { + this.field_175357_i.setWidth(this.field_175357_i.getButtonWidth() - 20); + this.field_175356_r = new GuiLockIconButton(109, + this.field_175357_i.xPosition + this.field_175357_i.getButtonWidth(), + this.field_175357_i.yPosition); + this.buttonList.add(this.field_175356_r); + this.field_175356_r.func_175229_b(this.mc.theWorld.getWorldInfo().isDifficultyLocked()); + this.field_175356_r.enabled = !this.field_175356_r.func_175230_c(); + this.field_175357_i.enabled = !this.field_175356_r.func_175230_c(); + } else { + this.field_175357_i.enabled = false; + } + } + + this.buttonList.add(new GuiButton(110, this.width / 2 - 155, this.height / 6 + 48 - 6, 150, 20, + I18n.format("options.skinCustomisation", new Object[0]))); + this.buttonList.add(new GuiButton(8675309, this.width / 2 + 5, this.height / 6 + 48 - 6, 150, 20, + I18n.format("shaders.gui.optionsButton"))); + this.buttonList.add(new GuiButton(106, this.width / 2 - 155, this.height / 6 + 72 - 6, 150, 20, + I18n.format("options.sounds", new Object[0]))); + boolean support = ScreenRecordingController.isSupported(); + this.buttonList.add(broadcastSettings = new GuiButton(107, this.width / 2 + 5, this.height / 6 + 72 - 6, 150, + 20, I18n.format(support ? "options.screenRecording.button" : "options.screenRecording.unsupported"))); + broadcastSettings.enabled = support; + this.buttonList.add(new GuiButton(101, this.width / 2 - 155, this.height / 6 + 96 - 6, 150, 20, + I18n.format("options.video", new Object[0]))); + this.buttonList.add(new GuiButton(100, this.width / 2 + 5, this.height / 6 + 96 - 6, 150, 20, + I18n.format("options.controls", new Object[0]))); + this.buttonList.add(new GuiButton(102, this.width / 2 - 155, this.height / 6 + 120 - 6, 150, 20, + I18n.format("options.language", new Object[0]))); + this.buttonList.add(new GuiButton(103, this.width / 2 + 5, this.height / 6 + 120 - 6, 150, 20, + I18n.format("options.chat.title", new Object[0]))); + GuiButton btn; + this.buttonList.add(btn = new GuiButton(105, this.width / 2 - 155, this.height / 6 + 144 - 6, 150, 20, + I18n.format("options.resourcepack", new Object[0]))); + btn.enabled = EaglerFolderResourcePack.isSupported(); + this.buttonList.add(btn = new GuiButton(104, this.width / 2 + 5, this.height / 6 + 144 - 6, 150, 20, + I18n.format("options.debugConsoleButton", new Object[0]))); + btn.enabled = EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP; + this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height / 6 + 168, + I18n.format("gui.done", new Object[0]))); + } + + public String func_175355_a(EnumDifficulty parEnumDifficulty) { + ChatComponentText chatcomponenttext = new ChatComponentText(""); + chatcomponenttext.appendSibling(new ChatComponentTranslation("options.difficulty", new Object[0])); + chatcomponenttext.appendText(": "); + chatcomponenttext.appendSibling( + new ChatComponentTranslation(parEnumDifficulty.getDifficultyResourceKey(), new Object[0])); + return chatcomponenttext.getUnformattedText(); + } + + public void confirmClicked(boolean flag, int i) { + this.mc.displayGuiScreen(this); + if (i == 109 && flag && this.mc.theWorld != null) { + this.mc.theWorld.getWorldInfo().setDifficultyLocked(true); + SingleplayerServerController.setDifficulty(-1); + this.field_175356_r.func_175229_b(true); + this.field_175356_r.enabled = false; + this.field_175357_i.enabled = false; + } + + } + + /** + * + + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + if (parGuiButton.enabled) { + if (parGuiButton.id < 100 && parGuiButton instanceof GuiOptionButton) { + GameSettings.Options gamesettings$options = ((GuiOptionButton) parGuiButton).returnEnumOptions(); + this.game_settings_1.setOptionValue(gamesettings$options, 1); + parGuiButton.displayString = this.game_settings_1 + .getKeyBinding(GameSettings.Options.getEnumOptions(parGuiButton.id)); + } + + if (parGuiButton.id == 108) { + this.mc.theWorld.getWorldInfo().setDifficulty( + EnumDifficulty.getDifficultyEnum(this.mc.theWorld.getDifficulty().getDifficultyId() + 1)); + SingleplayerServerController + .setDifficulty(this.mc.theWorld.getWorldInfo().getDifficulty().getDifficultyId()); + this.field_175357_i.displayString = this.func_175355_a(this.mc.theWorld.getDifficulty()); + } + + if (parGuiButton.id == 109) { + this.mc.displayGuiScreen( + new GuiYesNo(this, + (new ChatComponentTranslation("difficulty.lock.title", new Object[0])) + .getFormattedText(), + (new ChatComponentTranslation("difficulty.lock.question", + new Object[] { new ChatComponentTranslation(this.mc.theWorld.getWorldInfo() + .getDifficulty().getDifficultyResourceKey(), new Object[0]) })) + .getFormattedText(), + 109)); + } + + if (parGuiButton.id == 110) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiCustomizeSkin(this)); + } + + if (parGuiButton.id == 8675309) { + if (EaglerDeferredPipeline.isSupported()) { + this.mc.displayGuiScreen(new GuiShaderConfig(this)); + } else { + this.mc.displayGuiScreen(new GuiShadersNotSupported(this, + I18n.format(EaglerDeferredPipeline.getReasonUnsupported()))); + } + } + + if (parGuiButton.id == 420) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiClientSettings(this, this.game_settings_1)); + } + + if (parGuiButton.id == 101) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiVideoSettings(this, this.game_settings_1)); + } + + if (parGuiButton.id == 100) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiControls(this, this.game_settings_1)); + } + + if (parGuiButton.id == 102) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiLanguage(this, this.game_settings_1, this.mc.getLanguageManager())); + } + + if (parGuiButton.id == 103) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new ScreenChatOptions(this, this.game_settings_1)); + } + + if (parGuiButton.id == 200) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(this.field_146441_g); + } + + if (parGuiButton.id == 105) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiScreenResourcePacks(this)); + } + + if (parGuiButton.id == 106) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(new GuiScreenOptionsSounds(this, this.game_settings_1)); + } + + if (parGuiButton.id == 107) { + if (ScreenRecordingController.isSupported()) { + GuiScreen screen = new GuiScreenRecordingSettings(this); + if (!GuiScreenRecordingNote.hasShown) { + screen = new GuiScreenRecordingNote(screen); + } + this.mc.displayGuiScreen(screen); + } + } + + if (parGuiButton.id == 104) { + EagRuntime.showDebugConsole(); + } + } + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + this.drawDefaultBackground(); + this.drawCenteredString(this.fontRendererObj, this.field_146442_a, this.width / 2, 15, 16777215); + + if (mc.theWorld == null && !EagRuntime.getConfiguration().isDemo()) { + GlStateManager.pushMatrix(); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + + String text = I18n.format("editProfile.importExport"); + + int w = mc.fontRendererObj.getStringWidth(text); + boolean hover = i > 1 && j > 1 && i < (w * 3 / 4) + 7 && j < 12; + if (hover) { + Mouse.showCursor(EnumCursorType.HAND); + } + + drawString(mc.fontRendererObj, EnumChatFormatting.UNDERLINE + text, 5, 5, hover ? 0xFFEEEE22 : 0xFFCCCCCC); + + GlStateManager.popMatrix(); + } + + if (mc.theWorld == null && EagRuntime.getConfiguration().isAllowBootMenu()) { + drawCenteredString(mc.fontRendererObj, I18n.format("options.pressDeleteText"), width / 2, height / 6 + 22, + 11184810); + } + + if (EagRuntime.getConfiguration().isEnableServerCookies() && mc.thePlayer == null) { + GlStateManager.pushMatrix(); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + + String text = I18n.format("revokeSessionToken.button"); + + int w = mc.fontRendererObj.getStringWidth(text); + boolean hover = i > width - 5 - (w + 5) * 3 / 4 && j > 1 && i < width - 2 && j < 12; + if (hover) { + Mouse.showCursor(EnumCursorType.HAND); + } + + drawString(mc.fontRendererObj, EnumChatFormatting.UNDERLINE + text, (width - 1) * 4 / 3 - w - 5, 5, + hover ? 0xFFEEEE22 : 0xFFCCCCCC); + + GlStateManager.popMatrix(); + } + + super.drawScreen(i, j, f); + } + + @Override + protected void keyTyped(char parChar1, int parInt1) { + super.keyTyped(parChar1, parInt1); + if (parInt1 == KeyboardConstants.KEY_DELETE || parInt1 == KeyboardConstants.KEY_BACK) { + if (mc.theWorld == null && EagRuntime.getConfiguration().isAllowBootMenu()) { + mc.displayGuiScreen(new GuiScreenEnterBootMenu(this)); + } + } + } + + protected void mouseClicked(int mx, int my, int button) { + super.mouseClicked(mx, my, button); + if (mc.theWorld == null && !EagRuntime.getConfiguration().isDemo()) { + int w = mc.fontRendererObj.getStringWidth(I18n.format("editProfile.importExport")); + if (mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12) { + mc.displayGuiScreen(new GuiScreenImportExportProfile(this)); + mc.getSoundHandler() + .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + } + } + if (EagRuntime.getConfiguration().isEnableServerCookies() && mc.thePlayer == null) { + int w = mc.fontRendererObj.getStringWidth(I18n.format("revokeSessionToken.button")); + if (mx > width - 5 - (w + 5) * 3 / 4 && my > 1 && mx < width - 2 && my < 12) { + ServerCookieDataStore.flush(); + mc.displayGuiScreen(ServerCookieDataStore.numRevokable() == 0 + ? new GuiScreenGenericErrorMessage("errorNoSessions.title", "errorNoSessions.desc", this) + : new GuiScreenRevokeSessionToken(this)); + mc.getSoundHandler() + .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + } + } + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiOptionsRowList.java b/src/game/java/net/minecraft/client/gui/GuiOptionsRowList.java similarity index 68% rename from src/main/java/net/minecraft/client/gui/GuiOptionsRowList.java rename to src/game/java/net/minecraft/client/gui/GuiOptionsRowList.java index 0e1ade4..8d9bdfd 100644 --- a/src/main/java/net/minecraft/client/gui/GuiOptionsRowList.java +++ b/src/game/java/net/minecraft/client/gui/GuiOptionsRowList.java @@ -1,154 +1,211 @@ -package net.minecraft.client.gui; - -import java.util.List; - -import com.google.common.collect.Lists; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.settings.GameSettings; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiOptionsRowList extends GuiListExtended { - private final List field_148184_k = Lists.newArrayList(); - - public GuiOptionsRowList(Minecraft mcIn, int parInt1, int parInt2, int parInt3, int parInt4, int parInt5, - GameSettings.Options... parArrayOfOptions) { - super(mcIn, parInt1, parInt2, parInt3, parInt4, parInt5); - this.field_148163_i = false; - - for (int i = 0; i < parArrayOfOptions.length; i += 2) { - GameSettings.Options gamesettings$options = parArrayOfOptions[i]; - GameSettings.Options gamesettings$options1 = i < parArrayOfOptions.length - 1 ? parArrayOfOptions[i + 1] - : null; - GuiButton guibutton = this.func_148182_a(mcIn, parInt1 / 2 - 155, 0, gamesettings$options); - GuiButton guibutton1 = this.func_148182_a(mcIn, parInt1 / 2 - 155 + 160, 0, gamesettings$options1); - this.field_148184_k.add(new GuiOptionsRowList.Row(guibutton, guibutton1)); - } - - } - - private GuiButton func_148182_a(Minecraft mcIn, int parInt1, int parInt2, GameSettings.Options parOptions) { - if (parOptions == null) { - return null; - } else { - int i = parOptions.returnEnumOrdinal(); - return (GuiButton) (parOptions.getEnumFloat() ? new GuiOptionSlider(i, parInt1, parInt2, parOptions) - : new GuiOptionButton(i, parInt1, parInt2, parOptions, - mcIn.gameSettings.getKeyBinding(parOptions))); - } - } - - /** - * + - * Gets the IGuiListEntry object for the given index - */ - public GuiOptionsRowList.Row getListEntry(int i) { - return (GuiOptionsRowList.Row) this.field_148184_k.get(i); - } - - protected int getSize() { - return this.field_148184_k.size(); - } - - /** - * + - * Gets the width of the list - */ - public int getListWidth() { - return 400; - } - - protected int getScrollBarX() { - return super.getScrollBarX() + 32; - } - - public static class Row implements GuiListExtended.IGuiListEntry { - private final Minecraft field_148325_a = Minecraft.getMinecraft(); - private final GuiButton field_148323_b; - private final GuiButton field_148324_c; - - public Row(GuiButton parGuiButton, GuiButton parGuiButton2) { - this.field_148323_b = parGuiButton; - this.field_148324_c = parGuiButton2; - } - - public void drawEntry(int var1, int var2, int i, int var4, int var5, int j, int k, boolean var8) { - if (this.field_148323_b != null) { - this.field_148323_b.yPosition = i; - this.field_148323_b.drawButton(this.field_148325_a, j, k); - } - - if (this.field_148324_c != null) { - this.field_148324_c.yPosition = i; - this.field_148324_c.drawButton(this.field_148325_a, j, k); - } - - } - - public boolean mousePressed(int var1, int i, int j, int var4, int var5, int var6) { - if (this.field_148323_b.mousePressed(this.field_148325_a, i, j)) { - if (this.field_148323_b instanceof GuiOptionButton) { - this.field_148325_a.gameSettings - .setOptionValue(((GuiOptionButton) this.field_148323_b).returnEnumOptions(), 1); - this.field_148323_b.displayString = this.field_148325_a.gameSettings - .getKeyBinding(GameSettings.Options.getEnumOptions(this.field_148323_b.id)); - } - - return true; - } else if (this.field_148324_c != null && this.field_148324_c.mousePressed(this.field_148325_a, i, j)) { - if (this.field_148324_c instanceof GuiOptionButton) { - this.field_148325_a.gameSettings - .setOptionValue(((GuiOptionButton) this.field_148324_c).returnEnumOptions(), 1); - this.field_148324_c.displayString = this.field_148325_a.gameSettings - .getKeyBinding(GameSettings.Options.getEnumOptions(this.field_148324_c.id)); - } - - return true; - } else { - return false; - } - } - - public void mouseReleased(int var1, int i, int j, int var4, int var5, int var6) { - if (this.field_148323_b != null) { - this.field_148323_b.mouseReleased(i, j); - } - - if (this.field_148324_c != null) { - this.field_148324_c.mouseReleased(i, j); - } - - } - - public void setSelected(int var1, int var2, int var3) { - } - } +package net.minecraft.client.gui; + +import java.util.List; + +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.minecraft.client.Minecraft; +import net.minecraft.client.settings.GameSettings; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiOptionsRowList extends GuiListExtended { + private final List field_148184_k = Lists.newArrayList(); + + public GuiOptionsRowList(Minecraft mcIn, int parInt1, int parInt2, int parInt3, int parInt4, int parInt5, + GameSettings.Options... parArrayOfOptions) { + super(mcIn, parInt1, parInt2, parInt3, parInt4, parInt5); + this.field_148163_i = false; + + for (int i = 0; i < parArrayOfOptions.length; i += 2) { + GameSettings.Options gamesettings$options = parArrayOfOptions[i]; + GameSettings.Options gamesettings$options1 = i < parArrayOfOptions.length - 1 ? parArrayOfOptions[i + 1] + : null; + GuiButton guibutton = this.func_148182_a(mcIn, parInt1 / 2 - 155, 0, gamesettings$options); + GuiButton guibutton1 = this.func_148182_a(mcIn, parInt1 / 2 - 155 + 160, 0, gamesettings$options1); + this.field_148184_k.add(new GuiOptionsRowList.Row(guibutton, guibutton1)); + } + + } + + private GuiButton func_148182_a(Minecraft mcIn, int parInt1, int parInt2, GameSettings.Options parOptions) { + if (parOptions == null) { + return null; + } else { + int i = parOptions.returnEnumOrdinal(); + return (GuiButton) (parOptions.getEnumFloat() ? new GuiOptionSlider(i, parInt1, parInt2, parOptions) + : new GuiOptionButton(i, parInt1, parInt2, parOptions, + mcIn.gameSettings.getKeyBinding(parOptions))); + } + } + + /** + * + + * Gets the IGuiListEntry object for the given index + */ + public GuiOptionsRowList.Row getListEntry(int i) { + return (GuiOptionsRowList.Row) this.field_148184_k.get(i); + } + + protected int getSize() { + return this.field_148184_k.size(); + } + + /** + * + + * Gets the width of the list + */ + public int getListWidth() { + return 400; + } + + protected int getScrollBarX() { + return super.getScrollBarX() + 32; + } + + public static class Row implements GuiListExtended.IGuiListEntry { + private final Minecraft field_148325_a = Minecraft.getMinecraft(); + private final GuiButton field_148323_b; + private final GuiButton field_148324_c; + + public Row(GuiButton parGuiButton, GuiButton parGuiButton2) { + this.field_148323_b = parGuiButton; + this.field_148324_c = parGuiButton2; + } + + public void drawEntry(int var1, int var2, int i, int var4, int var5, int j, int k, boolean var8) { + if (this.field_148323_b != null) { + this.field_148323_b.yPosition = i; + this.field_148323_b.drawButton(this.field_148325_a, j, k); + } + + if (this.field_148324_c != null) { + this.field_148324_c.yPosition = i; + this.field_148324_c.drawButton(this.field_148325_a, j, k); + } + + } + + public boolean mousePressed(int var1, int i, int j, int var4, int var5, int var6) { + if (var4 != 0 && var4 != 12345) + return false; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if ((!touchMode || (this.field_148323_b.isSliderTouchEvents() == (var4 == 12345))) + && this.field_148323_b.mousePressed(this.field_148325_a, i, j)) { + if (this.field_148323_b instanceof GuiOptionButton) { + this.field_148325_a.gameSettings + .setOptionValue(((GuiOptionButton) this.field_148323_b).returnEnumOptions(), 1); + this.field_148323_b.displayString = this.field_148325_a.gameSettings + .getKeyBinding(GameSettings.Options.getEnumOptions(this.field_148323_b.id)); + } + + return true; + } else if (this.field_148324_c != null + && (!touchMode || (this.field_148324_c.isSliderTouchEvents() == (var4 == 12345))) + && this.field_148324_c.mousePressed(this.field_148325_a, i, j)) { + if (this.field_148324_c instanceof GuiOptionButton) { + this.field_148325_a.gameSettings + .setOptionValue(((GuiOptionButton) this.field_148324_c).returnEnumOptions(), 1); + this.field_148324_c.displayString = this.field_148325_a.gameSettings + .getKeyBinding(GameSettings.Options.getEnumOptions(this.field_148324_c.id)); + } + + return true; + } else { + return false; + } + } + + public void mouseReleased(int var1, int i, int j, int var4, int var5, int var6) { + if (var4 != 0 && var4 != 12345) + return; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if (this.field_148323_b != null + && (!touchMode || (this.field_148323_b.isSliderTouchEvents() == (var4 == 12345)))) { + this.field_148323_b.mouseReleased(i, j); + } + + if (this.field_148324_c != null + && (!touchMode || (this.field_148324_c.isSliderTouchEvents() == (var4 == 12345)))) { + this.field_148324_c.mouseReleased(i, j); + } + + } + + public void setSelected(int var1, int var2, int var3) { + } + } + + public GuiOptionButton getButtonFor(GameSettings.Options enumOption) { + for (Row r : field_148184_k) { + if (r.field_148323_b != null) { + if (r.field_148323_b instanceof GuiOptionButton) { + GuiOptionButton btn = (GuiOptionButton) r.field_148323_b; + if (btn.returnEnumOptions() == enumOption) { + return btn; + } + } + } + if (r.field_148324_c != null) { + if (r.field_148324_c instanceof GuiOptionButton) { + GuiOptionButton btn = (GuiOptionButton) r.field_148324_c; + if (btn.returnEnumOptions() == enumOption) { + return btn; + } + } + } + } + return null; + } + + public GuiOptionSlider getSliderFor(GameSettings.Options enumOption) { + for (Row r : field_148184_k) { + if (r.field_148323_b != null) { + if (r.field_148323_b instanceof GuiOptionSlider) { + GuiOptionSlider btn = (GuiOptionSlider) r.field_148323_b; + if (btn.getEnumOptions() == enumOption) { + return btn; + } + } + } + if (r.field_148324_c != null) { + if (r.field_148324_c instanceof GuiOptionSlider) { + GuiOptionSlider btn = (GuiOptionSlider) r.field_148324_c; + if (btn.getEnumOptions() == enumOption) { + return btn; + } + } + } + } + return null; + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiOverlayDebug.java b/src/game/java/net/minecraft/client/gui/GuiOverlayDebug.java similarity index 95% rename from src/main/java/net/minecraft/client/gui/GuiOverlayDebug.java rename to src/game/java/net/minecraft/client/gui/GuiOverlayDebug.java index 557c8a0..409a611 100644 --- a/src/main/java/net/minecraft/client/gui/GuiOverlayDebug.java +++ b/src/game/java/net/minecraft/client/gui/GuiOverlayDebug.java @@ -9,6 +9,9 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; + import java.util.TimeZone; import com.google.common.base.Strings; @@ -86,7 +89,6 @@ public class GuiOverlayDebug extends Gui { playerOffset = 0; int ww = scaledResolutionIn.getScaledWidth(); int hh = scaledResolutionIn.getScaledHeight(); - this.mc.mcProfiler.startSection("debug"); if (this.mc.gameSettings.showDebugInfo) { GlStateManager.pushMatrix(); this.renderDebugInfoLeft(); @@ -133,8 +135,6 @@ public class GuiOverlayDebug extends Gui { GlStateManager.disableBlend(); } } - - this.mc.mcProfiler.endSection(); } private void drawFPS(int x, int y) { @@ -231,7 +231,6 @@ public class GuiOverlayDebug extends Gui { final double dticks = ticks - minutes * ticksPerMinute; final long seconds = (long) Math.floor(dticks / ticksPerSecond); - // TODO: why does desktop JRE not apply "GMT" correctly? final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.ENGLISH); cal.setLenient(true); @@ -241,10 +240,10 @@ public class GuiOverlayDebug extends Gui { cal.add(Calendar.MINUTE, (int) minutes); cal.add(Calendar.SECOND, (int) seconds + 1); + SimpleDateFormat fmt = this.mc.gameSettings.hud24h ? SDFTwentyFour : SDFTwelve; + fmt.setCalendar(cal); String timeString = EnumChatFormatting.WHITE + "Day " + ((totalTicks + 30000l) / 24000l) + " (" - + EnumChatFormatting.YELLOW - + (this.mc.gameSettings.hud24h ? SDFTwentyFour : SDFTwelve).format(cal.getTime()) - + EnumChatFormatting.WHITE + ")"; + + EnumChatFormatting.YELLOW + fmt.format(cal.getTime()) + EnumChatFormatting.WHITE + ")"; Entity e = mc.getRenderViewEntity(); BlockPos blockpos = new BlockPos(e.posX, MathHelper.clamp_double(e.getEntityBoundingBox().minY, 0.0D, 254.0D), @@ -290,23 +289,30 @@ public class GuiOverlayDebug extends Gui { if (tpsAge < 20000l) { int color = tpsAge > 2000l ? 0x777777 : 0xFFFFFF; List strs = SingleplayerServerController.getTPS(); + if (SingleplayerServerController.isRunningSingleThreadMode()) { + strs = Lists.newArrayList(strs); + strs.add(""); + strs.add(I18n.format("singleplayer.tpscounter.singleThreadMode")); + } int l; boolean first = true; for (int j = 0, m = strs.size(); j < m; ++j) { String str = strs.get(j); - l = (int) (this.fontRenderer.getStringWidth(str) * (!first ? 0.5f : 1.0f)); - GlStateManager.pushMatrix(); - GlStateManager.translate(parScaledResolution.getScaledWidth() - 2 - l, i + 2, 0.0f); - if (!first) { - GlStateManager.scale(0.5f, 0.5f, 0.5f); + if (!StringUtils.isAllEmpty(str)) { + l = (int) (this.fontRenderer.getStringWidth(str) * (!first ? 0.5f : 1.0f)); + GlStateManager.pushMatrix(); + GlStateManager.translate(parScaledResolution.getScaledWidth() - 2 - l, i + 2, 0.0f); + if (!first) { + GlStateManager.scale(0.5f, 0.5f, 0.5f); + } + this.fontRenderer.drawStringWithShadow(str, 0, 0, color); + GlStateManager.popMatrix(); + if (color == 0xFFFFFF) { + color = 14737632; + } } - this.fontRenderer.drawStringWithShadow(str, 0, 0, color); - GlStateManager.popMatrix(); i += (int) (this.fontRenderer.FONT_HEIGHT * (!first ? 0.5f : 1.0f)); first = false; - if (color == 0xFFFFFF) { - color = 14737632; - } } } } @@ -505,7 +511,7 @@ public class GuiOverlayDebug extends Gui { int i = frametimer.func_181749_a(); int j = frametimer.func_181750_b(); long[] along = frametimer.func_181746_c(); - ScaledResolution scaledresolution = new ScaledResolution(this.mc); + ScaledResolution scaledresolution = this.mc.scaledResolution; int k = i; int l = 0; drawRect(0, scaledresolution.getScaledHeight() - 60, 240, scaledresolution.getScaledHeight(), -1873784752); diff --git a/src/main/java/net/minecraft/client/gui/GuiPageButtonList.java b/src/game/java/net/minecraft/client/gui/GuiPageButtonList.java similarity index 90% rename from src/main/java/net/minecraft/client/gui/GuiPageButtonList.java rename to src/game/java/net/minecraft/client/gui/GuiPageButtonList.java index a1c2834..5626a8d 100644 --- a/src/main/java/net/minecraft/client/gui/GuiPageButtonList.java +++ b/src/game/java/net/minecraft/client/gui/GuiPageButtonList.java @@ -1,563 +1,598 @@ -package net.minecraft.client.gui; - -import java.util.List; - -import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Lists; - -import net.minecraft.client.Minecraft; -import net.minecraft.util.IntHashMap; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiPageButtonList extends GuiListExtended { - private final List field_178074_u = Lists.newArrayList(); - private final IntHashMap field_178073_v = new IntHashMap(); - private final List field_178072_w = Lists.newArrayList(); - private final GuiPageButtonList.GuiListEntry[][] field_178078_x; - private int field_178077_y; - private GuiPageButtonList.GuiResponder field_178076_z; - private Gui field_178075_A; - - public GuiPageButtonList(Minecraft mcIn, int widthIn, int heightIn, int topIn, int bottomIn, int slotHeightIn, - GuiPageButtonList.GuiResponder parGuiResponder, GuiPageButtonList.GuiListEntry[]... parArrayOfarray) { - super(mcIn, widthIn, heightIn, topIn, bottomIn, slotHeightIn); - this.field_178076_z = parGuiResponder; - this.field_178078_x = parArrayOfarray; - this.field_148163_i = false; - this.func_178069_s(); - this.func_178055_t(); - } - - private void func_178069_s() { - for (int n = 0; n < this.field_178078_x.length; ++n) { - GuiPageButtonList.GuiListEntry[] aguipagebuttonlist$guilistentry = this.field_178078_x[n]; - for (int i = 0; i < aguipagebuttonlist$guilistentry.length; i += 2) { - GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry = aguipagebuttonlist$guilistentry[i]; - GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry1 = i < aguipagebuttonlist$guilistentry.length - - 1 ? aguipagebuttonlist$guilistentry[i + 1] : null; - Gui gui = this.func_178058_a(guipagebuttonlist$guilistentry, 0, - guipagebuttonlist$guilistentry1 == null); - Gui gui1 = this.func_178058_a(guipagebuttonlist$guilistentry1, 160, - guipagebuttonlist$guilistentry == null); - GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = new GuiPageButtonList.GuiEntry(gui, gui1); - this.field_178074_u.add(guipagebuttonlist$guientry); - if (guipagebuttonlist$guilistentry != null && gui != null) { - this.field_178073_v.addKey(guipagebuttonlist$guilistentry.func_178935_b(), gui); - if (gui instanceof GuiTextField) { - this.field_178072_w.add((GuiTextField) gui); - } - } - - if (guipagebuttonlist$guilistentry1 != null && gui1 != null) { - this.field_178073_v.addKey(guipagebuttonlist$guilistentry1.func_178935_b(), gui1); - if (gui1 instanceof GuiTextField) { - this.field_178072_w.add((GuiTextField) gui1); - } - } - } - } - - } - - private void func_178055_t() { - this.field_178074_u.clear(); - - for (int i = 0; i < this.field_178078_x[this.field_178077_y].length; i += 2) { - GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry = this.field_178078_x[this.field_178077_y][i]; - GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry1 = i < this.field_178078_x[this.field_178077_y].length - - 1 ? this.field_178078_x[this.field_178077_y][i + 1] : null; - Gui gui = (Gui) this.field_178073_v.lookup(guipagebuttonlist$guilistentry.func_178935_b()); - Gui gui1 = guipagebuttonlist$guilistentry1 != null - ? (Gui) this.field_178073_v.lookup(guipagebuttonlist$guilistentry1.func_178935_b()) - : null; - GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = new GuiPageButtonList.GuiEntry(gui, gui1); - this.field_178074_u.add(guipagebuttonlist$guientry); - } - - } - - public void func_181156_c(int parInt1) { - if (parInt1 != this.field_178077_y) { - int i = this.field_178077_y; - this.field_178077_y = parInt1; - this.func_178055_t(); - this.func_178060_e(i, parInt1); - this.amountScrolled = 0.0F; - } - } - - public int func_178059_e() { - return this.field_178077_y; - } - - public int func_178057_f() { - return this.field_178078_x.length; - } - - public Gui func_178056_g() { - return this.field_178075_A; - } - - public void func_178071_h() { - if (this.field_178077_y > 0) { - this.func_181156_c(this.field_178077_y - 1); - } - - } - - public void func_178064_i() { - if (this.field_178077_y < this.field_178078_x.length - 1) { - this.func_181156_c(this.field_178077_y + 1); - } - - } - - public Gui func_178061_c(int parInt1) { - return (Gui) this.field_178073_v.lookup(parInt1); - } - - private void func_178060_e(int parInt1, int parInt2) { - - GuiListEntry[] etr = this.field_178078_x[parInt1]; - for (int i = 0; i < etr.length; ++i) { - if (etr[i] != null) { - this.func_178066_a((Gui) this.field_178073_v.lookup(etr[i].func_178935_b()), false); - } - } - - etr = this.field_178078_x[parInt2]; - for (int i = 0; i < etr.length; ++i) { - if (etr[i] != null) { - this.func_178066_a((Gui) this.field_178073_v.lookup(etr[i].func_178935_b()), true); - } - } - - } - - private void func_178066_a(Gui parGui, boolean parFlag) { - if (parGui instanceof GuiButton) { - ((GuiButton) parGui).visible = parFlag; - } else if (parGui instanceof GuiTextField) { - ((GuiTextField) parGui).setVisible(parFlag); - } else if (parGui instanceof GuiLabel) { - ((GuiLabel) parGui).visible = parFlag; - } - - } - - private Gui func_178058_a(GuiPageButtonList.GuiListEntry parGuiListEntry, int parInt1, boolean parFlag) { - return (Gui) (parGuiListEntry instanceof GuiPageButtonList.GuiSlideEntry - ? this.func_178067_a(this.width / 2 - 155 + parInt1, 0, - (GuiPageButtonList.GuiSlideEntry) parGuiListEntry) - : (parGuiListEntry instanceof GuiPageButtonList.GuiButtonEntry - ? this.func_178065_a(this.width / 2 - 155 + parInt1, 0, - (GuiPageButtonList.GuiButtonEntry) parGuiListEntry) - : (parGuiListEntry instanceof GuiPageButtonList.EditBoxEntry - ? this.func_178068_a(this.width / 2 - 155 + parInt1, 0, - (GuiPageButtonList.EditBoxEntry) parGuiListEntry) - : (parGuiListEntry instanceof GuiPageButtonList.GuiLabelEntry - ? this.func_178063_a(this.width / 2 - 155 + parInt1, 0, - (GuiPageButtonList.GuiLabelEntry) parGuiListEntry, parFlag) - : null)))); - } - - public void func_181155_a(boolean parFlag) { - for (int i = 0, l = this.field_178074_u.size(); i < l; ++i) { - GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = this.field_178074_u.get(i); - if (guipagebuttonlist$guientry.field_178029_b instanceof GuiButton) { - ((GuiButton) guipagebuttonlist$guientry.field_178029_b).enabled = parFlag; - } - - if (guipagebuttonlist$guientry.field_178030_c instanceof GuiButton) { - ((GuiButton) guipagebuttonlist$guientry.field_178030_c).enabled = parFlag; - } - } - - } - - public boolean mouseClicked(int i, int j, int k) { - boolean flag = super.mouseClicked(i, j, k); - int l = this.getSlotIndexFromScreenCoords(i, j); - if (l >= 0) { - GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = this.getListEntry(l); - if (this.field_178075_A != guipagebuttonlist$guientry.field_178028_d && this.field_178075_A != null - && this.field_178075_A instanceof GuiTextField) { - ((GuiTextField) this.field_178075_A).setFocused(false); - } - - this.field_178075_A = guipagebuttonlist$guientry.field_178028_d; - } - - return flag; - } - - private GuiSlider func_178067_a(int parInt1, int parInt2, GuiPageButtonList.GuiSlideEntry parGuiSlideEntry) { - GuiSlider guislider = new GuiSlider(this.field_178076_z, parGuiSlideEntry.func_178935_b(), parInt1, parInt2, - parGuiSlideEntry.func_178936_c(), parGuiSlideEntry.func_178943_e(), parGuiSlideEntry.func_178944_f(), - parGuiSlideEntry.func_178942_g(), parGuiSlideEntry.func_178945_a()); - guislider.visible = parGuiSlideEntry.func_178934_d(); - return guislider; - } - - private GuiListButton func_178065_a(int parInt1, int parInt2, GuiPageButtonList.GuiButtonEntry parGuiButtonEntry) { - GuiListButton guilistbutton = new GuiListButton(this.field_178076_z, parGuiButtonEntry.func_178935_b(), parInt1, - parInt2, parGuiButtonEntry.func_178936_c(), parGuiButtonEntry.func_178940_a()); - guilistbutton.visible = parGuiButtonEntry.func_178934_d(); - return guilistbutton; - } - - private GuiTextField func_178068_a(int parInt1, int parInt2, GuiPageButtonList.EditBoxEntry parEditBoxEntry) { - GuiTextField guitextfield = new GuiTextField(parEditBoxEntry.func_178935_b(), this.mc.fontRendererObj, parInt1, - parInt2, 150, 20); - guitextfield.setText(parEditBoxEntry.func_178936_c()); - guitextfield.func_175207_a(this.field_178076_z); - guitextfield.setVisible(parEditBoxEntry.func_178934_d()); - guitextfield.func_175205_a(parEditBoxEntry.func_178950_a()); - return guitextfield; - } - - private GuiLabel func_178063_a(int parInt1, int parInt2, GuiPageButtonList.GuiLabelEntry parGuiLabelEntry, - boolean parFlag) { - GuiLabel guilabel; - if (parFlag) { - guilabel = new GuiLabel(this.mc.fontRendererObj, parGuiLabelEntry.func_178935_b(), parInt1, parInt2, - this.width - parInt1 * 2, 20, -1); - } else { - guilabel = new GuiLabel(this.mc.fontRendererObj, parGuiLabelEntry.func_178935_b(), parInt1, parInt2, 150, - 20, -1); - } - - guilabel.visible = parGuiLabelEntry.func_178934_d(); - guilabel.func_175202_a(parGuiLabelEntry.func_178936_c()); - guilabel.setCentered(); - return guilabel; - } - - public void func_178062_a(char parChar1, int parInt1) { - if (this.field_178075_A instanceof GuiTextField) { - GuiTextField guitextfield = (GuiTextField) this.field_178075_A; - if (!GuiScreen.isKeyComboCtrlV(parInt1)) { - if (parInt1 == 15) { - guitextfield.setFocused(false); - int k = this.field_178072_w.indexOf(this.field_178075_A); - if (GuiScreen.isShiftKeyDown()) { - if (k == 0) { - k = this.field_178072_w.size() - 1; - } else { - --k; - } - } else if (k == this.field_178072_w.size() - 1) { - k = 0; - } else { - ++k; - } - - this.field_178075_A = (Gui) this.field_178072_w.get(k); - guitextfield = (GuiTextField) this.field_178075_A; - guitextfield.setFocused(true); - int l = guitextfield.yPosition + this.slotHeight; - int i1 = guitextfield.yPosition; - if (l > this.bottom) { - this.amountScrolled += (float) (l - this.bottom); - } else if (i1 < this.top) { - this.amountScrolled = (float) i1; - } - } else { - guitextfield.textboxKeyTyped(parChar1, parInt1); - } - - } else { - String s = GuiScreen.getClipboardString(); - String[] astring = s.split(";"); - int i = this.field_178072_w.indexOf(this.field_178075_A); - int j = i; - - for (int k = 0; k < astring.length; ++k) { - ((GuiTextField) this.field_178072_w.get(j)).setText(astring[k]); - if (j == this.field_178072_w.size() - 1) { - j = 0; - } else { - ++j; - } - - if (j == i) { - break; - } - } - - } - } - } - - /** - * + - * Gets the IGuiListEntry object for the given index - */ - public GuiPageButtonList.GuiEntry getListEntry(int i) { - return (GuiPageButtonList.GuiEntry) this.field_178074_u.get(i); - } - - public int getSize() { - return this.field_178074_u.size(); - } - - /** - * + - * Gets the width of the list - */ - public int getListWidth() { - return 400; - } - - protected int getScrollBarX() { - return super.getScrollBarX() + 32; - } - - public static class EditBoxEntry extends GuiPageButtonList.GuiListEntry { - private final Predicate field_178951_a; - - public EditBoxEntry(int parInt1, String parString1, boolean parFlag, Predicate parPredicate) { - super(parInt1, parString1, parFlag); - this.field_178951_a = (Predicate) Objects.firstNonNull(parPredicate, Predicates.alwaysTrue()); - } - - public Predicate func_178950_a() { - return this.field_178951_a; - } - } - - public static class GuiButtonEntry extends GuiPageButtonList.GuiListEntry { - private final boolean field_178941_a; - - public GuiButtonEntry(int parInt1, String parString1, boolean parFlag, boolean parFlag2) { - super(parInt1, parString1, parFlag); - this.field_178941_a = parFlag2; - } - - public boolean func_178940_a() { - return this.field_178941_a; - } - } - - public static class GuiEntry implements GuiListExtended.IGuiListEntry { - private final Minecraft field_178031_a = Minecraft.getMinecraft(); - private final Gui field_178029_b; - private final Gui field_178030_c; - private Gui field_178028_d; - - public GuiEntry(Gui parGui, Gui parGui2) { - this.field_178029_b = parGui; - this.field_178030_c = parGui2; - } - - public Gui func_178022_a() { - return this.field_178029_b; - } - - public Gui func_178021_b() { - return this.field_178030_c; - } - - public void drawEntry(int var1, int var2, int i, int var4, int var5, int j, int k, boolean var8) { - this.func_178017_a(this.field_178029_b, i, j, k, false); - this.func_178017_a(this.field_178030_c, i, j, k, false); - } - - private void func_178017_a(Gui parGui, int parInt1, int parInt2, int parInt3, boolean parFlag) { - if (parGui != null) { - if (parGui instanceof GuiButton) { - this.func_178024_a((GuiButton) parGui, parInt1, parInt2, parInt3, parFlag); - } else if (parGui instanceof GuiTextField) { - this.func_178027_a((GuiTextField) parGui, parInt1, parFlag); - } else if (parGui instanceof GuiLabel) { - this.func_178025_a((GuiLabel) parGui, parInt1, parInt2, parInt3, parFlag); - } - - } - } - - private void func_178024_a(GuiButton parGuiButton, int parInt1, int parInt2, int parInt3, boolean parFlag) { - parGuiButton.yPosition = parInt1; - if (!parFlag) { - parGuiButton.drawButton(this.field_178031_a, parInt2, parInt3); - } - - } - - private void func_178027_a(GuiTextField parGuiTextField, int parInt1, boolean parFlag) { - parGuiTextField.yPosition = parInt1; - if (!parFlag) { - parGuiTextField.drawTextBox(); - } - - } - - private void func_178025_a(GuiLabel parGuiLabel, int parInt1, int parInt2, int parInt3, boolean parFlag) { - parGuiLabel.field_146174_h = parInt1; - if (!parFlag) { - parGuiLabel.drawLabel(this.field_178031_a, parInt2, parInt3); - } - - } - - public void setSelected(int var1, int var2, int i) { - this.func_178017_a(this.field_178029_b, i, 0, 0, true); - this.func_178017_a(this.field_178030_c, i, 0, 0, true); - } - - public boolean mousePressed(int var1, int i, int j, int k, int var5, int var6) { - boolean flag = this.func_178026_a(this.field_178029_b, i, j, k); - boolean flag1 = this.func_178026_a(this.field_178030_c, i, j, k); - return flag || flag1; - } - - private boolean func_178026_a(Gui parGui, int parInt1, int parInt2, int parInt3) { - if (parGui == null) { - return false; - } else if (parGui instanceof GuiButton) { - return this.func_178023_a((GuiButton) parGui, parInt1, parInt2, parInt3); - } else { - if (parGui instanceof GuiTextField) { - this.func_178018_a((GuiTextField) parGui, parInt1, parInt2, parInt3); - } - - return false; - } - } - - private boolean func_178023_a(GuiButton parGuiButton, int parInt1, int parInt2, int parInt3) { - boolean flag = parGuiButton.mousePressed(this.field_178031_a, parInt1, parInt2); - if (flag) { - this.field_178028_d = parGuiButton; - } - - return flag; - } - - private void func_178018_a(GuiTextField parGuiTextField, int parInt1, int parInt2, int parInt3) { - parGuiTextField.mouseClicked(parInt1, parInt2, parInt3); - if (parGuiTextField.isFocused()) { - this.field_178028_d = parGuiTextField; - } - - } - - public void mouseReleased(int var1, int i, int j, int k, int var5, int var6) { - this.func_178016_b(this.field_178029_b, i, j, k); - this.func_178016_b(this.field_178030_c, i, j, k); - } - - private void func_178016_b(Gui parGui, int parInt1, int parInt2, int parInt3) { - if (parGui != null) { - if (parGui instanceof GuiButton) { - this.func_178019_b((GuiButton) parGui, parInt1, parInt2, parInt3); - } - - } - } - - private void func_178019_b(GuiButton parGuiButton, int parInt1, int parInt2, int parInt3) { - parGuiButton.mouseReleased(parInt1, parInt2); - } - } - - public static class GuiLabelEntry extends GuiPageButtonList.GuiListEntry { - public GuiLabelEntry(int parInt1, String parString1, boolean parFlag) { - super(parInt1, parString1, parFlag); - } - } - - public static class GuiListEntry { - private final int field_178939_a; - private final String field_178937_b; - private final boolean field_178938_c; - - public GuiListEntry(int parInt1, String parString1, boolean parFlag) { - this.field_178939_a = parInt1; - this.field_178937_b = parString1; - this.field_178938_c = parFlag; - } - - public int func_178935_b() { - return this.field_178939_a; - } - - public String func_178936_c() { - return this.field_178937_b; - } - - public boolean func_178934_d() { - return this.field_178938_c; - } - } - - public interface GuiResponder { - void func_175321_a(int var1, boolean var2); - - void onTick(int var1, float var2); - - void func_175319_a(int var1, String var2); - } - - public static class GuiSlideEntry extends GuiPageButtonList.GuiListEntry { - private final GuiSlider.FormatHelper field_178949_a; - private final float field_178947_b; - private final float field_178948_c; - private final float field_178946_d; - - public GuiSlideEntry(int parInt1, String parString1, boolean parFlag, GuiSlider.FormatHelper parFormatHelper, - float parFloat1, float parFloat2, float parFloat3) { - super(parInt1, parString1, parFlag); - this.field_178949_a = parFormatHelper; - this.field_178947_b = parFloat1; - this.field_178948_c = parFloat2; - this.field_178946_d = parFloat3; - } - - public GuiSlider.FormatHelper func_178945_a() { - return this.field_178949_a; - } - - public float func_178943_e() { - return this.field_178947_b; - } - - public float func_178944_f() { - return this.field_178948_c; - } - - public float func_178942_g() { - return this.field_178946_d; - } - } +package net.minecraft.client.gui; + +import java.util.List; + +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.minecraft.client.Minecraft; +import net.minecraft.util.IntHashMap; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiPageButtonList extends GuiListExtended { + private final List field_178074_u = Lists.newArrayList(); + private final IntHashMap field_178073_v = new IntHashMap(); + private final List field_178072_w = Lists.newArrayList(); + private final GuiPageButtonList.GuiListEntry[][] field_178078_x; + private int field_178077_y; + private GuiPageButtonList.GuiResponder field_178076_z; + private Gui field_178075_A; + + public GuiPageButtonList(Minecraft mcIn, int widthIn, int heightIn, int topIn, int bottomIn, int slotHeightIn, + GuiPageButtonList.GuiResponder parGuiResponder, GuiPageButtonList.GuiListEntry[]... parArrayOfarray) { + super(mcIn, widthIn, heightIn, topIn, bottomIn, slotHeightIn); + this.field_178076_z = parGuiResponder; + this.field_178078_x = parArrayOfarray; + this.field_148163_i = false; + this.func_178069_s(); + this.func_178055_t(); + } + + private void func_178069_s() { + for (int n = 0; n < this.field_178078_x.length; ++n) { + GuiPageButtonList.GuiListEntry[] aguipagebuttonlist$guilistentry = this.field_178078_x[n]; + for (int i = 0; i < aguipagebuttonlist$guilistentry.length; i += 2) { + GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry = aguipagebuttonlist$guilistentry[i]; + GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry1 = i < aguipagebuttonlist$guilistentry.length + - 1 ? aguipagebuttonlist$guilistentry[i + 1] : null; + Gui gui = this.func_178058_a(guipagebuttonlist$guilistentry, 0, + guipagebuttonlist$guilistentry1 == null); + Gui gui1 = this.func_178058_a(guipagebuttonlist$guilistentry1, 160, + guipagebuttonlist$guilistentry == null); + GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = new GuiPageButtonList.GuiEntry(gui, gui1); + this.field_178074_u.add(guipagebuttonlist$guientry); + if (guipagebuttonlist$guilistentry != null && gui != null) { + this.field_178073_v.addKey(guipagebuttonlist$guilistentry.func_178935_b(), gui); + if (gui instanceof GuiTextField) { + this.field_178072_w.add((GuiTextField) gui); + } + } + + if (guipagebuttonlist$guilistentry1 != null && gui1 != null) { + this.field_178073_v.addKey(guipagebuttonlist$guilistentry1.func_178935_b(), gui1); + if (gui1 instanceof GuiTextField) { + this.field_178072_w.add((GuiTextField) gui1); + } + } + } + } + + } + + private void func_178055_t() { + this.field_178074_u.clear(); + + for (int i = 0; i < this.field_178078_x[this.field_178077_y].length; i += 2) { + GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry = this.field_178078_x[this.field_178077_y][i]; + GuiPageButtonList.GuiListEntry guipagebuttonlist$guilistentry1 = i < this.field_178078_x[this.field_178077_y].length + - 1 ? this.field_178078_x[this.field_178077_y][i + 1] : null; + Gui gui = (Gui) this.field_178073_v.lookup(guipagebuttonlist$guilistentry.func_178935_b()); + Gui gui1 = guipagebuttonlist$guilistentry1 != null + ? (Gui) this.field_178073_v.lookup(guipagebuttonlist$guilistentry1.func_178935_b()) + : null; + GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = new GuiPageButtonList.GuiEntry(gui, gui1); + this.field_178074_u.add(guipagebuttonlist$guientry); + } + + } + + public void func_181156_c(int parInt1) { + if (parInt1 != this.field_178077_y) { + int i = this.field_178077_y; + this.field_178077_y = parInt1; + this.func_178055_t(); + this.func_178060_e(i, parInt1); + this.amountScrolled = 0.0F; + } + } + + public int func_178059_e() { + return this.field_178077_y; + } + + public int func_178057_f() { + return this.field_178078_x.length; + } + + public Gui func_178056_g() { + return this.field_178075_A; + } + + public void func_178071_h() { + if (this.field_178077_y > 0) { + this.func_181156_c(this.field_178077_y - 1); + } + + } + + public void func_178064_i() { + if (this.field_178077_y < this.field_178078_x.length - 1) { + this.func_181156_c(this.field_178077_y + 1); + } + + } + + public Gui func_178061_c(int parInt1) { + return (Gui) this.field_178073_v.lookup(parInt1); + } + + private void func_178060_e(int parInt1, int parInt2) { + + GuiListEntry[] etr = this.field_178078_x[parInt1]; + for (int i = 0; i < etr.length; ++i) { + if (etr[i] != null) { + this.func_178066_a((Gui) this.field_178073_v.lookup(etr[i].func_178935_b()), false); + } + } + + etr = this.field_178078_x[parInt2]; + for (int i = 0; i < etr.length; ++i) { + if (etr[i] != null) { + this.func_178066_a((Gui) this.field_178073_v.lookup(etr[i].func_178935_b()), true); + } + } + + } + + private void func_178066_a(Gui parGui, boolean parFlag) { + if (parGui instanceof GuiButton) { + ((GuiButton) parGui).visible = parFlag; + } else if (parGui instanceof GuiTextField) { + ((GuiTextField) parGui).setVisible(parFlag); + } else if (parGui instanceof GuiLabel) { + ((GuiLabel) parGui).visible = parFlag; + } + + } + + private Gui func_178058_a(GuiPageButtonList.GuiListEntry parGuiListEntry, int parInt1, boolean parFlag) { + return (Gui) (parGuiListEntry instanceof GuiPageButtonList.GuiSlideEntry + ? this.func_178067_a(this.width / 2 - 155 + parInt1, 0, + (GuiPageButtonList.GuiSlideEntry) parGuiListEntry) + : (parGuiListEntry instanceof GuiPageButtonList.GuiButtonEntry + ? this.func_178065_a(this.width / 2 - 155 + parInt1, 0, + (GuiPageButtonList.GuiButtonEntry) parGuiListEntry) + : (parGuiListEntry instanceof GuiPageButtonList.EditBoxEntry + ? this.func_178068_a(this.width / 2 - 155 + parInt1, 0, + (GuiPageButtonList.EditBoxEntry) parGuiListEntry) + : (parGuiListEntry instanceof GuiPageButtonList.GuiLabelEntry + ? this.func_178063_a(this.width / 2 - 155 + parInt1, 0, + (GuiPageButtonList.GuiLabelEntry) parGuiListEntry, parFlag) + : null)))); + } + + public void func_181155_a(boolean parFlag) { + for (int i = 0, l = this.field_178074_u.size(); i < l; ++i) { + GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = this.field_178074_u.get(i); + if (guipagebuttonlist$guientry.field_178029_b instanceof GuiButton) { + ((GuiButton) guipagebuttonlist$guientry.field_178029_b).enabled = parFlag; + } + + if (guipagebuttonlist$guientry.field_178030_c instanceof GuiButton) { + ((GuiButton) guipagebuttonlist$guientry.field_178030_c).enabled = parFlag; + } + } + + } + + public boolean mouseClicked(int i, int j, int k) { + boolean flag = super.mouseClicked(i, j, k); + int l = this.getSlotIndexFromScreenCoords(i, j); + if (l >= 0) { + GuiPageButtonList.GuiEntry guipagebuttonlist$guientry = this.getListEntry(l); + if (this.field_178075_A != guipagebuttonlist$guientry.field_178028_d && this.field_178075_A != null + && this.field_178075_A instanceof GuiTextField) { + ((GuiTextField) this.field_178075_A).setFocused(false); + } + + this.field_178075_A = guipagebuttonlist$guientry.field_178028_d; + } + + return flag; + } + + private GuiSlider func_178067_a(int parInt1, int parInt2, GuiPageButtonList.GuiSlideEntry parGuiSlideEntry) { + GuiSlider guislider = new GuiSlider(this.field_178076_z, parGuiSlideEntry.func_178935_b(), parInt1, parInt2, + parGuiSlideEntry.func_178936_c(), parGuiSlideEntry.func_178943_e(), parGuiSlideEntry.func_178944_f(), + parGuiSlideEntry.func_178942_g(), parGuiSlideEntry.func_178945_a()); + guislider.visible = parGuiSlideEntry.func_178934_d(); + return guislider; + } + + private GuiListButton func_178065_a(int parInt1, int parInt2, GuiPageButtonList.GuiButtonEntry parGuiButtonEntry) { + GuiListButton guilistbutton = new GuiListButton(this.field_178076_z, parGuiButtonEntry.func_178935_b(), parInt1, + parInt2, parGuiButtonEntry.func_178936_c(), parGuiButtonEntry.func_178940_a()); + guilistbutton.visible = parGuiButtonEntry.func_178934_d(); + return guilistbutton; + } + + private GuiTextField func_178068_a(int parInt1, int parInt2, GuiPageButtonList.EditBoxEntry parEditBoxEntry) { + GuiTextField guitextfield = new GuiTextField(parEditBoxEntry.func_178935_b(), this.mc.fontRendererObj, parInt1, + parInt2, 150, 20); + guitextfield.setText(parEditBoxEntry.func_178936_c()); + guitextfield.func_175207_a(this.field_178076_z); + guitextfield.setVisible(parEditBoxEntry.func_178934_d()); + guitextfield.func_175205_a(parEditBoxEntry.func_178950_a()); + return guitextfield; + } + + private GuiLabel func_178063_a(int parInt1, int parInt2, GuiPageButtonList.GuiLabelEntry parGuiLabelEntry, + boolean parFlag) { + GuiLabel guilabel; + if (parFlag) { + guilabel = new GuiLabel(this.mc.fontRendererObj, parGuiLabelEntry.func_178935_b(), parInt1, parInt2, + this.width - parInt1 * 2, 20, -1); + } else { + guilabel = new GuiLabel(this.mc.fontRendererObj, parGuiLabelEntry.func_178935_b(), parInt1, parInt2, 150, + 20, -1); + } + + guilabel.visible = parGuiLabelEntry.func_178934_d(); + guilabel.func_175202_a(parGuiLabelEntry.func_178936_c()); + guilabel.setCentered(); + return guilabel; + } + + public void func_178062_a(char parChar1, int parInt1) { + if (this.field_178075_A instanceof GuiTextField) { + GuiTextField guitextfield = (GuiTextField) this.field_178075_A; + if (!GuiScreen.isKeyComboCtrlV(parInt1)) { + if (parInt1 == 15) { + guitextfield.setFocused(false); + int k = this.field_178072_w.indexOf(this.field_178075_A); + if (GuiScreen.isShiftKeyDown()) { + if (k == 0) { + k = this.field_178072_w.size() - 1; + } else { + --k; + } + } else if (k == this.field_178072_w.size() - 1) { + k = 0; + } else { + ++k; + } + + this.field_178075_A = (Gui) this.field_178072_w.get(k); + guitextfield = (GuiTextField) this.field_178075_A; + guitextfield.setFocused(true); + int l = guitextfield.yPosition + this.slotHeight; + int i1 = guitextfield.yPosition; + if (l > this.bottom) { + this.amountScrolled += (float) (l - this.bottom); + } else if (i1 < this.top) { + this.amountScrolled = (float) i1; + } + } else { + guitextfield.textboxKeyTyped(parChar1, parInt1); + } + + } else { + String s = GuiScreen.getClipboardString(); + String[] astring = s.split(";"); + int i = this.field_178072_w.indexOf(this.field_178075_A); + int j = i; + + for (int k = 0; k < astring.length; ++k) { + ((GuiTextField) this.field_178072_w.get(j)).setText(astring[k]); + if (j == this.field_178072_w.size() - 1) { + j = 0; + } else { + ++j; + } + + if (j == i) { + break; + } + } + + } + } + } + + /** + * + + * Gets the IGuiListEntry object for the given index + */ + public GuiPageButtonList.GuiEntry getListEntry(int i) { + return (GuiPageButtonList.GuiEntry) this.field_178074_u.get(i); + } + + public int getSize() { + return this.field_178074_u.size(); + } + + /** + * + + * Gets the width of the list + */ + public int getListWidth() { + return 400; + } + + protected int getScrollBarX() { + return super.getScrollBarX() + 32; + } + + public boolean isTextFieldFocused() { + for (GuiTextField txt : field_178072_w) { + if (txt.isFocused()) { + return true; + } + } + return false; + } + + public void fireInputEvent(EnumInputEvent event, String param) { + for (GuiTextField txt : field_178072_w) { + txt.fireInputEvent(event, param); + } + } + + public static class EditBoxEntry extends GuiPageButtonList.GuiListEntry { + private final Predicate field_178951_a; + + public EditBoxEntry(int parInt1, String parString1, boolean parFlag, Predicate parPredicate) { + super(parInt1, parString1, parFlag); + this.field_178951_a = (Predicate) Objects.firstNonNull(parPredicate, Predicates.alwaysTrue()); + } + + public Predicate func_178950_a() { + return this.field_178951_a; + } + } + + public static class GuiButtonEntry extends GuiPageButtonList.GuiListEntry { + private final boolean field_178941_a; + + public GuiButtonEntry(int parInt1, String parString1, boolean parFlag, boolean parFlag2) { + super(parInt1, parString1, parFlag); + this.field_178941_a = parFlag2; + } + + public boolean func_178940_a() { + return this.field_178941_a; + } + } + + public static class GuiEntry implements GuiListExtended.IGuiListEntry { + private final Minecraft field_178031_a = Minecraft.getMinecraft(); + private final Gui field_178029_b; + private final Gui field_178030_c; + private Gui field_178028_d; + + public GuiEntry(Gui parGui, Gui parGui2) { + this.field_178029_b = parGui; + this.field_178030_c = parGui2; + } + + public Gui func_178022_a() { + return this.field_178029_b; + } + + public Gui func_178021_b() { + return this.field_178030_c; + } + + public void drawEntry(int var1, int var2, int i, int var4, int var5, int j, int k, boolean var8) { + this.func_178017_a(this.field_178029_b, i, j, k, false); + this.func_178017_a(this.field_178030_c, i, j, k, false); + } + + private void func_178017_a(Gui parGui, int parInt1, int parInt2, int parInt3, boolean parFlag) { + if (parGui != null) { + if (parGui instanceof GuiButton) { + this.func_178024_a((GuiButton) parGui, parInt1, parInt2, parInt3, parFlag); + } else if (parGui instanceof GuiTextField) { + this.func_178027_a((GuiTextField) parGui, parInt1, parFlag); + } else if (parGui instanceof GuiLabel) { + this.func_178025_a((GuiLabel) parGui, parInt1, parInt2, parInt3, parFlag); + } + + } + } + + private void func_178024_a(GuiButton parGuiButton, int parInt1, int parInt2, int parInt3, boolean parFlag) { + parGuiButton.yPosition = parInt1; + if (!parFlag) { + parGuiButton.drawButton(this.field_178031_a, parInt2, parInt3); + } + + } + + private void func_178027_a(GuiTextField parGuiTextField, int parInt1, boolean parFlag) { + parGuiTextField.yPosition = parInt1; + if (!parFlag) { + parGuiTextField.drawTextBox(); + } + + } + + private void func_178025_a(GuiLabel parGuiLabel, int parInt1, int parInt2, int parInt3, boolean parFlag) { + parGuiLabel.field_146174_h = parInt1; + if (!parFlag) { + parGuiLabel.drawLabel(this.field_178031_a, parInt2, parInt3); + } + + } + + public void setSelected(int var1, int var2, int i) { + this.func_178017_a(this.field_178029_b, i, 0, 0, true); + this.func_178017_a(this.field_178030_c, i, 0, 0, true); + } + + public boolean mousePressed(int var1, int i, int j, int k, int var5, int var6) { + if (k != 0 && k != 12345) + return false; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + boolean flag = this.field_178029_b != null && (!touchMode || stupidCheck(this.field_178029_b, k)) + && this.func_178026_a(this.field_178029_b, i, j, k); + boolean flag1 = this.field_178030_c != null && (!touchMode || stupidCheck(this.field_178030_c, k)) + && this.func_178026_a(this.field_178030_c, i, j, k); + return flag || flag1; + } + + private static boolean stupidCheck(Gui gui, int k) { + if (gui instanceof GuiButton) { + return ((GuiButton) gui).isSliderTouchEvents() == (k == 12345); + } else { + return k != 12345; + } + } + + private boolean func_178026_a(Gui parGui, int parInt1, int parInt2, int parInt3) { + if (parGui == null) { + return false; + } else if (parGui instanceof GuiButton) { + return this.func_178023_a((GuiButton) parGui, parInt1, parInt2, parInt3); + } else { + if (parGui instanceof GuiTextField) { + this.func_178018_a((GuiTextField) parGui, parInt1, parInt2, parInt3); + } + + return false; + } + } + + private boolean func_178023_a(GuiButton parGuiButton, int parInt1, int parInt2, int parInt3) { + boolean flag = parGuiButton.mousePressed(this.field_178031_a, parInt1, parInt2); + if (flag) { + this.field_178028_d = parGuiButton; + } + + return flag; + } + + private void func_178018_a(GuiTextField parGuiTextField, int parInt1, int parInt2, int parInt3) { + parGuiTextField.mouseClicked(parInt1, parInt2, parInt3); + if (parGuiTextField.isFocused()) { + this.field_178028_d = parGuiTextField; + } + + } + + public void mouseReleased(int var1, int i, int j, int k, int var5, int var6) { + if (k != 0 && k != 12345) + return; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if (!touchMode || stupidCheck(field_178029_b, k)) + this.func_178016_b(this.field_178029_b, i, j, k); + if (!touchMode || stupidCheck(field_178030_c, k)) + this.func_178016_b(this.field_178030_c, i, j, k); + } + + private void func_178016_b(Gui parGui, int parInt1, int parInt2, int parInt3) { + if (parGui != null) { + if (parGui instanceof GuiButton) { + this.func_178019_b((GuiButton) parGui, parInt1, parInt2, parInt3); + } + + } + } + + private void func_178019_b(GuiButton parGuiButton, int parInt1, int parInt2, int parInt3) { + parGuiButton.mouseReleased(parInt1, parInt2); + } + } + + public static class GuiLabelEntry extends GuiPageButtonList.GuiListEntry { + public GuiLabelEntry(int parInt1, String parString1, boolean parFlag) { + super(parInt1, parString1, parFlag); + } + } + + public static class GuiListEntry { + private final int field_178939_a; + private final String field_178937_b; + private final boolean field_178938_c; + + public GuiListEntry(int parInt1, String parString1, boolean parFlag) { + this.field_178939_a = parInt1; + this.field_178937_b = parString1; + this.field_178938_c = parFlag; + } + + public int func_178935_b() { + return this.field_178939_a; + } + + public String func_178936_c() { + return this.field_178937_b; + } + + public boolean func_178934_d() { + return this.field_178938_c; + } + } + + public interface GuiResponder { + void func_175321_a(int var1, boolean var2); + + void onTick(int var1, float var2); + + void func_175319_a(int var1, String var2); + } + + public static class GuiSlideEntry extends GuiPageButtonList.GuiListEntry { + private final GuiSlider.FormatHelper field_178949_a; + private final float field_178947_b; + private final float field_178948_c; + private final float field_178946_d; + + public GuiSlideEntry(int parInt1, String parString1, boolean parFlag, GuiSlider.FormatHelper parFormatHelper, + float parFloat1, float parFloat2, float parFloat3) { + super(parInt1, parString1, parFlag); + this.field_178949_a = parFormatHelper; + this.field_178947_b = parFloat1; + this.field_178948_c = parFloat2; + this.field_178946_d = parFloat3; + } + + public GuiSlider.FormatHelper func_178945_a() { + return this.field_178949_a; + } + + public float func_178943_e() { + return this.field_178947_b; + } + + public float func_178944_f() { + return this.field_178948_c; + } + + public float func_178942_g() { + return this.field_178946_d; + } + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java b/src/game/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java rename to src/game/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java index c5b3d19..492adf6 100644 --- a/src/main/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java +++ b/src/game/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java @@ -74,9 +74,10 @@ public class GuiPlayerTabOverlay extends Gui { * supplied */ public String getPlayerName(NetworkPlayerInfo networkPlayerInfoIn) { - return networkPlayerInfoIn.getDisplayName() != null ? networkPlayerInfoIn.getDisplayName().getFormattedText() + IChatComponent dname = networkPlayerInfoIn.getDisplayNameProfanityFilter(); + return dname != null ? dname.getFormattedText() : ScorePlayerTeam.formatPlayerName(networkPlayerInfoIn.getPlayerTeam(), - networkPlayerInfoIn.getGameProfile().getName()); + networkPlayerInfoIn.getGameProfileNameProfanityFilter()); } /** diff --git a/src/main/java/net/minecraft/client/gui/GuiRenameWorld.java b/src/game/java/net/minecraft/client/gui/GuiRenameWorld.java similarity index 95% rename from src/main/java/net/minecraft/client/gui/GuiRenameWorld.java rename to src/game/java/net/minecraft/client/gui/GuiRenameWorld.java index e94d6c1..cdeae44 100644 --- a/src/main/java/net/minecraft/client/gui/GuiRenameWorld.java +++ b/src/game/java/net/minecraft/client/gui/GuiRenameWorld.java @@ -1,6 +1,7 @@ package net.minecraft.client.gui; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenIntegratedServerBusy; import net.minecraft.client.resources.I18n; @@ -162,4 +163,15 @@ public class GuiRenameWorld extends GuiScreen { this.field_146583_f.drawTextBox(); super.drawScreen(i, j, f); } + + @Override + public boolean showCopyPasteButtons() { + return field_146583_f.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + field_146583_f.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiRepair.java b/src/game/java/net/minecraft/client/gui/GuiRepair.java similarity index 96% rename from src/main/java/net/minecraft/client/gui/GuiRepair.java rename to src/game/java/net/minecraft/client/gui/GuiRepair.java index ab5295b..9f6e9f1 100644 --- a/src/main/java/net/minecraft/client/gui/GuiRepair.java +++ b/src/game/java/net/minecraft/client/gui/GuiRepair.java @@ -4,6 +4,7 @@ import java.util.List; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiContainer; @@ -247,4 +248,15 @@ public class GuiRepair extends GuiContainer implements ICrafting { public boolean blockPTTKey() { return nameField.isFocused(); } + + @Override + public boolean showCopyPasteButtons() { + return nameField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + nameField.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiResourcePackAvailable.java b/src/game/java/net/minecraft/client/gui/GuiResourcePackAvailable.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiResourcePackAvailable.java rename to src/game/java/net/minecraft/client/gui/GuiResourcePackAvailable.java diff --git a/src/main/java/net/minecraft/client/gui/GuiResourcePackList.java b/src/game/java/net/minecraft/client/gui/GuiResourcePackList.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiResourcePackList.java rename to src/game/java/net/minecraft/client/gui/GuiResourcePackList.java diff --git a/src/main/java/net/minecraft/client/gui/GuiResourcePackSelected.java b/src/game/java/net/minecraft/client/gui/GuiResourcePackSelected.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiResourcePackSelected.java rename to src/game/java/net/minecraft/client/gui/GuiResourcePackSelected.java diff --git a/src/main/java/net/minecraft/client/gui/GuiScreen.java b/src/game/java/net/minecraft/client/gui/GuiScreen.java similarity index 68% rename from src/main/java/net/minecraft/client/gui/GuiScreen.java rename to src/game/java/net/minecraft/client/gui/GuiScreen.java index 860d858..d14df85 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreen.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreen.java @@ -1,718 +1,957 @@ -package net.minecraft.client.gui; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import org.apache.commons.lang3.StringUtils; - -import com.google.common.base.Splitter; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; -import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.lax1dude.eaglercraft.v1_8.Mouse; -import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.entity.RenderItem; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.resources.I18n; -import net.minecraft.entity.EntityList; -import net.minecraft.event.ClickEvent; -import net.minecraft.event.HoverEvent; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.JsonToNBT; -import net.minecraft.nbt.NBTException; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.stats.Achievement; -import net.minecraft.stats.StatBase; -import net.minecraft.stats.StatList; -import net.minecraft.util.ChatComponentTranslation; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.IChatComponent; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public abstract class GuiScreen extends Gui implements GuiYesNoCallback { - private static final Logger LOGGER = LogManager.getLogger(); - private static final Set PROTOCOLS = Sets.newHashSet(new String[] { "http", "https" }); - private static final Splitter NEWLINE_SPLITTER = Splitter.on('\n'); - protected Minecraft mc; - protected RenderItem itemRender; - public int width; - public int height; - /** - * + - * A list of all the buttons in this container. - */ - protected List buttonList = Lists.newArrayList(); - /** - * + - * A list of all the labels in this container. - */ - protected List labelList = Lists.newArrayList(); - public boolean allowUserInput; - protected FontRenderer fontRendererObj; - private GuiButton selectedButton; - private int eventButton; - private long lastMouseEvent; - private int touchValue; - private String clickedLinkURI; - protected long showingCloseKey = 0; - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float var3) { - for (int k = 0, l = this.buttonList.size(); k < l; ++k) { - ((GuiButton) this.buttonList.get(k)).drawButton(this.mc, i, j); - } - - for (int l = 0, m = this.labelList.size(); l < m; ++l) { - ((GuiLabel) this.labelList.get(l)).drawLabel(this.mc, i, j); - } - - long millis = System.currentTimeMillis(); - long closeKeyTimeout = millis - showingCloseKey; - if (closeKeyTimeout < 3000l) { - int alpha1 = 0xC0000000; - int alpha2 = 0xFF000000; - if (closeKeyTimeout > 2500l) { - float f = (float) (3000l - closeKeyTimeout) * 0.002f; - if (f < 0.03f) - f = 0.03f; - alpha1 = (int) (f * 192.0f) << 24; - alpha2 = (int) (f * 255.0f) << 24; - } - String str; - int k = getCloseKey(); - if (k == KeyboardConstants.KEY_GRAVE) { - str = I18n.format("gui.exitKeyRetarded"); - } else { - str = I18n.format("gui.exitKey", Keyboard.getKeyName(k)); - } - int w = fontRendererObj.getStringWidth(str); - int x = (width - w - 4) / 2; - int y = 10; - drawRect(x, y, x + w + 4, y + 12, alpha1); - if (closeKeyTimeout > 2500l) - GlStateManager.enableBlend(); - fontRendererObj.drawStringWithShadow(str, x + 2, y + 2, 0xFFAAAA | alpha2); - if (closeKeyTimeout > 2500l) - GlStateManager.disableBlend(); - } - - } - - protected int getCloseKey() { - if (this instanceof GuiContainer) { - return this.mc.gameSettings.keyBindInventory.getKeyCode(); - } else { - return this.mc.gameSettings.keyBindClose.getKeyCode(); - } - } - - /** - * + - * Fired when a key is typed (except F11 which toggles full - * screen). This is the equivalent of - * KeyListener.keyTyped(KeyEvent e). Args : character (character - * on the key), keyCode (lwjgl Keyboard key code) - */ - protected void keyTyped(char parChar1, int parInt1) { - if (((this.mc.theWorld == null || this.mc.thePlayer.getHealth() <= 0.0F) && parInt1 == 1) - || parInt1 == this.mc.gameSettings.keyBindClose.getKeyCode() - || (parInt1 == 1 && (this.mc.gameSettings.keyBindClose.getKeyCode() == 0 || this.mc.areKeysLocked()))) { - this.mc.displayGuiScreen((GuiScreen) null); - if (this.mc.currentScreen == null) { - this.mc.setIngameFocus(); - } - } else if (parInt1 == 1) { - showingCloseKey = System.currentTimeMillis(); - } - } - - /** - * + - * Returns a string stored in the system clipboard. - */ - public static String getClipboardString() { - return EagRuntime.getClipboard(); - } - - /** - * + - * Stores the given string in the system clipboard - */ - public static void setClipboardString(String copyText) { - if (!StringUtils.isEmpty(copyText)) { - EagRuntime.setClipboard(copyText); - } - } - - protected void renderToolTip(ItemStack itemstack, int i, int j) { - List list = itemstack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); - - for (int k = 0, l = list.size(); k < l; ++k) { - if (k == 0) { - list.set(k, itemstack.getRarity().rarityColor + (String) list.get(k)); - } else { - list.set(k, EnumChatFormatting.GRAY + (String) list.get(k)); - } - } - - this.drawHoveringText(list, i, j); - } - - /** - * + - * Draws the text when mouse is over creative inventory tab. - * Params: current creative tab to be checked, current mouse x - * position, current mouse y position. - */ - protected void drawCreativeTabHoveringText(String s, int i, int j) { - this.drawHoveringText(Arrays.asList(new String[] { s }), i, j); - } - - /** - * + - * Draws a List of strings as a tooltip. Every entry is drawn on - * a seperate line. - */ - protected void drawHoveringText(List list, int i, int j) { - if (!list.isEmpty()) { - GlStateManager.disableRescaleNormal(); - RenderHelper.disableStandardItemLighting(); - GlStateManager.disableLighting(); - GlStateManager.disableDepth(); - int k = 0; - - for (int m = 0, n = list.size(); m < n; ++m) { - int l = this.fontRendererObj.getStringWidth(list.get(m)); - if (l > k) { - k = l; - } - } - - int j2 = i + 12; - int k2 = j - 12; - int i1 = 8; - if (list.size() > 1) { - i1 += 2 + (list.size() - 1) * 10; - } - - if (j2 + k > this.width) { - j2 -= 28 + k; - } - - if (k2 + i1 + 6 > this.height) { - k2 = this.height - i1 - 6; - } - - this.zLevel = 300.0F; - this.itemRender.zLevel = 300.0F; - int j1 = -267386864; - this.drawGradientRect(j2 - 3, k2 - 4, j2 + k + 3, k2 - 3, j1, j1); - this.drawGradientRect(j2 - 3, k2 + i1 + 3, j2 + k + 3, k2 + i1 + 4, j1, j1); - this.drawGradientRect(j2 - 3, k2 - 3, j2 + k + 3, k2 + i1 + 3, j1, j1); - this.drawGradientRect(j2 - 4, k2 - 3, j2 - 3, k2 + i1 + 3, j1, j1); - this.drawGradientRect(j2 + k + 3, k2 - 3, j2 + k + 4, k2 + i1 + 3, j1, j1); - int k1 = 1347420415; - int l1 = (k1 & 16711422) >> 1 | k1 & -16777216; - this.drawGradientRect(j2 - 3, k2 - 3 + 1, j2 - 3 + 1, k2 + i1 + 3 - 1, k1, l1); - this.drawGradientRect(j2 + k + 2, k2 - 3 + 1, j2 + k + 3, k2 + i1 + 3 - 1, k1, l1); - this.drawGradientRect(j2 - 3, k2 - 3, j2 + k + 3, k2 - 3 + 1, k1, k1); - this.drawGradientRect(j2 - 3, k2 + i1 + 2, j2 + k + 3, k2 + i1 + 3, l1, l1); - - for (int i2 = 0; i2 < list.size(); ++i2) { - String s1 = (String) list.get(i2); - if (s1.length() > 0) { - this.fontRendererObj.drawStringWithShadow(s1, (float) j2, (float) k2, -1); - } - if (i2 == 0) { - k2 += 2; - } - - k2 += 10; - } - - this.zLevel = 0.0F; - this.itemRender.zLevel = 0.0F; - GlStateManager.enableLighting(); - GlStateManager.enableDepth(); - RenderHelper.enableStandardItemLighting(); - GlStateManager.enableRescaleNormal(); - } - } - - /** - * + - * Draws the hover event specified by the given chat component - */ - protected void handleComponentHover(IChatComponent parIChatComponent, int parInt1, int parInt2) { - if (parIChatComponent != null && parIChatComponent.getChatStyle().getChatHoverEvent() != null) { - HoverEvent hoverevent = parIChatComponent.getChatStyle().getChatHoverEvent(); - if (hoverevent.getAction() == HoverEvent.Action.SHOW_ITEM) { - ItemStack itemstack = null; - - try { - NBTTagCompound nbttagcompound = JsonToNBT - .getTagFromJson(hoverevent.getValue().getUnformattedText()); - if (nbttagcompound instanceof NBTTagCompound) { - itemstack = ItemStack.loadItemStackFromNBT((NBTTagCompound) nbttagcompound); - } - } catch (NBTException var11) { - ; - } - - if (itemstack != null) { - this.renderToolTip(itemstack, parInt1, parInt2); - } else { - this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Item!", parInt1, parInt2); - } - } else if (hoverevent.getAction() == HoverEvent.Action.SHOW_ENTITY) { - if (this.mc.gameSettings.advancedItemTooltips) { - try { - NBTTagCompound nbttagcompound2 = JsonToNBT - .getTagFromJson(hoverevent.getValue().getUnformattedText()); - if (nbttagcompound2 instanceof NBTTagCompound) { - ArrayList arraylist1 = Lists.newArrayList(); - NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbttagcompound2; - arraylist1.add(nbttagcompound1.getString("name")); - if (nbttagcompound1.hasKey("type", 8)) { - String s = nbttagcompound1.getString("type"); - arraylist1.add("Type: " + s + " (" + EntityList.getIDFromString(s) + ")"); - } - - arraylist1.add(nbttagcompound1.getString("id")); - this.drawHoveringText(arraylist1, parInt1, parInt2); - } else { - this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Entity!", parInt1, - parInt2); - } - } catch (NBTException var10) { - this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Entity!", parInt1, parInt2); - } - } - } else if (hoverevent.getAction() == HoverEvent.Action.SHOW_TEXT) { - this.drawHoveringText(NEWLINE_SPLITTER.splitToList(hoverevent.getValue().getFormattedText()), parInt1, - parInt2); - } else if (hoverevent.getAction() == HoverEvent.Action.SHOW_ACHIEVEMENT) { - StatBase statbase = StatList.getOneShotStat(hoverevent.getValue().getUnformattedText()); - if (statbase != null) { - IChatComponent ichatcomponent = statbase.getStatName(); - ChatComponentTranslation chatcomponenttranslation = new ChatComponentTranslation( - "stats.tooltip.type." + (statbase.isAchievement() ? "achievement" : "statistic"), - new Object[0]); - chatcomponenttranslation.getChatStyle().setItalic(Boolean.valueOf(true)); - String s1 = statbase instanceof Achievement ? ((Achievement) statbase).getDescription() : null; - ArrayList arraylist = Lists.newArrayList(new String[] { ichatcomponent.getFormattedText(), - chatcomponenttranslation.getFormattedText() }); - if (s1 != null) { - arraylist.addAll(this.fontRendererObj.listFormattedStringToWidth(s1, 150)); - } - - this.drawHoveringText(arraylist, parInt1, parInt2); - } else { - this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid statistic/achievement!", parInt1, - parInt2); - } - } - - GlStateManager.disableLighting(); - } - } - - /** - * + - * Sets the text of the chat - */ - protected void setText(String var1, boolean var2) { - } - - /** - * + - * Executes the click event specified by the given chat - * component - */ - protected boolean handleComponentClick(IChatComponent parIChatComponent) { - if (parIChatComponent == null) { - return false; - } else { - ClickEvent clickevent = parIChatComponent.getChatStyle().getChatClickEvent(); - if (isShiftKeyDown()) { - if (parIChatComponent.getChatStyle().getInsertion() != null) { - this.setText(parIChatComponent.getChatStyle().getInsertion(), false); - } - } else if (clickevent != null) { - if (clickevent.getAction() == ClickEvent.Action.OPEN_URL) { - if (!this.mc.gameSettings.chatLinks) { - return false; - } - String uri = clickevent.getValue(); - - if (this.mc.gameSettings.chatLinksPrompt) { - this.clickedLinkURI = uri; - this.mc.displayGuiScreen(new GuiConfirmOpenLink(this, clickevent.getValue(), 31102009, false)); - } else { - this.openWebLink(uri); - } - } else if (clickevent.getAction() == ClickEvent.Action.OPEN_FILE) { - // rip - } else if (clickevent.getAction() == ClickEvent.Action.SUGGEST_COMMAND) { - this.setText(clickevent.getValue(), true); - } else if (clickevent.getAction() == ClickEvent.Action.RUN_COMMAND) { - this.sendChatMessage(clickevent.getValue(), false); - } else if (clickevent.getAction() == ClickEvent.Action.TWITCH_USER_INFO) { - /* - * ChatUserInfo chatuserinfo = - * this.mc.getTwitchStream().func_152926_a(clickevent.getValue()); if - * (chatuserinfo != null) { this.mc.displayGuiScreen(new - * GuiTwitchUserMode(this.mc.getTwitchStream(), chatuserinfo)); } else { } - */ - LOGGER.error("Tried to handle twitch user but couldn\'t find them!"); - } else if (clickevent.getAction() == ClickEvent.Action.EAGLER_PLUGIN_DOWNLOAD) { - if (EaglerXBungeeVersion.pluginFileEPK.equals(clickevent.getValue())) { - EaglerXBungeeVersion.startPluginDownload(); - } else { - LOGGER.error("Invalid plugin download from EPK was blocked: {}", - EaglerXBungeeVersion.pluginFileEPK); - } - } else { - LOGGER.error("Don\'t know how to handle " + clickevent); - } - - return true; - } - - return false; - } - } - - public void sendChatMessage(String msg) { - this.sendChatMessage(msg, true); - } - - public void sendChatMessage(String msg, boolean addToChat) { - if (addToChat) { - this.mc.ingameGUI.getChatGUI().addToSentMessages(msg); - } - - this.mc.thePlayer.sendChatMessage(msg); - } - - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { - if (parInt3 == 0) { - for (int i = 0; i < this.buttonList.size(); ++i) { - GuiButton guibutton = (GuiButton) this.buttonList.get(i); - if (guibutton.mousePressed(this.mc, parInt1, parInt2)) { - this.selectedButton = guibutton; - guibutton.playPressSound(this.mc.getSoundHandler()); - this.actionPerformed(guibutton); - } - } - } - - } - - /** - * + - * Called when a mouse button is released. Args : mouseX, - * mouseY, releaseButton - */ - protected void mouseReleased(int i, int j, int k) { - if (this.selectedButton != null && k == 0) { - this.selectedButton.mouseReleased(i, j); - this.selectedButton = null; - } - - } - - /** - * + - * Called when a mouse button is pressed and the mouse is moved - * around. Parameters are : mouseX, mouseY, lastButtonClicked & - * timeSinceMouseClick. - */ - protected void mouseClickMove(int var1, int var2, int var3, long var4) { - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - } - - /** - * + - * Causes the screen to lay out its subcomponents again. This is - * the equivalent of the Java call Container.validate() - */ - public void setWorldAndResolution(Minecraft mc, int width, int height) { - this.mc = mc; - this.itemRender = mc.getRenderItem(); - this.fontRendererObj = mc.fontRendererObj; - this.width = width; - this.height = height; - this.buttonList.clear(); - this.initGui(); - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - } - - /** - * + - * Delegates mouse and keyboard input. - */ - public void handleInput() throws IOException { - if (Mouse.isCreated()) { - while (Mouse.next()) { - this.handleMouseInput(); - } - } - - if (Keyboard.isCreated()) { - while (Keyboard.next()) { - this.handleKeyboardInput(); - } - } - - } - - /** - * + - * Handles mouse input. - */ - public void handleMouseInput() throws IOException { - int i = Mouse.getEventX() * this.width / this.mc.displayWidth; - int j = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; - int k = Mouse.getEventButton(); - if (Mouse.getEventButtonState()) { - if (this.mc.gameSettings.touchscreen && this.touchValue++ > 0) { - return; - } - - this.eventButton = k; - this.lastMouseEvent = Minecraft.getSystemTime(); - this.mouseClicked(i, j, this.eventButton); - } else if (k != -1) { - if (this.mc.gameSettings.touchscreen && --this.touchValue > 0) { - return; - } - - this.eventButton = -1; - this.mouseReleased(i, j, k); - } else if (this.eventButton != -1 && this.lastMouseEvent > 0L) { - long l = Minecraft.getSystemTime() - this.lastMouseEvent; - this.mouseClickMove(i, j, this.eventButton, l); - } - - } - - /** - * + - * Handles keyboard input. - */ - public void handleKeyboardInput() throws IOException { - if (Keyboard.getEventKeyState()) { - this.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey()); - } - - this.mc.dispatchKeypresses(); - } - - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - } - - /** - * + - * Called when the screen is unloaded. Used to disable keyboard - * repeat events - */ - public void onGuiClosed() { - } - - /** - * + - * Draws either a gradient over the background screen (when it - * exists) or a flat gradient over background.png - */ - public void drawDefaultBackground() { - this.drawWorldBackground(0); - } - - public void drawWorldBackground(int i) { - if (this.mc.theWorld != null) { - this.drawGradientRect(0, 0, this.width, this.height, -1072689136, -804253680); - } else { - this.drawBackground(i); - } - - } - - /** - * + - * Draws the background (i is always 0 as of 1.2.2) - */ - public void drawBackground(int tint) { - GlStateManager.disableLighting(); - GlStateManager.disableFog(); - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - this.mc.getTextureManager().bindTexture(optionsBackground); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - float f = 32.0F; - worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); - worldrenderer.pos(0.0D, (double) this.height, 0.0D) - .tex(0.0D, (double) ((float) this.height / 32.0F + (float) tint)).color(64, 64, 64, 255).endVertex(); - worldrenderer.pos((double) this.width, (double) this.height, 0.0D) - .tex((double) ((float) this.width / 32.0F), (double) ((float) this.height / 32.0F + (float) tint)) - .color(64, 64, 64, 255).endVertex(); - worldrenderer.pos((double) this.width, 0.0D, 0.0D).tex((double) ((float) this.width / 32.0F), (double) tint) - .color(64, 64, 64, 255).endVertex(); - worldrenderer.pos(0.0D, 0.0D, 0.0D).tex(0.0D, (double) tint).color(64, 64, 64, 255).endVertex(); - tessellator.draw(); - } - - /** - * + - * Returns true if this GUI should pause the game when it is - * displayed in single-player - */ - public boolean doesGuiPauseGame() { - return true; - } - - public void confirmClicked(boolean flag, int i) { - if (i == 31102009) { - if (flag) { - this.openWebLink(this.clickedLinkURI); - } - - this.clickedLinkURI = null; - this.mc.displayGuiScreen(this); - } - - } - - private void openWebLink(String parURI) { - EagRuntime.openLink(parURI); - } - - /** - * + - * Returns true if either windows ctrl key is down or if either - * mac meta key is down - */ - public static boolean isCtrlKeyDown() { - return Minecraft.isRunningOnMac ? Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220) - : Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157); - } - - /** - * + - * Returns true if either shift key is down - */ - public static boolean isShiftKeyDown() { - return Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54); - } - - /** - * + - * Returns true if either alt key is down - */ - public static boolean isAltKeyDown() { - return Keyboard.isKeyDown(56) || Keyboard.isKeyDown(184); - } - - public static boolean isKeyComboCtrlX(int parInt1) { - return parInt1 == 45 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); - } - - public static boolean isKeyComboCtrlV(int parInt1) { - return parInt1 == 47 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); - } - - public static boolean isKeyComboCtrlC(int parInt1) { - return parInt1 == 46 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); - } - - public static boolean isKeyComboCtrlA(int parInt1) { - return parInt1 == 30 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); - } - - /** - * + - * Called when the GUI is resized in order to update the world - * and the resolution - */ - public void onResize(Minecraft mcIn, int parInt1, int parInt2) { - this.setWorldAndResolution(mcIn, parInt1, parInt2); - } - - public boolean shouldHangupIntegratedServer() { - return true; - } - - public boolean blockPTTKey() { - return false; - } +package net.minecraft.client.gui; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent; +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchControls; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenServerInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.EntityList; +import net.minecraft.event.ClickEvent; +import net.minecraft.event.HoverEvent; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTException; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.stats.Achievement; +import net.minecraft.stats.StatBase; +import net.minecraft.stats.StatList; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.ResourceLocation; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class GuiScreen extends Gui implements GuiYesNoCallback { + private static final Logger LOGGER = LogManager.getLogger(); + private static final Set PROTOCOLS = Sets.newHashSet(new String[] { "http", "https" }); + private static final Splitter NEWLINE_SPLITTER = Splitter.on('\n'); + protected Minecraft mc; + protected RenderItem itemRender; + public int width; + public int height; + /** + * + + * A list of all the buttons in this container. + */ + protected List buttonList = Lists.newArrayList(); + /** + * + + * A list of all the labels in this container. + */ + protected List labelList = Lists.newArrayList(); + public boolean allowUserInput; + protected FontRenderer fontRendererObj; + protected GuiButton selectedButton; + private int eventButton; + private long lastMouseEvent; + private int touchValue; + private String clickedLinkURI; + protected long showingCloseKey = 0; + + protected int touchModeCursorPosX = -1; + protected int touchModeCursorPosY = -1; + private long lastTouchEvent; + + /**+ + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float var3) { + for (int k = 0, l = this.buttonList.size(); k < l; ++k) { + ((GuiButton) this.buttonList.get(k)).drawButton(this.mc, i, j); + } + + for (int l = 0, m = this.labelList.size(); l < m; ++l) { + ((GuiLabel) this.labelList.get(l)).drawLabel(this.mc, i, j); + } + + long millis = EagRuntime.steadyTimeMillis(); + long closeKeyTimeout = millis - showingCloseKey; + if (closeKeyTimeout < 3000l) { + int alpha1 = 0xC0000000; + int alpha2 = 0xFF000000; + if (closeKeyTimeout > 2500l) { + float f = (float) (3000l - closeKeyTimeout) * 0.002f; + if (f < 0.03f) + f = 0.03f; + alpha1 = (int) (f * 192.0f) << 24; + alpha2 = (int) (f * 255.0f) << 24; + } + String str; + int k = getCloseKey(); + if (k == KeyboardConstants.KEY_GRAVE) { + str = I18n.format("gui.exitKeyRetarded"); + } else { + str = I18n.format("gui.exitKey", Keyboard.getKeyName(k)); + } + int w = fontRendererObj.getStringWidth(str); + int x = (width - w - 4) / 2; + int y = 10; + drawRect(x, y, x + w + 4, y + 12, alpha1); + if (closeKeyTimeout > 2500l) + GlStateManager.enableBlend(); + fontRendererObj.drawStringWithShadow(str, x + 2, y + 2, 0xFFAAAA | alpha2); + if (closeKeyTimeout > 2500l) + GlStateManager.disableBlend(); + } + + } + + protected int getCloseKey() { + if (this instanceof GuiContainer) { + return this.mc.gameSettings.keyBindInventory.getKeyCode(); + } else { + return this.mc.gameSettings.keyBindClose.getKeyCode(); + } + } + + /** + * + + * Fired when a key is typed (except F11 which toggles full + * screen). This is the equivalent of + * KeyListener.keyTyped(KeyEvent e). Args : character (character + * on the key), keyCode (lwjgl Keyboard key code) + */ + protected void keyTyped(char parChar1, int parInt1) { + if (!canCloseGui()) + return; + if (((this.mc.theWorld == null || this.mc.thePlayer.getHealth() <= 0.0F) && parInt1 == 1) + || parInt1 == this.mc.gameSettings.keyBindClose.getKeyCode() + || (parInt1 == 1 && (this.mc.gameSettings.keyBindClose.getKeyCode() == 0 || this.mc.areKeysLocked()))) { + this.mc.displayGuiScreen((GuiScreen) null); + if (this.mc.currentScreen == null) { + this.mc.setIngameFocus(); + } + } else if (parInt1 == 1) { + showingCloseKey = EagRuntime.steadyTimeMillis(); + } + } + + /** + * + + * Returns a string stored in the system clipboard. + */ + public static String getClipboardString() { + return EagRuntime.getClipboard(); + } + + /** + * + + * Stores the given string in the system clipboard + */ + public static void setClipboardString(String copyText) { + if (!StringUtils.isEmpty(copyText)) { + EagRuntime.setClipboard(copyText); + } + } + + protected void renderToolTip(ItemStack itemstack, int i, int j) { + renderToolTip0(itemstack, i, j, false); + } + + protected void renderToolTip0(ItemStack itemstack, int i, int j, boolean eagler) { + List list = itemstack.getTooltipProfanityFilter(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); + + for (int k = 0, l = list.size(); k < l; ++k) { + if (k == 0) { + list.set(k, itemstack.getRarity().rarityColor + (String) list.get(k)); + } else { + list.set(k, EnumChatFormatting.GRAY + (String) list.get(k)); + } + } + + this.drawHoveringText0(list, i, j, eagler); + } + + /** + * + + * Draws the text when mouse is over creative inventory tab. + * Params: current creative tab to be checked, current mouse x + * position, current mouse y position. + */ + protected void drawCreativeTabHoveringText(String s, int i, int j) { + this.drawHoveringText(Arrays.asList(new String[] { s }), i, j); + } + + /** + * + + * Draws a List of strings as a tooltip. Every entry is drawn on + * a seperate line. + */ + protected void drawHoveringText(List list, int i, int j) { + drawHoveringText0(list, i, j, false); + } + + protected void drawHoveringText0(List list, int i, int j, boolean eagler) { + if (!list.isEmpty()) { + GlStateManager.disableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + int k = 0; + + for (int m = 0, n = list.size(); m < n; ++m) { + int l = this.fontRendererObj.getStringWidth(list.get(m)); + if (l > k) { + k = l; + } + } + + int j2 = i; + int k2 = j; + int i1 = 8; + if (list.size() > 1) { + i1 += 2 + (list.size() - 1) * 10; + } + + if (!eagler) { + j2 += 12; + k2 -= 12; + + if (j2 + k > this.width) { + j2 -= 28 + k; + } + + if (k2 + i1 + 6 > this.height) { + k2 = this.height - i1 - 6; + } + } else { + j2 -= (k + 3) >> 1; + } + + this.zLevel = 300.0F; + this.itemRender.zLevel = 300.0F; + int j1 = -267386864; + this.drawGradientRect(j2 - 3, k2 - 4, j2 + k + 3, k2 - 3, j1, j1); + this.drawGradientRect(j2 - 3, k2 + i1 + 3, j2 + k + 3, k2 + i1 + 4, j1, j1); + this.drawGradientRect(j2 - 3, k2 - 3, j2 + k + 3, k2 + i1 + 3, j1, j1); + this.drawGradientRect(j2 - 4, k2 - 3, j2 - 3, k2 + i1 + 3, j1, j1); + this.drawGradientRect(j2 + k + 3, k2 - 3, j2 + k + 4, k2 + i1 + 3, j1, j1); + int k1 = 1347420415; + int l1 = (k1 & 16711422) >> 1 | k1 & -16777216; + this.drawGradientRect(j2 - 3, k2 - 3 + 1, j2 - 3 + 1, k2 + i1 + 3 - 1, k1, l1); + this.drawGradientRect(j2 + k + 2, k2 - 3 + 1, j2 + k + 3, k2 + i1 + 3 - 1, k1, l1); + this.drawGradientRect(j2 - 3, k2 - 3, j2 + k + 3, k2 - 3 + 1, k1, k1); + this.drawGradientRect(j2 - 3, k2 + i1 + 2, j2 + k + 3, k2 + i1 + 3, l1, l1); + + for (int i2 = 0; i2 < list.size(); ++i2) { + String s1 = (String) list.get(i2); + if (s1.length() > 0) { + this.fontRendererObj.drawStringWithShadow(s1, (float) j2, (float) k2, -1); + } + if (i2 == 0) { + k2 += 2; + } + + k2 += 10; + } + + this.zLevel = 0.0F; + this.itemRender.zLevel = 0.0F; + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableRescaleNormal(); + } + } + + /** + * + + * Draws the hover event specified by the given chat component + */ + public void handleComponentHover(IChatComponent parIChatComponent, int parInt1, int parInt2) { + if (parIChatComponent != null && parIChatComponent.getChatStyle().getChatHoverEvent() != null) { + HoverEvent hoverevent = parIChatComponent.getChatStyle().getChatHoverEvent(); + if (hoverevent.getAction() == HoverEvent.Action.SHOW_ITEM) { + ItemStack itemstack = null; + + try { + NBTTagCompound nbttagcompound = JsonToNBT + .getTagFromJson(hoverevent.getValue().getUnformattedText()); + if (nbttagcompound instanceof NBTTagCompound) { + itemstack = ItemStack.loadItemStackFromNBT((NBTTagCompound) nbttagcompound); + } + } catch (NBTException var11) { + ; + } + + if (itemstack != null) { + this.renderToolTip(itemstack, parInt1, parInt2); + } else { + this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Item!", parInt1, parInt2); + } + } else if (hoverevent.getAction() == HoverEvent.Action.SHOW_ENTITY) { + if (this.mc.gameSettings.advancedItemTooltips) { + try { + NBTTagCompound nbttagcompound2 = JsonToNBT + .getTagFromJson(hoverevent.getValue().getUnformattedText()); + if (nbttagcompound2 instanceof NBTTagCompound) { + ArrayList arraylist1 = Lists.newArrayList(); + NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbttagcompound2; + arraylist1.add(nbttagcompound1.getString("name")); + if (nbttagcompound1.hasKey("type", 8)) { + String s = nbttagcompound1.getString("type"); + arraylist1.add("Type: " + s + " (" + EntityList.getIDFromString(s) + ")"); + } + + arraylist1.add(nbttagcompound1.getString("id")); + this.drawHoveringText(arraylist1, parInt1, parInt2); + } else { + this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Entity!", parInt1, + parInt2); + } + } catch (NBTException var10) { + this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Entity!", parInt1, parInt2); + } + } + } else if (hoverevent.getAction() == HoverEvent.Action.SHOW_TEXT) { + this.drawHoveringText(NEWLINE_SPLITTER.splitToList(hoverevent.getValue().getFormattedText()), parInt1, + parInt2); + } else if (hoverevent.getAction() == HoverEvent.Action.SHOW_ACHIEVEMENT) { + StatBase statbase = StatList.getOneShotStat(hoverevent.getValue().getUnformattedText()); + if (statbase != null) { + IChatComponent ichatcomponent = statbase.getStatName(); + ChatComponentTranslation chatcomponenttranslation = new ChatComponentTranslation( + "stats.tooltip.type." + (statbase.isAchievement() ? "achievement" : "statistic"), + new Object[0]); + chatcomponenttranslation.getChatStyle().setItalic(Boolean.valueOf(true)); + String s1 = statbase instanceof Achievement ? ((Achievement) statbase).getDescription() : null; + ArrayList arraylist = Lists.newArrayList(new String[] { ichatcomponent.getFormattedText(), + chatcomponenttranslation.getFormattedText() }); + if (s1 != null) { + arraylist.addAll(this.fontRendererObj.listFormattedStringToWidth(s1, 150)); + } + + this.drawHoveringText(arraylist, parInt1, parInt2); + } else { + this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid statistic/achievement!", parInt1, + parInt2); + } + } + + GlStateManager.disableLighting(); + } + } + + /** + * + + * Sets the text of the chat + */ + protected void setText(String var1, boolean var2) { + } + + /** + * + + * Executes the click event specified by the given chat + * component + */ + public boolean handleComponentClick(IChatComponent parIChatComponent) { + if (parIChatComponent == null) { + return false; + } else { + ClickEvent clickevent = parIChatComponent.getChatStyle().getChatClickEvent(); + if (isShiftKeyDown()) { + if (parIChatComponent.getChatStyle().getInsertion() != null) { + this.setText(parIChatComponent.getChatStyle().getInsertion(), false); + } + } else if (clickevent != null) { + if (clickevent.getAction() == ClickEvent.Action.OPEN_URL) { + if (!this.mc.gameSettings.chatLinks) { + return false; + } + String uri = clickevent.getValue(); + + if (this.mc.gameSettings.chatLinksPrompt) { + this.clickedLinkURI = uri; + this.mc.displayGuiScreen(new GuiConfirmOpenLink(this, clickevent.getValue(), 31102009, false)); + } else { + this.openWebLink(uri); + } + } else if (clickevent.getAction() == ClickEvent.Action.OPEN_FILE) { + // rip + } else if (clickevent.getAction() == ClickEvent.Action.SUGGEST_COMMAND) { + this.setText(clickevent.getValue(), true); + } else if (clickevent.getAction() == ClickEvent.Action.RUN_COMMAND) { + this.sendChatMessage(clickevent.getValue(), false); + } else if (clickevent.getAction() == ClickEvent.Action.TWITCH_USER_INFO) { + /* + * ChatUserInfo chatuserinfo = + * this.mc.getTwitchStream().func_152926_a(clickevent.getValue()); if + * (chatuserinfo != null) { this.mc.displayGuiScreen(new + * GuiTwitchUserMode(this.mc.getTwitchStream(), chatuserinfo)); } else { } + */ + LOGGER.error("Tried to handle twitch user but couldn\'t find them!"); + } else if (clickevent.getAction() == ClickEvent.Action.EAGLER_PLUGIN_DOWNLOAD) { + if (EaglerXBungeeVersion.pluginFileEPK.equals(clickevent.getValue())) { + EaglerXBungeeVersion.startPluginDownload(); + } else { + LOGGER.error("Invalid plugin download from EPK was blocked: {}", + EaglerXBungeeVersion.pluginFileEPK); + } + } else { + LOGGER.error("Don\'t know how to handle " + clickevent); + } + + return true; + } + + return false; + } + } + + public void sendChatMessage(String msg) { + this.sendChatMessage(msg, true); + } + + public void sendChatMessage(String msg, boolean addToChat) { + if (addToChat) { + this.mc.ingameGUI.getChatGUI().addToSentMessages(msg); + } + + this.mc.thePlayer.sendChatMessage(msg); + } + + protected void touchStarted(int parInt1, int parInt2, int parInt3) { + if (shouldTouchGenerateMouseEvents()) { + this.mouseClicked(parInt1, parInt2, 12345); + } + } + + protected void touchTapped(int parInt1, int parInt2, int parInt3) { + if (shouldTouchGenerateMouseEvents()) { + this.mouseClicked(parInt1, parInt2, 0); + this.mouseReleased(parInt1, parInt2, 0); + } + } + + protected void touchMoved(int parInt1, int parInt2, int parInt3) { + } + + protected void touchEndMove(int parInt1, int parInt2, int parInt3) { + if (shouldTouchGenerateMouseEvents()) { + this.mouseReleased(parInt1, parInt2, 12345); + } + } + + /**+ + * Called when the mouse is clicked. Args : mouseX, mouseY, + * clickedButton + */ + protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if (parInt3 == 0 || parInt3 == 12345) { + for (int i = 0; i < this.buttonList.size(); ++i) { + GuiButton guibutton = (GuiButton) this.buttonList.get(i); + if (touchMode && (parInt3 == 12345) != guibutton.isSliderTouchEvents()) + continue; + if (guibutton.mousePressed(this.mc, parInt1, parInt2)) { + this.selectedButton = guibutton; + guibutton.playPressSound(this.mc.getSoundHandler()); + this.actionPerformed(guibutton); + } + } + } + + } + + /** + * + + * Called when a mouse button is released. Args : mouseX, + * mouseY, releaseButton + */ + protected void mouseReleased(int i, int j, int k) { + if (this.selectedButton != null && (k == 0 || k == 12345) + && (!PointerInputAbstraction.isTouchMode() || (k == 12345) == selectedButton.isSliderTouchEvents())) { + this.selectedButton.mouseReleased(i, j); + this.selectedButton = null; + } + + } + + /** + * + + * Called when a mouse button is pressed and the mouse is moved + * around. Parameters are : mouseX, mouseY, lastButtonClicked & + * timeSinceMouseClick. + */ + protected void mouseClickMove(int var1, int var2, int var3, long var4) { + } + + /** + * + + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + } + + /** + * + + * Causes the screen to lay out its subcomponents again. This is + * the equivalent of the Java call Container.validate() + */ + public void setWorldAndResolution(Minecraft mc, int width, int height) { + this.mc = mc; + this.itemRender = mc.getRenderItem(); + this.fontRendererObj = mc.fontRendererObj; + this.width = width; + this.height = height; + this.buttonList.clear(); + this.initGui(); + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + } + + /** + * + + * Delegates mouse and keyboard input. + */ + public void handleInput() throws IOException { + boolean noTouch = true; + while (Touch.next()) { + noTouch = false; + this.handleTouchInput(); + TouchControls.handleInput(); + } + + if (Mouse.isCreated()) { + while (Mouse.next()) { + if (noTouch) { + this.handleMouseInput(); + } + } + } + + if (Keyboard.isCreated()) { + while (Keyboard.next()) { + this.handleKeyboardInput(); + } + } + + } + + public final Map touchStarts = new HashMap<>(); + + /** + * Handles touch input. + */ + public void handleTouchInput() throws IOException { + EnumTouchEvent et = Touch.getEventType(); + if (et == EnumTouchEvent.TOUCHSTART) { + PointerInputAbstraction.enterTouchModeHook(); + } + float scaleFac = getEaglerScale(); + for (int t = 0, c = Touch.getEventTouchPointCount(); t < c; ++t) { + int u = Touch.getEventTouchPointUID(t); + int i = Touch.getEventTouchX(t); + int j = Touch.getEventTouchY(t); + if (et == EnumTouchEvent.TOUCHSTART) { + if (TouchControls.handleTouchBegin(u, i, j)) { + continue; + } + } else if (et == EnumTouchEvent.TOUCHEND) { + if (TouchControls.handleTouchEnd(u, i, j)) { + continue; + } + } + i = applyEaglerScale(scaleFac, i * this.width / this.mc.displayWidth, this.width); + j = applyEaglerScale(scaleFac, this.height - j * this.height / this.mc.displayHeight - 1, this.height); + float si = Touch.getEventTouchRadiusX(t) * this.width / this.mc.displayWidth / scaleFac; + if (si < 1.0f) + si = 1.0f; + float sj = Touch.getEventTouchRadiusY(t) * this.height / this.mc.displayHeight / scaleFac; + if (sj < 1.0f) + sj = 1.0f; + int[] ck = touchStarts.remove(u); + switch (et) { + case TOUCHSTART: + if (t == 0) { + touchModeCursorPosX = i; + touchModeCursorPosY = j; + } + lastTouchEvent = EagRuntime.steadyTimeMillis(); + touchStarts.put(u, new int[] { i, j, 0 }); + this.touchStarted(i, j, u); + break; + case TOUCHMOVE: + if (t == 0) { + touchModeCursorPosX = i; + touchModeCursorPosY = j; + } + if (ck != null && Math.abs(ck[0] - i) < si && Math.abs(ck[1] - j) < sj) { + touchStarts.put(u, ck); + break; + } + touchStarts.put(u, new int[] { i, j, (ck != null && isTouchDraggingStateLocked(u)) ? ck[2] : 1 }); + this.touchMoved(i, j, u); + if (t == 0 && shouldTouchGenerateMouseEvents()) { + this.mouseClickMove(i, j, 0, EagRuntime.steadyTimeMillis() - lastTouchEvent); + } + break; + case TOUCHEND: + if (ck == null) + break; + if (t == 0) { + touchModeCursorPosX = -1; + touchModeCursorPosY = -1; + } + if (ck != null && ck[2] == 1) { + this.touchEndMove(i, j, u); + } else { + if (ck != null) { + i = ck[0]; + j = ck[1]; + } + this.touchTapped(i, j, u); + } + break; + } + } + } + + public boolean isTouchPointDragging(int uid) { + int[] ret = touchStarts.get(uid); + return ret != null && ret[2] == 1; + } + + /**+ + * Handles mouse input. + */ + public void handleMouseInput() throws IOException { + float f = getEaglerScale(); + int i = applyEaglerScale(f, Mouse.getEventX() * this.width / this.mc.displayWidth, this.width); + int j = applyEaglerScale(f, this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1, + this.height); + int k = Mouse.getEventButton(); + if (Mouse.getEventButtonState()) { + PointerInputAbstraction.enterMouseModeHook(); + if (this.mc.gameSettings.touchscreen && this.touchValue++ > 0) { + return; + } + + this.eventButton = k; + this.lastMouseEvent = Minecraft.getSystemTime(); + this.mouseClicked(i, j, this.eventButton); + } else if (k != -1) { + if (this.mc.gameSettings.touchscreen && --this.touchValue > 0) { + return; + } + + this.eventButton = -1; + this.mouseReleased(i, j, k); + } else if (this.eventButton != -1 && this.lastMouseEvent > 0L) { + long l = Minecraft.getSystemTime() - this.lastMouseEvent; + this.mouseClickMove(i, j, this.eventButton, l); + } + + } + + /** + * + + * Handles keyboard input. + */ + public void handleKeyboardInput() throws IOException { + if (Keyboard.getEventKeyState()) { + this.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey()); + } + + this.mc.dispatchKeypresses(); + } + + /** + * + + * Called from the main game loop to update the screen. + */ + public void updateScreen() { + } + + /** + * + + * Called when the screen is unloaded. Used to disable keyboard + * repeat events + */ + public void onGuiClosed() { + } + + /** + * + + * Draws either a gradient over the background screen (when it + * exists) or a flat gradient over background.png + */ + public void drawDefaultBackground() { + this.drawWorldBackground(0); + } + + protected boolean isPartOfPauseMenu() { + return false; + } + + public void drawWorldBackground(int i) { + if (this.mc.theWorld != null) { + boolean ingame = isPartOfPauseMenu(); + ResourceLocation loc = (ingame && PauseMenuCustomizeState.icon_background_pause != null) + ? PauseMenuCustomizeState.icon_background_pause + : PauseMenuCustomizeState.icon_background_all; + float aspect = (ingame && PauseMenuCustomizeState.icon_background_pause != null) + ? 1.0f / PauseMenuCustomizeState.icon_background_pause_aspect + : 1.0f / PauseMenuCustomizeState.icon_background_all_aspect; + if (loc != null) { + GlStateManager.disableLighting(); + GlStateManager.disableFog(); + GlStateManager.enableBlend(); + GlStateManager.disableAlpha(); + GlStateManager.enableTexture2D(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + this.mc.getTextureManager().bindTexture(loc); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + float f = 64.0F; + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); + worldrenderer.pos(0.0D, (double) this.height, 0.0D).tex(0.0D, (double) ((float) this.height / f)) + .color(64, 64, 64, 192).endVertex(); + worldrenderer.pos((double) this.width, (double) this.height, 0.0D) + .tex((double) ((float) this.width / f * aspect), (double) ((float) this.height / f)) + .color(64, 64, 64, 192).endVertex(); + worldrenderer.pos((double) this.width, 0.0D, 0.0D) + .tex((double) ((float) this.width / f * aspect), (double) 0).color(64, 64, 64, 192).endVertex(); + worldrenderer.pos(0.0D, 0.0D, 0.0D).tex(0.0D, (double) 0).color(64, 64, 64, 192).endVertex(); + tessellator.draw(); + GlStateManager.enableAlpha(); + } else { + this.drawGradientRect(0, 0, this.width, this.height, -1072689136, -804253680); + } + if (!(this instanceof GuiScreenServerInfo)) { + loc = (ingame && PauseMenuCustomizeState.icon_watermark_pause != null) + ? PauseMenuCustomizeState.icon_watermark_pause + : PauseMenuCustomizeState.icon_watermark_all; + aspect = (ingame && PauseMenuCustomizeState.icon_watermark_pause != null) + ? PauseMenuCustomizeState.icon_watermark_pause_aspect + : PauseMenuCustomizeState.icon_watermark_all_aspect; + if (loc != null) { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + mc.getTextureManager().bindTexture(loc); + GlStateManager.pushMatrix(); + GlStateManager.translate(8, height - 72, 0.0f); + float f2 = 64.0f / 256.0f; + GlStateManager.scale(f2 * aspect, f2, f2); + this.drawTexturedModalRect(0, 0, 0, 0, 256, 256); + GlStateManager.popMatrix(); + } + } + } else { + this.drawBackground(i); + } + + } + + /** + * + + * Draws the background (i is always 0 as of 1.2.2) + */ + public void drawBackground(int tint) { + GlStateManager.disableLighting(); + GlStateManager.disableFog(); + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + this.mc.getTextureManager().bindTexture(optionsBackground); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + float f = 32.0F; + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); + worldrenderer.pos(0.0D, (double) this.height, 0.0D) + .tex(0.0D, (double) ((float) this.height / 32.0F + (float) tint)).color(64, 64, 64, 255).endVertex(); + worldrenderer.pos((double) this.width, (double) this.height, 0.0D) + .tex((double) ((float) this.width / 32.0F), (double) ((float) this.height / 32.0F + (float) tint)) + .color(64, 64, 64, 255).endVertex(); + worldrenderer.pos((double) this.width, 0.0D, 0.0D).tex((double) ((float) this.width / 32.0F), (double) tint) + .color(64, 64, 64, 255).endVertex(); + worldrenderer.pos(0.0D, 0.0D, 0.0D).tex(0.0D, (double) tint).color(64, 64, 64, 255).endVertex(); + tessellator.draw(); + } + + /** + * + + * Returns true if this GUI should pause the game when it is + * displayed in single-player + */ + public boolean doesGuiPauseGame() { + return true; + } + + public void confirmClicked(boolean flag, int i) { + if (i == 31102009) { + if (flag) { + this.openWebLink(this.clickedLinkURI); + } + + this.clickedLinkURI = null; + this.mc.displayGuiScreen(this); + } + + } + + private void openWebLink(String parURI) { + EagRuntime.openLink(parURI); + } + + /** + * + + * Returns true if either windows ctrl key is down or if either + * mac meta key is down + */ + public static boolean isCtrlKeyDown() { + return Minecraft.isRunningOnMac ? Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220) + : Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157); + } + + /** + * + + * Returns true if either shift key is down + */ + public static boolean isShiftKeyDown() { + return Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54); + } + + /** + * + + * Returns true if either alt key is down + */ + public static boolean isAltKeyDown() { + return Keyboard.isKeyDown(56) || Keyboard.isKeyDown(184); + } + + public static boolean isKeyComboCtrlX(int parInt1) { + return parInt1 == 45 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); + } + + public static boolean isKeyComboCtrlV(int parInt1) { + return parInt1 == 47 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); + } + + public static boolean isKeyComboCtrlC(int parInt1) { + return parInt1 == 46 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); + } + + public static boolean isKeyComboCtrlA(int parInt1) { + return parInt1 == 30 && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown(); + } + + /** + * + + * Called when the GUI is resized in order to update the world + * and the resolution + */ + public void onResize(Minecraft mcIn, int parInt1, int parInt2) { + this.setWorldAndResolution(mcIn, parInt1, parInt2); + } + + public boolean shouldHangupIntegratedServer() { + return true; + } + + public boolean blockPTTKey() { + return false; + } + + public void fireInputEvent(EnumInputEvent event, String param) { + + } + + public boolean showCopyPasteButtons() { + return false; + } + + public static int applyEaglerScale(float scaleFac, int coord, int screenDim) { + return (int) ((coord - (1.0f - scaleFac) * screenDim * 0.5f) / scaleFac); + } + + public float getEaglerScale() { + return PointerInputAbstraction.isTouchMode() ? getTouchModeScale() : 1.0f; + } + + protected float getTouchModeScale() { + return 1.0f; + } + + public boolean canCloseGui() { + return true; + } + + protected boolean isTouchDraggingStateLocked(int uid) { + return false; + } + + protected boolean shouldTouchGenerateMouseEvents() { + return true; + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenAddServer.java b/src/game/java/net/minecraft/client/gui/GuiScreenAddServer.java similarity index 76% rename from src/main/java/net/minecraft/client/gui/GuiScreenAddServer.java rename to src/game/java/net/minecraft/client/gui/GuiScreenAddServer.java index ef16695..84c2c1f 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenAddServer.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreenAddServer.java @@ -1,189 +1,220 @@ -package net.minecraft.client.gui; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.minecraft.client.multiplayer.ServerData; -import net.minecraft.client.resources.I18n; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiScreenAddServer extends GuiScreen { - private final GuiScreen parentScreen; - private final ServerData serverData; - private GuiTextField serverIPField; - private GuiTextField serverNameField; - private GuiButton serverResourcePacks; - private GuiButton hideAddress; - - public GuiScreenAddServer(GuiScreen parGuiScreen, ServerData parServerData) { - this.parentScreen = parGuiScreen; - this.serverData = parServerData; - } - - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - this.serverNameField.updateCursorCounter(); - this.serverIPField.updateCursorCounter(); - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - Keyboard.enableRepeatEvents(true); - int i = 80; - this.buttonList.clear(); - GuiButton done; - GuiButton cancel; - this.buttonList.add(done = new GuiButton(0, this.width / 2 - 100, i + 96 + 12, - I18n.format("addServer.add", new Object[0]))); - this.buttonList.add(cancel = new GuiButton(1, this.width / 2 - 100, i + 120 + 12, - I18n.format("gui.cancel", new Object[0]))); - if (EagRuntime.requireSSL()) { - done.yPosition = cancel.yPosition; - done.width = (done.width / 2) - 2; - cancel.width = (cancel.width / 2) - 2; - done.xPosition += cancel.width + 4; - } - this.buttonList.add(this.serverResourcePacks = new GuiButton(2, this.width / 2 - 100, i + 54, - I18n.format("addServer.resourcePack", new Object[0]) + ": " - + this.serverData.getResourceMode().getMotd().getFormattedText())); - this.buttonList.add(this.hideAddress = new GuiButton(3, this.width / 2 - 100, i + 78, - I18n.format("addServer.hideAddress", new Object[0]) + ": " - + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]))); - this.serverNameField = new GuiTextField(0, this.fontRendererObj, this.width / 2 - 100, 66, 200, 20); - this.serverNameField.setFocused(true); - this.serverNameField.setText(this.serverData.serverName); - this.serverIPField = new GuiTextField(1, this.fontRendererObj, this.width / 2 - 100, 106, 200, 20); - this.serverIPField.setMaxStringLength(128); - this.serverIPField.setText(this.serverData.serverIP); - ((GuiButton) this.buttonList.get(0)).enabled = this.serverIPField.getText().trim().length() > 0; - } - - /** - * + - * Called when the screen is unloaded. Used to disable keyboard - * repeat events - */ - public void onGuiClosed() { - Keyboard.enableRepeatEvents(false); - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.enabled) { - if (parGuiButton.id == 3) { - this.serverData.hideAddress = !this.serverData.hideAddress; - this.hideAddress.displayString = I18n.format("addServer.hideAddress", new Object[0]) + ": " - + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]); - } else if (parGuiButton.id == 2) { - this.serverData.setResourceMode( - ServerData.ServerResourceMode._VALUES[(this.serverData.getResourceMode().ordinal() + 1) - % ServerData.ServerResourceMode._VALUES.length]); - this.serverResourcePacks.displayString = I18n.format("addServer.resourcePack", new Object[0]) + ": " - + this.serverData.getResourceMode().getMotd().getFormattedText(); - } else if (parGuiButton.id == 1) { - this.parentScreen.confirmClicked(false, 0); - } else if (parGuiButton.id == 0) { - this.serverData.serverName = this.serverNameField.getText().trim(); - this.serverData.serverIP = this.serverIPField.getText().trim(); - this.parentScreen.confirmClicked(true, 0); - } - - } - } - - /** - * + - * Fired when a key is typed (except F11 which toggles full - * screen). This is the equivalent of - * KeyListener.keyTyped(KeyEvent e). Args : character (character - * on the key), keyCode (lwjgl Keyboard key code) - */ - protected void keyTyped(char parChar1, int parInt1) { - this.serverNameField.textboxKeyTyped(parChar1, parInt1); - this.serverIPField.textboxKeyTyped(parChar1, parInt1); - if (parInt1 == 15) { - this.serverNameField.setFocused(!this.serverNameField.isFocused()); - this.serverIPField.setFocused(!this.serverIPField.isFocused()); - } - - if (parInt1 == 28 || parInt1 == 156) { - this.actionPerformed((GuiButton) this.buttonList.get(0)); - } - - ((GuiButton) this.buttonList.get(0)).enabled = this.serverIPField.getText().trim().length() > 0; - } - - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { - super.mouseClicked(parInt1, parInt2, parInt3); - this.serverIPField.mouseClicked(parInt1, parInt2, parInt3); - this.serverNameField.mouseClicked(parInt1, parInt2, parInt3); - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - this.drawDefaultBackground(); - this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.title", new Object[0]), this.width / 2, 17, - 16777215); - this.drawString(this.fontRendererObj, I18n.format("addServer.enterName", new Object[0]), this.width / 2 - 100, - 53, 10526880); - this.drawString(this.fontRendererObj, I18n.format("addServer.enterIp", new Object[0]), this.width / 2 - 100, 94, - 10526880); - if (EagRuntime.requireSSL()) { - this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn1"), this.width / 2, 184, - 0xccccff); - this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn2"), this.width / 2, 196, - 0xccccff); - } - this.serverNameField.drawTextBox(); - this.serverIPField.drawTextBox(); - super.drawScreen(i, j, f); - } +package net.minecraft.client.gui; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.resources.I18n; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenAddServer extends GuiScreen { + private final GuiScreen parentScreen; + private final ServerData serverData; + private GuiTextField serverIPField; + private GuiTextField serverNameField; + private GuiButton serverResourcePacks; + private GuiButton hideAddress; + private GuiButton enableCookies; + + public GuiScreenAddServer(GuiScreen parGuiScreen, ServerData parServerData) { + this.parentScreen = parGuiScreen; + this.serverData = parServerData; + } + + /** + * + + * Called from the main game loop to update the screen. + */ + public void updateScreen() { + this.serverNameField.updateCursorCounter(); + this.serverIPField.updateCursorCounter(); + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + Keyboard.enableRepeatEvents(true); + int i = 80; + this.buttonList.clear(); + GuiButton done; + GuiButton cancel; + this.buttonList.add(done = new GuiButton(0, this.width / 2 - 100, i + 96 + 12, + I18n.format("addServer.add", new Object[0]))); + this.buttonList.add(cancel = new GuiButton(1, this.width / 2 - 100, i + 120 + 12, + I18n.format("gui.cancel", new Object[0]))); + if (EagRuntime.requireSSL()) { + done.yPosition = cancel.yPosition; + done.width = (done.width / 2) - 2; + cancel.width = (cancel.width / 2) - 2; + done.xPosition += cancel.width + 4; + } + this.buttonList.add(this.serverResourcePacks = new GuiButton(2, this.width / 2 - 100, i + 54, + I18n.format("addServer.resourcePack", new Object[0]) + ": " + + this.serverData.getResourceMode().getMotd().getFormattedText())); + if (EagRuntime.getConfiguration().isEnableServerCookies()) { + this.buttonList.add(this.enableCookies = new GuiButton(4, this.width / 2 - 100, i + 78, 99, 20, + I18n.format("addServer.enableCookies") + ": " + + I18n.format(this.serverData.enableCookies ? "addServer.enableCookies.enabled" + : "addServer.enableCookies.disabled"))); + this.buttonList.add(this.hideAddress = new GuiButton(3, this.width / 2 + 1, i + 78, 99, 20, + I18n.format("addServer.hideAddr", new Object[0]) + ": " + + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]))); + } else { + this.buttonList.add(this.hideAddress = new GuiButton(3, this.width / 2 - 100, i + 78, + I18n.format("addServer.hideAddress", new Object[0]) + ": " + + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]))); + } + this.serverNameField = new GuiTextField(0, this.fontRendererObj, this.width / 2 - 100, 66, 200, 20); + this.serverNameField.setFocused(true); + this.serverNameField.setText(this.serverData.serverName); + this.serverIPField = new GuiTextField(1, this.fontRendererObj, this.width / 2 - 100, 106, 200, 20); + this.serverIPField.setMaxStringLength(128); + this.serverIPField.setText(this.serverData.serverIP); + ((GuiButton) this.buttonList.get(0)).enabled = this.serverIPField.getText().trim().length() > 0; + } + + /** + * + + * Called when the screen is unloaded. Used to disable keyboard + * repeat events + */ + public void onGuiClosed() { + Keyboard.enableRepeatEvents(false); + } + + /** + * + + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + if (parGuiButton.enabled) { + if (parGuiButton.id == 3) { + this.serverData.hideAddress = !this.serverData.hideAddress; + this.hideAddress.displayString = I18n + .format(EagRuntime.getConfiguration().isEnableServerCookies() ? "addServer.hideAddr" + : "addServer.hideAddress", new Object[0]) + + ": " + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]); + } else if (parGuiButton.id == 4) { + this.serverData.enableCookies = !this.serverData.enableCookies; + this.enableCookies.displayString = I18n.format("addServer.enableCookies") + ": " + + I18n.format(this.serverData.enableCookies ? "addServer.enableCookies.enabled" + : "addServer.enableCookies.disabled"); + } else if (parGuiButton.id == 2) { + this.serverData.setResourceMode( + ServerData.ServerResourceMode._VALUES[(this.serverData.getResourceMode().ordinal() + 1) + % ServerData.ServerResourceMode._VALUES.length]); + this.serverResourcePacks.displayString = I18n.format("addServer.resourcePack", new Object[0]) + ": " + + this.serverData.getResourceMode().getMotd().getFormattedText(); + } else if (parGuiButton.id == 1) { + this.parentScreen.confirmClicked(false, 0); + } else if (parGuiButton.id == 0) { + this.serverData.serverName = this.serverNameField.getText().trim(); + this.serverData.serverIP = this.serverIPField.getText().trim(); + this.parentScreen.confirmClicked(true, 0); + } + + } + } + + /** + * + + * Fired when a key is typed (except F11 which toggles full + * screen). This is the equivalent of + * KeyListener.keyTyped(KeyEvent e). Args : character (character + * on the key), keyCode (lwjgl Keyboard key code) + */ + protected void keyTyped(char parChar1, int parInt1) { + this.serverNameField.textboxKeyTyped(parChar1, parInt1); + this.serverIPField.textboxKeyTyped(parChar1, parInt1); + if (parInt1 == 15) { + this.serverNameField.setFocused(!this.serverNameField.isFocused()); + this.serverIPField.setFocused(!this.serverIPField.isFocused()); + } + + if (parInt1 == 28 || parInt1 == 156) { + this.actionPerformed((GuiButton) this.buttonList.get(0)); + } + + ((GuiButton) this.buttonList.get(0)).enabled = this.serverIPField.getText().trim().length() > 0; + } + + /** + * + + * Called when the mouse is clicked. Args : mouseX, mouseY, + * clickedButton + */ + protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + super.mouseClicked(parInt1, parInt2, parInt3); + this.serverIPField.mouseClicked(parInt1, parInt2, parInt3); + this.serverNameField.mouseClicked(parInt1, parInt2, parInt3); + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + this.drawDefaultBackground(); + this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.title", new Object[0]), this.width / 2, 17, + 16777215); + this.drawString(this.fontRendererObj, I18n.format("addServer.enterName", new Object[0]), this.width / 2 - 100, + 53, 10526880); + this.drawString(this.fontRendererObj, I18n.format("addServer.enterIp", new Object[0]), this.width / 2 - 100, 94, + 10526880); + if (EagRuntime.requireSSL()) { + this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn1"), this.width / 2, 184, + 0xccccff); + this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn2"), this.width / 2, 196, + 0xccccff); + } + this.serverNameField.drawTextBox(); + this.serverIPField.drawTextBox(); + super.drawScreen(i, j, f); + } + + @Override + public boolean showCopyPasteButtons() { + return serverNameField.isFocused() || serverIPField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + serverNameField.fireInputEvent(event, param); + serverIPField.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenBook.java b/src/game/java/net/minecraft/client/gui/GuiScreenBook.java similarity index 96% rename from src/main/java/net/minecraft/client/gui/GuiScreenBook.java rename to src/game/java/net/minecraft/client/gui/GuiScreenBook.java index dfc7a19..3085c7d 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenBook.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreenBook.java @@ -10,6 +10,7 @@ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenVisualViewport; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; @@ -58,7 +59,7 @@ import net.minecraft.util.ResourceLocation; * POSSIBILITY OF SUCH DAMAGE. * */ -public class GuiScreenBook extends GuiScreen { +public class GuiScreenBook extends GuiScreenVisualViewport { private static final Logger logger = LogManager.getLogger(); private static final ResourceLocation bookGuiTextures = new ResourceLocation("textures/gui/book.png"); private final EntityPlayer editingPlayer; @@ -106,12 +107,8 @@ public class GuiScreenBook extends GuiScreen { } - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - super.updateScreen(); + public void updateScreen0() { + super.updateScreen0(); ++this.updateCount; } @@ -369,12 +366,7 @@ public class GuiScreenBook extends GuiScreen { } - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { + public void drawScreen0(int i, int j, float f) { GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(bookGuiTextures); int k = (this.width - this.bookImageWidth) / 2; @@ -457,15 +449,10 @@ public class GuiScreenBook extends GuiScreen { } } - super.drawScreen(i, j, f); + super.drawScreen0(i, j, f); } - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + protected void mouseClicked0(int parInt1, int parInt2, int parInt3) { if (parInt3 == 0) { IChatComponent ichatcomponent = this.func_175385_b(parInt1, parInt2); if (this.handleComponentClick(ichatcomponent)) { @@ -473,7 +460,7 @@ public class GuiScreenBook extends GuiScreen { } } - super.mouseClicked(parInt1, parInt2, parInt3); + super.mouseClicked0(parInt1, parInt2, parInt3); } /** @@ -481,7 +468,7 @@ public class GuiScreenBook extends GuiScreen { * Executes the click event specified by the given chat * component */ - protected boolean handleComponentClick(IChatComponent ichatcomponent) { + public boolean handleComponentClick(IChatComponent ichatcomponent) { ClickEvent clickevent = ichatcomponent == null ? null : ichatcomponent.getChatStyle().getChatClickEvent(); if (clickevent == null) { return false; diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenCustomizePresets.java b/src/game/java/net/minecraft/client/gui/GuiScreenCustomizePresets.java similarity index 97% rename from src/main/java/net/minecraft/client/gui/GuiScreenCustomizePresets.java rename to src/game/java/net/minecraft/client/gui/GuiScreenCustomizePresets.java index 1131eed..5e4685f 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenCustomizePresets.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreenCustomizePresets.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.List; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.minecraft.client.renderer.Tessellator; @@ -88,8 +89,12 @@ public class GuiScreenCustomizePresets extends GuiScreen { this.field_175311_g.handleMouseInput(); } - /** - * + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.field_175311_g.handleTouchInput(); + } + + /**+ * Called when the screen is unloaded. Used to disable keyboard * repeat events */ @@ -171,6 +176,16 @@ public class GuiScreenCustomizePresets extends GuiScreen { || this.field_175317_i.getText().length() > 1; } + @Override + public boolean showCopyPasteButtons() { + return field_175317_i.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + field_175317_i.fireInputEvent(event, param); + } + static { ChunkProviderSettings.Factory chunkprovidersettings$factory = ChunkProviderSettings.Factory.jsonToFactory( "{ \"coordinateScale\":684.412, \"heightScale\":684.412, \"upperLimitScale\":512.0, \"lowerLimitScale\":512.0, \"depthNoiseScaleX\":200.0, \"depthNoiseScaleZ\":200.0, \"depthNoiseScaleExponent\":0.5, \"mainNoiseScaleX\":5000.0, \"mainNoiseScaleY\":1000.0, \"mainNoiseScaleZ\":5000.0, \"baseSize\":8.5, \"stretchY\":8.0, \"biomeDepthWeight\":2.0, \"biomeDepthOffset\":0.5, \"biomeScaleWeight\":2.0, \"biomeScaleOffset\":0.375, \"useCaves\":true, \"useDungeons\":true, \"dungeonChance\":8, \"useStrongholds\":true, \"useVillages\":true, \"useMineShafts\":true, \"useTemples\":true, \"useRavines\":true, \"useWaterLakes\":true, \"waterLakeChance\":4, \"useLavaLakes\":true, \"lavaLakeChance\":80, \"useLavaOceans\":false, \"seaLevel\":255 }"); diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenOptionsSounds.java b/src/game/java/net/minecraft/client/gui/GuiScreenOptionsSounds.java similarity index 99% rename from src/main/java/net/minecraft/client/gui/GuiScreenOptionsSounds.java rename to src/game/java/net/minecraft/client/gui/GuiScreenOptionsSounds.java index 5b4f552..2a131a2 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenOptionsSounds.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreenOptionsSounds.java @@ -180,5 +180,9 @@ public class GuiScreenOptionsSounds extends GuiScreen { this.field_146155_p = false; } + + public boolean isSliderTouchEvents() { + return true; + } } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenResourcePacks.java b/src/game/java/net/minecraft/client/gui/GuiScreenResourcePacks.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiScreenResourcePacks.java rename to src/game/java/net/minecraft/client/gui/GuiScreenResourcePacks.java index 888d0a1..587aa20 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenResourcePacks.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreenResourcePacks.java @@ -114,6 +114,12 @@ public class GuiScreenResourcePacks extends GuiScreen { this.availableResourcePacksList.handleMouseInput(); } + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.selectedResourcePacksList.handleTouchInput(); + this.availableResourcePacksList.handleTouchInput(); + } + public boolean hasResourcePackEntry(ResourcePackListEntry parResourcePackListEntry) { return this.selectedResourcePacks.contains(parResourcePackListEntry); } diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenServerList.java b/src/game/java/net/minecraft/client/gui/GuiScreenServerList.java similarity index 95% rename from src/main/java/net/minecraft/client/gui/GuiScreenServerList.java rename to src/game/java/net/minecraft/client/gui/GuiScreenServerList.java index 14be2ca..15be410 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenServerList.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreenServerList.java @@ -2,6 +2,7 @@ package net.minecraft.client.gui; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.resources.I18n; @@ -154,4 +155,15 @@ public class GuiScreenServerList extends GuiScreen { this.field_146302_g.drawTextBox(); super.drawScreen(i, j, f); } + + @Override + public boolean showCopyPasteButtons() { + return field_146302_g.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + field_146302_g.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenWorking.java b/src/game/java/net/minecraft/client/gui/GuiScreenWorking.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiScreenWorking.java rename to src/game/java/net/minecraft/client/gui/GuiScreenWorking.java diff --git a/src/main/java/net/minecraft/client/gui/GuiSelectWorld.java b/src/game/java/net/minecraft/client/gui/GuiSelectWorld.java similarity index 95% rename from src/main/java/net/minecraft/client/gui/GuiSelectWorld.java rename to src/game/java/net/minecraft/client/gui/GuiSelectWorld.java index 1118b46..204a3ef 100644 --- a/src/main/java/net/minecraft/client/gui/GuiSelectWorld.java +++ b/src/game/java/net/minecraft/client/gui/GuiSelectWorld.java @@ -10,6 +10,7 @@ import java.util.Date; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANConnect; import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANNotSupported; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket1CIssueDetected; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; @@ -79,6 +80,7 @@ public class GuiSelectWorld extends GuiScreen implements GuiYesNoCallback { private GuiButton recreateButton; private boolean hasRequestedWorlds = false; private boolean waitingForWorlds = false; + private boolean ramdiskMode = false; public GuiSelectWorld(GuiScreen parentScreenIn) { this.parentScreen = parentScreenIn; @@ -92,6 +94,7 @@ public class GuiSelectWorld extends GuiScreen implements GuiYesNoCallback { * window resizes, the buttonList is cleared beforehand. */ public void initGui() { + this.ramdiskMode = SingleplayerServerController.isIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE); this.field_146628_f = I18n.format("selectWorld.title", new Object[0]); this.field_146637_u = I18n.format("selectWorld.world", new Object[0]); this.field_146636_v = I18n.format("selectWorld.conversion", new Object[0]); @@ -101,7 +104,7 @@ public class GuiSelectWorld extends GuiScreen implements GuiYesNoCallback { new Object[0]); this.field_146635_w[WorldSettings.GameType.SPECTATOR.getID()] = I18n.format("gameMode.spectator", new Object[0]); - this.field_146638_t = new GuiSelectWorld.List(this.mc); + this.field_146638_t = new GuiSelectWorld.List(this.mc, ramdiskMode ? -10 : 0); this.field_146638_t.registerScrollButtons(4, 5); this.func_146618_g(); } @@ -132,6 +135,11 @@ public class GuiSelectWorld extends GuiScreen implements GuiYesNoCallback { this.field_146638_t.handleMouseInput(); } + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.field_146638_t.handleTouchInput(); + } + private void func_146627_h() { ISaveFormat isaveformat = this.mc.getSaveLoader(); this.field_146639_s = isaveformat.getSaveList(); @@ -253,6 +261,11 @@ public class GuiSelectWorld extends GuiScreen implements GuiYesNoCallback { this.field_146638_t.drawScreen(i, j, f); this.drawCenteredString(this.fontRendererObj, this.field_146628_f, this.width / 2, 20, 16777215); + if (ramdiskMode) { + this.drawCenteredString(this.fontRendererObj, I18n.format("selectWorld.ramdiskWarning"), this.width / 2, + height - 68, 11184810); + } + GlStateManager.pushMatrix(); GlStateManager.scale(0.75f, 0.75f, 0.75f); @@ -296,8 +309,9 @@ public class GuiSelectWorld extends GuiScreen implements GuiYesNoCallback { } class List extends GuiSlot { - public List(Minecraft mcIn) { - super(mcIn, GuiSelectWorld.this.width, GuiSelectWorld.this.height, 32, GuiSelectWorld.this.height - 64, 36); + public List(Minecraft mcIn, int i) { + super(mcIn, GuiSelectWorld.this.width, GuiSelectWorld.this.height, 32, GuiSelectWorld.this.height - 64 + i, + 36); } protected int getSize() { diff --git a/src/main/java/net/minecraft/client/gui/GuiSleepMP.java b/src/game/java/net/minecraft/client/gui/GuiSleepMP.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiSleepMP.java rename to src/game/java/net/minecraft/client/gui/GuiSleepMP.java diff --git a/src/main/java/net/minecraft/client/gui/GuiSlider.java b/src/game/java/net/minecraft/client/gui/GuiSlider.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/GuiSlider.java rename to src/game/java/net/minecraft/client/gui/GuiSlider.java index 5088d91..2d400f2 100644 --- a/src/main/java/net/minecraft/client/gui/GuiSlider.java +++ b/src/game/java/net/minecraft/client/gui/GuiSlider.java @@ -158,4 +158,8 @@ public class GuiSlider extends GuiButton { public interface FormatHelper { String getText(int var1, String var2, float var3); } + + public boolean isSliderTouchEvents() { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiSlot.java b/src/game/java/net/minecraft/client/gui/GuiSlot.java similarity index 95% rename from src/main/java/net/minecraft/client/gui/GuiSlot.java rename to src/game/java/net/minecraft/client/gui/GuiSlot.java index 7597ed4..d2e0501 100644 --- a/src/main/java/net/minecraft/client/gui/GuiSlot.java +++ b/src/game/java/net/minecraft/client/gui/GuiSlot.java @@ -3,7 +3,10 @@ package net.minecraft.client.gui; import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.Touch; import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; @@ -341,9 +344,18 @@ public abstract class GuiSlot { } public void handleMouseInput() { + handleInput(Mouse.getEventButton(), Mouse.getEventButtonState(), Mouse.getDWheel()); + } + + public void handleTouchInput() { + mouseX = PointerInputAbstraction.getVCursorX() * width / mc.displayWidth; + mouseY = height - PointerInputAbstraction.getVCursorY() * height / mc.displayHeight - 1; + handleInput(0, Touch.getEventType() == EnumTouchEvent.TOUCHSTART, 0); + } + + protected void handleInput(int eventButton, boolean eventState, int dWheel) { if (this.isMouseYWithinSlotBounds(this.mouseY)) { - if (Mouse.getEventButton() == 0 && Mouse.getEventButtonState() && this.mouseY >= this.top - && this.mouseY <= this.bottom) { + if (eventButton == 0 && eventState && this.mouseY >= this.top && this.mouseY <= this.bottom) { int i = (this.width - this.getListWidth()) / 2; int j = (this.width + this.getListWidth()) / 2; int k = this.mouseY - this.top - this.headerPadding + (int) this.amountScrolled - 4; @@ -356,7 +368,7 @@ public abstract class GuiSlot { } } - if (Mouse.isButtonDown(0) && this.getEnabled()) { + if (PointerInputAbstraction.getVCursorButtonDown(0) && this.getEnabled()) { if (this.initialClickY == -1) { boolean flag1 = true; if (this.mouseY >= this.top && this.mouseY <= this.bottom) { @@ -409,15 +421,14 @@ public abstract class GuiSlot { this.initialClickY = -1; } - int i2 = Mouse.getEventDWheel(); - if (i2 != 0) { - if (i2 > 0) { - i2 = -1; - } else if (i2 < 0) { - i2 = 1; + if (dWheel != 0) { + if (dWheel > 0) { + dWheel = -1; + } else if (dWheel < 0) { + dWheel = 1; } - this.amountScrolled += (float) (i2 * this.slotHeight / 2); + this.amountScrolled += (float) (dWheel * this.slotHeight / 2); } } @@ -447,9 +458,6 @@ public abstract class GuiSlot { Tessellator tessellator = Tessellator.getInstance(); WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - int mx = Mouse.getX(); - int my = Mouse.getY(); - for (int j = 0; j < i; ++j) { int k = mouseYIn + j * this.slotHeight + this.headerPadding; int l = this.slotHeight - 4; diff --git a/src/main/java/net/minecraft/client/gui/GuiSpectator.java b/src/game/java/net/minecraft/client/gui/GuiSpectator.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiSpectator.java rename to src/game/java/net/minecraft/client/gui/GuiSpectator.java diff --git a/src/main/java/net/minecraft/client/gui/GuiTextField.java b/src/game/java/net/minecraft/client/gui/GuiTextField.java similarity index 97% rename from src/main/java/net/minecraft/client/gui/GuiTextField.java rename to src/game/java/net/minecraft/client/gui/GuiTextField.java index a63498a..c78000c 100644 --- a/src/main/java/net/minecraft/client/gui/GuiTextField.java +++ b/src/game/java/net/minecraft/client/gui/GuiTextField.java @@ -5,6 +5,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.minecraft.client.renderer.Tessellator; @@ -764,4 +765,22 @@ public class GuiTextField extends Gui { public void setVisible(boolean parFlag) { this.visible = parFlag; } + + public void fireInputEvent(EnumInputEvent clipboardPaste, String param) { + if (!isFocused) + return; + switch (clipboardPaste) { + case CLIPBOARD_COPY: + GuiScreen.setClipboardString(this.getSelectedText()); + break; + case CLIPBOARD_PASTE: + if (this.isEnabled) { + this.writeText(param != null ? param : GuiScreen.getClipboardString()); + } + break; + default: + break; + } + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiUtilRenderComponents.java b/src/game/java/net/minecraft/client/gui/GuiUtilRenderComponents.java similarity index 97% rename from src/main/java/net/minecraft/client/gui/GuiUtilRenderComponents.java rename to src/game/java/net/minecraft/client/gui/GuiUtilRenderComponents.java index 071a5bd..3831c78 100644 --- a/src/main/java/net/minecraft/client/gui/GuiUtilRenderComponents.java +++ b/src/game/java/net/minecraft/client/gui/GuiUtilRenderComponents.java @@ -46,6 +46,10 @@ public class GuiUtilRenderComponents { : parString1; } + /** + * This function is like the FontRenderer wrap function, except for chat + * components + */ public static List func_178908_a(IChatComponent parIChatComponent, int parInt1, FontRenderer parFontRenderer, boolean parFlag, boolean parFlag2) { int i = 0; diff --git a/src/main/java/net/minecraft/client/gui/GuiVideoSettings.java b/src/game/java/net/minecraft/client/gui/GuiVideoSettings.java similarity index 70% rename from src/main/java/net/minecraft/client/gui/GuiVideoSettings.java rename to src/game/java/net/minecraft/client/gui/GuiVideoSettings.java index 4be077f..7e6c876 100644 --- a/src/main/java/net/minecraft/client/gui/GuiVideoSettings.java +++ b/src/game/java/net/minecraft/client/gui/GuiVideoSettings.java @@ -1,149 +1,195 @@ -package net.minecraft.client.gui; - -import java.io.IOException; - -import net.minecraft.client.resources.I18n; -import net.minecraft.client.settings.GameSettings; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiVideoSettings extends GuiScreen { - private GuiScreen parentGuiScreen; - protected String screenTitle = "Video Settings"; - private GameSettings guiGameSettings; - private GuiListExtended optionsRowList; - /** - * + - * An array of all of GameSettings.Options's video options. - */ - private static final GameSettings.Options[] videoOptions = new GameSettings.Options[] { - GameSettings.Options.GRAPHICS, GameSettings.Options.RENDER_DISTANCE, GameSettings.Options.AMBIENT_OCCLUSION, - GameSettings.Options.FRAMERATE_LIMIT, GameSettings.Options.EAGLER_VSYNC, GameSettings.Options.ANAGLYPH, - GameSettings.Options.VIEW_BOBBING, GameSettings.Options.GUI_SCALE, GameSettings.Options.GAMMA, - GameSettings.Options.RENDER_CLOUDS, GameSettings.Options.PARTICLES, GameSettings.Options.FXAA, - GameSettings.Options.MIPMAP_LEVELS, GameSettings.Options.BLOCK_ALTERNATIVES, - GameSettings.Options.ENTITY_SHADOWS, GameSettings.Options.FOG, GameSettings.Options.EAGLER_DYNAMIC_LIGHTS, - GameSettings.Options.FULLSCREEN, GameSettings.Options.FNAW_SKINS, GameSettings.Options.HUD_FPS, - GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER, GameSettings.Options.HUD_STATS, - GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H, GameSettings.Options.CHUNK_FIX }; - - public GuiVideoSettings(GuiScreen parentScreenIn, GameSettings gameSettingsIn) { - this.parentGuiScreen = parentScreenIn; - this.guiGameSettings = gameSettingsIn; - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - this.screenTitle = I18n.format("options.videoTitle", new Object[0]); - this.buttonList.clear(); - this.buttonList.add( - new GuiButton(200, this.width / 2 - 100, this.height - 27, I18n.format("gui.done", new Object[0]))); - this.optionsRowList = new GuiOptionsRowList(this.mc, this.width, this.height, 32, this.height - 32, 25, - videoOptions); - } - - /** - * + - * Handles mouse input. - */ - public void handleMouseInput() throws IOException { - super.handleMouseInput(); - this.optionsRowList.handleMouseInput(); - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.enabled) { - if (parGuiButton.id == 200) { - this.mc.gameSettings.saveOptions(); - this.mc.displayGuiScreen(this.parentGuiScreen); - } - - } - } - - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { - int i = this.guiGameSettings.guiScale; - super.mouseClicked(parInt1, parInt2, parInt3); - this.optionsRowList.mouseClicked(parInt1, parInt2, parInt3); - if (this.guiGameSettings.guiScale != i) { - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - int j = scaledresolution.getScaledWidth(); - int k = scaledresolution.getScaledHeight(); - this.setWorldAndResolution(this.mc, j, k); - this.mc.voiceOverlay.setResolution(j, k); - } - - } - - /** - * + - * Called when a mouse button is released. Args : mouseX, - * mouseY, releaseButton - */ - protected void mouseReleased(int i, int j, int k) { - int l = this.guiGameSettings.guiScale; - super.mouseReleased(i, j, k); - this.optionsRowList.mouseReleased(i, j, k); - if (this.guiGameSettings.guiScale != l) { - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - int i1 = scaledresolution.getScaledWidth(); - int j1 = scaledresolution.getScaledHeight(); - this.setWorldAndResolution(this.mc, i1, j1); - } - - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - this.drawDefaultBackground(); - this.optionsRowList.drawScreen(i, j, f); - this.drawCenteredString(this.fontRendererObj, this.screenTitle, this.width / 2, 5, 16777215); - super.drawScreen(i, j, f); - } +package net.minecraft.client.gui; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.Display; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.dynamiclights.DynamicLightsStateManager; +import net.lax1dude.eaglercraft.v1_8.recording.ScreenRecordingController; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.GameSettings; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiVideoSettings extends GuiScreen { + private GuiScreen parentGuiScreen; + protected String screenTitle = "Video Settings"; + private GameSettings guiGameSettings; + private GuiListExtended optionsRowList; + private boolean vsyncLock = false; + /** + * + + * An array of all of GameSettings.Options's video options. + */ + private static final GameSettings.Options[] videoOptions = new GameSettings.Options[] { + GameSettings.Options.GRAPHICS, GameSettings.Options.RENDER_DISTANCE, GameSettings.Options.AMBIENT_OCCLUSION, + GameSettings.Options.FRAMERATE_LIMIT, GameSettings.Options.EAGLER_VSYNC, GameSettings.Options.ANAGLYPH, + GameSettings.Options.VIEW_BOBBING, GameSettings.Options.GUI_SCALE, GameSettings.Options.GAMMA, + GameSettings.Options.RENDER_CLOUDS, GameSettings.Options.PARTICLES, GameSettings.Options.FXAA, + GameSettings.Options.MIPMAP_LEVELS, GameSettings.Options.BLOCK_ALTERNATIVES, + GameSettings.Options.ENTITY_SHADOWS, GameSettings.Options.FOG, GameSettings.Options.EAGLER_DYNAMIC_LIGHTS, + GameSettings.Options.FULLSCREEN, GameSettings.Options.FNAW_SKINS, GameSettings.Options.HUD_FPS, + GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER, GameSettings.Options.HUD_STATS, + GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H, GameSettings.Options.CHUNK_FIX }; + + public GuiVideoSettings(GuiScreen parentScreenIn, GameSettings gameSettingsIn) { + this.parentGuiScreen = parentScreenIn; + this.guiGameSettings = gameSettingsIn; + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + this.screenTitle = I18n.format("options.videoTitle", new Object[0]); + this.buttonList.clear(); + this.buttonList.add( + new GuiButton(200, this.width / 2 - 100, this.height - 27, I18n.format("gui.done", new Object[0]))); + this.optionsRowList = new GuiOptionsRowList(this.mc, this.width, this.height, 32, this.height - 32, 25, + videoOptions); + if (!DynamicLightsStateManager.isSupported()) { + GuiOptionButton btn = ((GuiOptionsRowList) optionsRowList) + .getButtonFor(GameSettings.Options.EAGLER_DYNAMIC_LIGHTS); + if (btn != null) { + btn.enabled = false; + } + } + if (EaglercraftGPU.checkOpenGLESVersion() < 300) { + GuiOptionSlider btn = ((GuiOptionsRowList) optionsRowList).getSliderFor(GameSettings.Options.MIPMAP_LEVELS); + if (btn != null) { + btn.displayString = I18n.format(GameSettings.Options.MIPMAP_LEVELS.getEnumString()) + ": N/A"; + btn.sliderValue = 0.0f; + btn.enabled = false; + } + } + if (!Display.supportsFullscreen()) { + GuiOptionButton btn = ((GuiOptionsRowList) optionsRowList).getButtonFor(GameSettings.Options.FULLSCREEN); + if (btn != null) { + btn.displayString = I18n.format(GameSettings.Options.FULLSCREEN.getEnumString()) + ": " + + I18n.format("options.off"); + btn.enabled = false; + } + } + } + + /** + * + + * Handles mouse input. + */ + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + this.optionsRowList.handleMouseInput(); + } + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.optionsRowList.handleTouchInput(); + } + + /**+ + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + if (parGuiButton.enabled) { + if (parGuiButton.id == 200) { + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(this.parentGuiScreen); + } + + } + } + + /** + * + + * Called when the mouse is clicked. Args : mouseX, mouseY, + * clickedButton + */ + protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + int i = this.guiGameSettings.guiScale; + super.mouseClicked(parInt1, parInt2, parInt3); + this.optionsRowList.mouseClicked(parInt1, parInt2, parInt3); + if (this.guiGameSettings.guiScale != i) { + ScaledResolution scaledresolution = mc.scaledResolution = new ScaledResolution(mc); + int j = scaledresolution.getScaledWidth(); + int k = scaledresolution.getScaledHeight(); + this.setWorldAndResolution(this.mc, j, k); + this.mc.voiceOverlay.setResolution(j, k); + this.mc.notifRenderer.setResolution(this.mc, j, k, scaledresolution.getScaleFactor()); + } + + } + + /** + * + + * Called when a mouse button is released. Args : mouseX, + * mouseY, releaseButton + */ + protected void mouseReleased(int i, int j, int k) { + int l = this.guiGameSettings.guiScale; + super.mouseReleased(i, j, k); + this.optionsRowList.mouseReleased(i, j, k); + if (this.guiGameSettings.guiScale != l) { + ScaledResolution scaledresolution = mc.scaledResolution = new ScaledResolution(mc); + int i1 = scaledresolution.getScaledWidth(); + int j1 = scaledresolution.getScaledHeight(); + this.setWorldAndResolution(this.mc, i1, j1); + } + + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + this.drawDefaultBackground(); + this.optionsRowList.drawScreen(i, j, f); + this.drawCenteredString(this.fontRendererObj, this.screenTitle, this.width / 2, 5, 16777215); + super.drawScreen(i, j, f); + } + + @Override + public void updateScreen() { + boolean vsyncLockEn = ScreenRecordingController.isVSyncLocked(); + if (vsyncLockEn != vsyncLock) { + vsyncLock = vsyncLockEn; + GuiOptionButton btn = ((GuiOptionsRowList) optionsRowList).getButtonFor(GameSettings.Options.EAGLER_VSYNC); + if (btn != null) { + btn.enabled = !vsyncLockEn; + } + } + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiWinGame.java b/src/game/java/net/minecraft/client/gui/GuiWinGame.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiWinGame.java rename to src/game/java/net/minecraft/client/gui/GuiWinGame.java diff --git a/src/main/java/net/minecraft/client/gui/GuiYesNo.java b/src/game/java/net/minecraft/client/gui/GuiYesNo.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiYesNo.java rename to src/game/java/net/minecraft/client/gui/GuiYesNo.java diff --git a/src/main/java/net/minecraft/client/gui/GuiYesNoCallback.java b/src/game/java/net/minecraft/client/gui/GuiYesNoCallback.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/GuiYesNoCallback.java rename to src/game/java/net/minecraft/client/gui/GuiYesNoCallback.java diff --git a/src/main/java/net/minecraft/client/gui/IProgressMeter.java b/src/game/java/net/minecraft/client/gui/IProgressMeter.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/IProgressMeter.java rename to src/game/java/net/minecraft/client/gui/IProgressMeter.java diff --git a/src/main/java/net/minecraft/client/gui/MapItemRenderer.java b/src/game/java/net/minecraft/client/gui/MapItemRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/MapItemRenderer.java rename to src/game/java/net/minecraft/client/gui/MapItemRenderer.java diff --git a/src/main/java/net/minecraft/client/gui/ScaledResolution.java b/src/game/java/net/minecraft/client/gui/ScaledResolution.java similarity index 93% rename from src/main/java/net/minecraft/client/gui/ScaledResolution.java rename to src/game/java/net/minecraft/client/gui/ScaledResolution.java index f4988b3..1651f2b 100644 --- a/src/main/java/net/minecraft/client/gui/ScaledResolution.java +++ b/src/game/java/net/minecraft/client/gui/ScaledResolution.java @@ -39,6 +39,10 @@ public class ScaledResolution { private int scaledHeight; private int scaleFactor; + /** + * EAGLER NOTE: This constructor is deprecated! Use + * Minecraft.getMinecraft().scaledResolution + */ public ScaledResolution(Minecraft parMinecraft) { this.scaledWidth = parMinecraft.displayWidth; this.scaledHeight = parMinecraft.displayHeight; @@ -49,6 +53,8 @@ public class ScaledResolution { i = 1000; } + i = Math.round(i * Math.max(parMinecraft.displayDPI, 0.5f)); + while (this.scaleFactor < i && this.scaledWidth / (this.scaleFactor + 1) >= 320 && this.scaledHeight / (this.scaleFactor + 1) >= 240) { ++this.scaleFactor; diff --git a/src/main/java/net/minecraft/client/gui/ScreenChatOptions.java b/src/game/java/net/minecraft/client/gui/ScreenChatOptions.java similarity index 79% rename from src/main/java/net/minecraft/client/gui/ScreenChatOptions.java rename to src/game/java/net/minecraft/client/gui/ScreenChatOptions.java index c4c2406..5f33432 100644 --- a/src/main/java/net/minecraft/client/gui/ScreenChatOptions.java +++ b/src/game/java/net/minecraft/client/gui/ScreenChatOptions.java @@ -1,5 +1,6 @@ package net.minecraft.client.gui; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.minecraft.client.resources.I18n; import net.minecraft.client.settings.GameSettings; @@ -34,6 +35,12 @@ import net.minecraft.client.settings.GameSettings; */ public class ScreenChatOptions extends GuiScreen { private static final GameSettings.Options[] field_146399_a = new GameSettings.Options[] { + GameSettings.Options.CHAT_VISIBILITY, GameSettings.Options.CHAT_COLOR, GameSettings.Options.CHAT_LINKS, + GameSettings.Options.CHAT_OPACITY, GameSettings.Options.CHAT_LINKS_PROMPT, GameSettings.Options.CHAT_SCALE, + GameSettings.Options.CHAT_HEIGHT_FOCUSED, GameSettings.Options.CHAT_HEIGHT_UNFOCUSED, + GameSettings.Options.CHAT_WIDTH, GameSettings.Options.REDUCED_DEBUG_INFO, + GameSettings.Options.EAGLER_PROFANITY_FILTER }; + private static final GameSettings.Options[] no_profanity_filter = new GameSettings.Options[] { GameSettings.Options.CHAT_VISIBILITY, GameSettings.Options.CHAT_COLOR, GameSettings.Options.CHAT_LINKS, GameSettings.Options.CHAT_OPACITY, GameSettings.Options.CHAT_LINKS_PROMPT, GameSettings.Options.CHAT_SCALE, GameSettings.Options.CHAT_HEIGHT_FOCUSED, GameSettings.Options.CHAT_HEIGHT_UNFOCUSED, @@ -57,8 +64,10 @@ public class ScreenChatOptions extends GuiScreen { int i = 0; this.field_146401_i = I18n.format("options.chat.title", new Object[0]); - for (int j = 0; j < field_146399_a.length; ++j) { - GameSettings.Options gamesettings$options = field_146399_a[j]; + boolean profanityFilterForce = EagRuntime.getConfiguration().isForceProfanityFilter(); + GameSettings.Options[] opts = profanityFilterForce ? no_profanity_filter : field_146399_a; + for (int j = 0; j < opts.length; ++j) { + GameSettings.Options gamesettings$options = opts[j]; if (gamesettings$options.getEnumFloat()) { this.buttonList.add(new GuiOptionSlider(gamesettings$options.returnEnumOrdinal(), this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), gamesettings$options)); @@ -71,8 +80,8 @@ public class ScreenChatOptions extends GuiScreen { ++i; } - this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height / 6 + 120, - I18n.format("gui.done", new Object[0]))); + this.buttonList.add(new GuiButton(200, this.width / 2 - 100, + this.height / 6 + (profanityFilterForce ? 130 : 154), I18n.format("gui.done", new Object[0]))); } /** diff --git a/src/main/java/net/minecraft/client/gui/ServerListEntryNormal.java b/src/game/java/net/minecraft/client/gui/ServerListEntryNormal.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/ServerListEntryNormal.java rename to src/game/java/net/minecraft/client/gui/ServerListEntryNormal.java diff --git a/src/main/java/net/minecraft/client/gui/ServerSelectionList.java b/src/game/java/net/minecraft/client/gui/ServerSelectionList.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/ServerSelectionList.java rename to src/game/java/net/minecraft/client/gui/ServerSelectionList.java diff --git a/src/main/java/net/minecraft/client/gui/achievement/GuiAchievement.java b/src/game/java/net/minecraft/client/gui/achievement/GuiAchievement.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/achievement/GuiAchievement.java rename to src/game/java/net/minecraft/client/gui/achievement/GuiAchievement.java index 1ca287e..361e616 100644 --- a/src/main/java/net/minecraft/client/gui/achievement/GuiAchievement.java +++ b/src/game/java/net/minecraft/client/gui/achievement/GuiAchievement.java @@ -83,7 +83,7 @@ public class GuiAchievement extends Gui { GlStateManager.loadIdentity(); this.width = this.mc.displayWidth; this.height = this.mc.displayHeight; - ScaledResolution scaledresolution = new ScaledResolution(this.mc); + ScaledResolution scaledresolution = mc.scaledResolution; this.width = scaledresolution.getScaledWidth(); this.height = scaledresolution.getScaledHeight(); GlStateManager.clear(GL_DEPTH_BUFFER_BIT); diff --git a/src/main/java/net/minecraft/client/gui/achievement/GuiAchievements.java b/src/game/java/net/minecraft/client/gui/achievement/GuiAchievements.java similarity index 99% rename from src/main/java/net/minecraft/client/gui/achievement/GuiAchievements.java rename to src/game/java/net/minecraft/client/gui/achievement/GuiAchievements.java index 59bed45..b49ee80 100644 --- a/src/main/java/net/minecraft/client/gui/achievement/GuiAchievements.java +++ b/src/game/java/net/minecraft/client/gui/achievement/GuiAchievements.java @@ -5,6 +5,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.block.Block; @@ -134,7 +135,7 @@ public class GuiAchievements extends GuiScreen implements IProgressMeter { lanSearchStates[(int) (Minecraft.getSystemTime() / 150L % (long) lanSearchStates.length)], this.width / 2, this.height / 2 + this.fontRendererObj.FONT_HEIGHT * 2, 16777215); } else { - if (Mouse.isButtonDown(0)) { + if (PointerInputAbstraction.getVCursorButtonDown(0)) { int k = (this.width - this.field_146555_f) / 2; int l = (this.height - this.field_146557_g) / 2; int i1 = k + 8; diff --git a/src/main/java/net/minecraft/client/gui/achievement/GuiStats.java b/src/game/java/net/minecraft/client/gui/achievement/GuiStats.java similarity index 98% rename from src/main/java/net/minecraft/client/gui/achievement/GuiStats.java rename to src/game/java/net/minecraft/client/gui/achievement/GuiStats.java index 491303a..5dc97ed 100644 --- a/src/main/java/net/minecraft/client/gui/achievement/GuiStats.java +++ b/src/game/java/net/minecraft/client/gui/achievement/GuiStats.java @@ -7,7 +7,7 @@ import java.util.List; import com.google.common.collect.Lists; -import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.minecraft.client.Minecraft; @@ -104,6 +104,13 @@ public class GuiStats extends GuiScreen implements IProgressMeter { } + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + if (this.displaySlot != null) { + this.displaySlot.handleTouchInput(); + } + } + public void func_175366_f() { this.generalStats = new GuiStats.StatsGeneral(this.mc); this.generalStats.registerScrollButtons(1, 1); @@ -279,7 +286,7 @@ public class GuiStats extends GuiScreen implements IProgressMeter { } protected void drawListHeader(int i, int j, Tessellator var3) { - if (!Mouse.isButtonDown(0)) { + if (!PointerInputAbstraction.getVCursorButtonDown(0)) { this.field_148218_l = -1; } diff --git a/src/main/java/net/minecraft/client/gui/inventory/CreativeCrafting.java b/src/game/java/net/minecraft/client/gui/inventory/CreativeCrafting.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/CreativeCrafting.java rename to src/game/java/net/minecraft/client/gui/inventory/CreativeCrafting.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiBeacon.java b/src/game/java/net/minecraft/client/gui/inventory/GuiBeacon.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiBeacon.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiBeacon.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiBrewingStand.java b/src/game/java/net/minecraft/client/gui/inventory/GuiBrewingStand.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiBrewingStand.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiBrewingStand.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiChest.java b/src/game/java/net/minecraft/client/gui/inventory/GuiChest.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiChest.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiChest.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java b/src/game/java/net/minecraft/client/gui/inventory/GuiContainer.java similarity index 89% rename from src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiContainer.java index 660151a..40bbc60 100644 --- a/src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java +++ b/src/game/java/net/minecraft/client/gui/inventory/GuiContainer.java @@ -1,683 +1,740 @@ -package net.minecraft.client.gui.inventory; - -import java.util.List; -import java.util.Set; - -import com.google.common.collect.Sets; - -import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.inventory.Container; -import net.minecraft.inventory.Slot; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.MathHelper; -import net.minecraft.util.ResourceLocation; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public abstract class GuiContainer extends GuiScreen { - /** - * + - * The location of the inventory background texture - */ - protected static final ResourceLocation inventoryBackground = new ResourceLocation( - "textures/gui/container/inventory.png"); - /** - * + - * The X size of the inventory window in pixels. - */ - protected int xSize = 176; - /** - * + - * The Y size of the inventory window in pixels. - */ - protected int ySize = 166; - public Container inventorySlots; - protected int guiLeft; - protected int guiTop; - private Slot theSlot; - private Slot clickedSlot; - private boolean isRightMouseClick; - private ItemStack draggedStack; - private int touchUpX; - private int touchUpY; - private Slot returningStackDestSlot; - private long returningStackTime; - private ItemStack returningStack; - private Slot currentDragTargetSlot; - private long dragItemDropDelay; - protected final Set dragSplittingSlots = Sets.newHashSet(); - protected boolean dragSplitting; - private int dragSplittingLimit; - private int dragSplittingButton; - private boolean ignoreMouseUp; - private int dragSplittingRemnant; - private long lastClickTime; - private Slot lastClickSlot; - private int lastClickButton; - private boolean doubleClick; - private ItemStack shiftClickedSlot; - - public GuiContainer(Container inventorySlotsIn) { - this.inventorySlots = inventorySlotsIn; - this.ignoreMouseUp = true; - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - super.initGui(); - this.mc.thePlayer.openContainer = this.inventorySlots; - this.guiLeft = (this.width - this.xSize) / 2; - this.guiTop = (this.height - this.ySize) / 2; - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - this.drawDefaultBackground(); - int k = this.guiLeft; - int l = this.guiTop; - this.drawGuiContainerBackgroundLayer(f, i, j); - GlStateManager.disableRescaleNormal(); - RenderHelper.disableStandardItemLighting(); - GlStateManager.disableLighting(); - GlStateManager.disableDepth(); - super.drawScreen(i, j, f); - RenderHelper.enableGUIStandardItemLighting(); - GlStateManager.pushMatrix(); - GlStateManager.translate((float) k, (float) l, 0.0F); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.enableRescaleNormal(); - this.theSlot = null; - short short1 = 240; - short short2 = 240; - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) short1 / 1.0F, - (float) short2 / 1.0F); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - - for (int i1 = 0; i1 < this.inventorySlots.inventorySlots.size(); ++i1) { - Slot slot = (Slot) this.inventorySlots.inventorySlots.get(i1); - this.drawSlot(slot); - if (this.isMouseOverSlot(slot, i, j) && slot.canBeHovered()) { - this.theSlot = slot; - GlStateManager.disableLighting(); - GlStateManager.disableDepth(); - int j1 = slot.xDisplayPosition; - int k1 = slot.yDisplayPosition; - GlStateManager.colorMask(true, true, true, false); - this.drawGradientRect(j1, k1, j1 + 16, k1 + 16, -2130706433, -2130706433); - GlStateManager.colorMask(true, true, true, true); - GlStateManager.enableLighting(); - GlStateManager.enableDepth(); - } - GlStateManager.enableAlpha(); - } - - RenderHelper.disableStandardItemLighting(); - this.drawGuiContainerForegroundLayer(i, j); - RenderHelper.enableGUIStandardItemLighting(); - InventoryPlayer inventoryplayer = this.mc.thePlayer.inventory; - ItemStack itemstack = this.draggedStack == null ? inventoryplayer.getItemStack() : this.draggedStack; - if (itemstack != null) { - byte b0 = 8; - int j2 = this.draggedStack == null ? 8 : 16; - String s = null; - if (this.draggedStack != null && this.isRightMouseClick) { - itemstack = itemstack.copy(); - itemstack.stackSize = MathHelper.ceiling_float_int((float) itemstack.stackSize / 2.0F); - } else if (this.dragSplitting && this.dragSplittingSlots.size() > 1) { - itemstack = itemstack.copy(); - itemstack.stackSize = this.dragSplittingRemnant; - if (itemstack.stackSize == 0) { - s = "" + EnumChatFormatting.YELLOW + "0"; - } - } - - this.drawItemStack(itemstack, i - k - b0, j - l - j2, s); - } - - if (this.returningStack != null) { - float f1 = (float) (Minecraft.getSystemTime() - this.returningStackTime) / 100.0F; - if (f1 >= 1.0F) { - f1 = 1.0F; - this.returningStack = null; - } - - int k2 = this.returningStackDestSlot.xDisplayPosition - this.touchUpX; - int l2 = this.returningStackDestSlot.yDisplayPosition - this.touchUpY; - int l1 = this.touchUpX + (int) ((float) k2 * f1); - int i2 = this.touchUpY + (int) ((float) l2 * f1); - this.drawItemStack(this.returningStack, l1, i2, (String) null); - } - - GlStateManager.popMatrix(); - if (inventoryplayer.getItemStack() == null && this.theSlot != null && this.theSlot.getHasStack()) { - ItemStack itemstack1 = this.theSlot.getStack(); - this.renderToolTip(itemstack1, i, j); - } - - GlStateManager.enableLighting(); - GlStateManager.enableDepth(); - RenderHelper.enableStandardItemLighting(); - } - - /** - * + - * Render an ItemStack. Args : stack, x, y, format - */ - private void drawItemStack(ItemStack stack, int x, int y, String altText) { - GlStateManager.translate(0.0F, 0.0F, 32.0F); - this.zLevel = 200.0F; - this.itemRender.zLevel = 200.0F; - this.itemRender.renderItemAndEffectIntoGUI(stack, x, y); - this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, stack, x, - y - (this.draggedStack == null ? 0 : 8), altText); - this.zLevel = 0.0F; - this.itemRender.zLevel = 0.0F; - } - - /** - * + - * Draw the foreground layer for the GuiContainer (everything in - * front of the items). Args : mouseX, mouseY - */ - protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { - } - - protected abstract void drawGuiContainerBackgroundLayer(float var1, int var2, int var3); - - private void drawSlot(Slot slotIn) { - int i = slotIn.xDisplayPosition; - int j = slotIn.yDisplayPosition; - ItemStack itemstack = slotIn.getStack(); - boolean flag = false; - boolean flag1 = slotIn == this.clickedSlot && this.draggedStack != null && !this.isRightMouseClick; - ItemStack itemstack1 = this.mc.thePlayer.inventory.getItemStack(); - String s = null; - if (slotIn == this.clickedSlot && this.draggedStack != null && this.isRightMouseClick && itemstack != null) { - itemstack = itemstack.copy(); - itemstack.stackSize /= 2; - } else if (this.dragSplitting && this.dragSplittingSlots.contains(slotIn) && itemstack1 != null) { - if (this.dragSplittingSlots.size() == 1) { - return; - } - - if (Container.canAddItemToSlot(slotIn, itemstack1, true) && this.inventorySlots.canDragIntoSlot(slotIn)) { - itemstack = itemstack1.copy(); - flag = true; - Container.computeStackSize(this.dragSplittingSlots, this.dragSplittingLimit, itemstack, - slotIn.getStack() == null ? 0 : slotIn.getStack().stackSize); - if (itemstack.stackSize > itemstack.getMaxStackSize()) { - s = EnumChatFormatting.YELLOW + "" + itemstack.getMaxStackSize(); - itemstack.stackSize = itemstack.getMaxStackSize(); - } - - if (itemstack.stackSize > slotIn.getItemStackLimit(itemstack)) { - s = EnumChatFormatting.YELLOW + "" + slotIn.getItemStackLimit(itemstack); - itemstack.stackSize = slotIn.getItemStackLimit(itemstack); - } - } else { - this.dragSplittingSlots.remove(slotIn); - this.updateDragSplitting(); - } - } - - this.zLevel = 100.0F; - this.itemRender.zLevel = 100.0F; - if (itemstack == null) { - String s1 = slotIn.getSlotTexture(); - if (s1 != null) { - EaglerTextureAtlasSprite textureatlassprite = this.mc.getTextureMapBlocks().getAtlasSprite(s1); - GlStateManager.disableLighting(); - this.mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); - this.drawTexturedModalRect(i, j, textureatlassprite, 16, 16); - GlStateManager.enableLighting(); - flag1 = true; - } - } - - if (!flag1) { - if (flag) { - drawRect(i, j, i + 16, j + 16, -2130706433); - } - - GlStateManager.enableDepth(); - this.itemRender.renderItemAndEffectIntoGUI(itemstack, i, j); - this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, itemstack, i, j, s); - } - - this.itemRender.zLevel = 0.0F; - this.zLevel = 0.0F; - } - - private void updateDragSplitting() { - ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack(); - if (itemstack != null && this.dragSplitting) { - this.dragSplittingRemnant = itemstack.stackSize; - - for (Slot slot : this.dragSplittingSlots) { - ItemStack itemstack1 = itemstack.copy(); - int i = slot.getStack() == null ? 0 : slot.getStack().stackSize; - Container.computeStackSize(this.dragSplittingSlots, this.dragSplittingLimit, itemstack1, i); - if (itemstack1.stackSize > itemstack1.getMaxStackSize()) { - itemstack1.stackSize = itemstack1.getMaxStackSize(); - } - - if (itemstack1.stackSize > slot.getItemStackLimit(itemstack1)) { - itemstack1.stackSize = slot.getItemStackLimit(itemstack1); - } - - this.dragSplittingRemnant -= itemstack1.stackSize - i; - } - - } - } - - /** - * + - * Returns the slot at the given coordinates or null if there is - * none. - */ - private Slot getSlotAtPosition(int x, int y) { - for (int i = 0; i < this.inventorySlots.inventorySlots.size(); ++i) { - Slot slot = (Slot) this.inventorySlots.inventorySlots.get(i); - if (this.isMouseOverSlot(slot, x, y)) { - return slot; - } - } - - return null; - } - - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { - super.mouseClicked(parInt1, parInt2, parInt3); - boolean flag = parInt3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100; - Slot slot = this.getSlotAtPosition(parInt1, parInt2); - long i = Minecraft.getSystemTime(); - this.doubleClick = this.lastClickSlot == slot && i - this.lastClickTime < 250L - && this.lastClickButton == parInt3; - this.ignoreMouseUp = false; - if (parInt3 == 0 || parInt3 == 1 || flag) { - int j = this.guiLeft; - int k = this.guiTop; - boolean flag1 = parInt1 < j || parInt2 < k || parInt1 >= j + this.xSize || parInt2 >= k + this.ySize; - int l = -1; - if (slot != null) { - l = slot.slotNumber; - } - - if (flag1) { - l = -999; - } - - if (this.mc.gameSettings.touchscreen && flag1 && this.mc.thePlayer.inventory.getItemStack() == null) { - this.mc.displayGuiScreen((GuiScreen) null); - return; - } - - if (l != -1) { - if (this.mc.gameSettings.touchscreen) { - if (slot != null && slot.getHasStack()) { - this.clickedSlot = slot; - this.draggedStack = null; - this.isRightMouseClick = parInt3 == 1; - } else { - this.clickedSlot = null; - } - } else if (!this.dragSplitting) { - if (this.mc.thePlayer.inventory.getItemStack() == null) { - if (parInt3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) { - this.handleMouseClick(slot, l, parInt3, 3); - } else { - boolean flag2 = l != -999 && (Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54)); - byte b0 = 0; - if (flag2) { - this.shiftClickedSlot = slot != null && slot.getHasStack() ? slot.getStack() : null; - b0 = 1; - } else if (l == -999) { - b0 = 4; - } - - this.handleMouseClick(slot, l, parInt3, b0); - } - - this.ignoreMouseUp = true; - } else { - this.dragSplitting = true; - this.dragSplittingButton = parInt3; - this.dragSplittingSlots.clear(); - if (parInt3 == 0) { - this.dragSplittingLimit = 0; - } else if (parInt3 == 1) { - this.dragSplittingLimit = 1; - } else if (parInt3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) { - this.dragSplittingLimit = 2; - } - } - } - } - } - - this.lastClickSlot = slot; - this.lastClickTime = i; - this.lastClickButton = parInt3; - } - - /** - * + - * Called when a mouse button is pressed and the mouse is moved - * around. Parameters are : mouseX, mouseY, lastButtonClicked & - * timeSinceMouseClick. - */ - protected void mouseClickMove(int i, int j, int k, long var4) { - Slot slot = this.getSlotAtPosition(i, j); - ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack(); - if (this.clickedSlot != null && this.mc.gameSettings.touchscreen) { - if (k == 0 || k == 1) { - if (this.draggedStack == null) { - if (slot != this.clickedSlot && this.clickedSlot.getStack() != null) { - this.draggedStack = this.clickedSlot.getStack().copy(); - } - } else if (this.draggedStack.stackSize > 1 && slot != null - && Container.canAddItemToSlot(slot, this.draggedStack, false)) { - long l = Minecraft.getSystemTime(); - if (this.currentDragTargetSlot == slot) { - if (l - this.dragItemDropDelay > 500L) { - this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, 0, 0); - this.handleMouseClick(slot, slot.slotNumber, 1, 0); - this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, 0, 0); - this.dragItemDropDelay = l + 750L; - --this.draggedStack.stackSize; - } - } else { - this.currentDragTargetSlot = slot; - this.dragItemDropDelay = l; - } - } - } - } else if (this.dragSplitting && slot != null && itemstack != null - && itemstack.stackSize > this.dragSplittingSlots.size() - && Container.canAddItemToSlot(slot, itemstack, true) && slot.isItemValid(itemstack) - && this.inventorySlots.canDragIntoSlot(slot)) { - this.dragSplittingSlots.add(slot); - this.updateDragSplitting(); - } - - } - - /** - * + - * Called when a mouse button is released. Args : mouseX, - * mouseY, releaseButton - */ - protected void mouseReleased(int i, int j, int k) { - Slot slot = this.getSlotAtPosition(i, j); - int l = this.guiLeft; - int i1 = this.guiTop; - boolean flag = i < l || j < i1 || i >= l + this.xSize || j >= i1 + this.ySize; - int j1 = -1; - if (slot != null) { - j1 = slot.slotNumber; - } - - if (flag) { - j1 = -999; - } - - if (this.doubleClick && slot != null && k == 0 && this.inventorySlots.canMergeSlot((ItemStack) null, slot)) { - if (isShiftKeyDown()) { - if (slot != null && slot.inventory != null && this.shiftClickedSlot != null) { - List lst = this.inventorySlots.inventorySlots; - for (int n = 0, m = lst.size(); n < m; ++n) { - Slot slot2 = lst.get(n); - if (slot2 != null && slot2.canTakeStack(this.mc.thePlayer) && slot2.getHasStack() - && slot2.inventory == slot.inventory - && Container.canAddItemToSlot(slot2, this.shiftClickedSlot, true)) { - this.handleMouseClick(slot2, slot2.slotNumber, k, 1); - } - } - } - } else { - this.handleMouseClick(slot, j1, k, 6); - } - - this.doubleClick = false; - this.lastClickTime = 0L; - } else { - if (this.dragSplitting && this.dragSplittingButton != k) { - this.dragSplitting = false; - this.dragSplittingSlots.clear(); - this.ignoreMouseUp = true; - return; - } - - if (this.ignoreMouseUp) { - this.ignoreMouseUp = false; - return; - } - - if (this.clickedSlot != null && this.mc.gameSettings.touchscreen) { - if (k == 0 || k == 1) { - if (this.draggedStack == null && slot != this.clickedSlot) { - this.draggedStack = this.clickedSlot.getStack(); - } - - boolean flag2 = Container.canAddItemToSlot(slot, this.draggedStack, false); - if (j1 != -1 && this.draggedStack != null && flag2) { - this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, k, 0); - this.handleMouseClick(slot, j1, 0, 0); - if (this.mc.thePlayer.inventory.getItemStack() != null) { - this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, k, 0); - this.touchUpX = i - l; - this.touchUpY = j - i1; - this.returningStackDestSlot = this.clickedSlot; - this.returningStack = this.draggedStack; - this.returningStackTime = Minecraft.getSystemTime(); - } else { - this.returningStack = null; - } - } else if (this.draggedStack != null) { - this.touchUpX = i - l; - this.touchUpY = j - i1; - this.returningStackDestSlot = this.clickedSlot; - this.returningStack = this.draggedStack; - this.returningStackTime = Minecraft.getSystemTime(); - } - - this.draggedStack = null; - this.clickedSlot = null; - } - } else if (this.dragSplitting && !this.dragSplittingSlots.isEmpty()) { - this.handleMouseClick((Slot) null, -999, Container.func_94534_d(0, this.dragSplittingLimit), 5); - - for (Slot slot1 : this.dragSplittingSlots) { - this.handleMouseClick(slot1, slot1.slotNumber, Container.func_94534_d(1, this.dragSplittingLimit), - 5); - } - - this.handleMouseClick((Slot) null, -999, Container.func_94534_d(2, this.dragSplittingLimit), 5); - } else if (this.mc.thePlayer.inventory.getItemStack() != null) { - if (k == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) { - this.handleMouseClick(slot, j1, k, 3); - } else { - boolean flag1 = j1 != -999 && (Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54)); - if (flag1) { - this.shiftClickedSlot = slot != null && slot.getHasStack() ? slot.getStack() : null; - } - - this.handleMouseClick(slot, j1, k, flag1 ? 1 : 0); - } - } - } - - if (this.mc.thePlayer.inventory.getItemStack() == null) { - this.lastClickTime = 0L; - } - - this.dragSplitting = false; - } - - /** - * + - * Returns if the passed mouse position is over the specified - * slot. Args : slot, mouseX, mouseY - */ - private boolean isMouseOverSlot(Slot slotIn, int mouseX, int mouseY) { - return this.isPointInRegion(slotIn.xDisplayPosition, slotIn.yDisplayPosition, 16, 16, mouseX, mouseY); - } - - /** - * + - * Test if the 2D point is in a rectangle (relative to the GUI). - * Args : rectX, rectY, rectWidth, rectHeight, pointX, pointY - */ - protected boolean isPointInRegion(int left, int top, int right, int bottom, int pointX, int pointY) { - int i = this.guiLeft; - int j = this.guiTop; - pointX = pointX - i; - pointY = pointY - j; - return pointX >= left - 1 && pointX < left + right + 1 && pointY >= top - 1 && pointY < top + bottom + 1; - } - - /** - * + - * Called when the mouse is clicked over a slot or outside the - * gui. - */ - protected void handleMouseClick(Slot slotIn, int slotId, int clickedButton, int clickType) { - if (slotIn != null) { - slotId = slotIn.slotNumber; - } - - this.mc.playerController.windowClick(this.inventorySlots.windowId, slotId, clickedButton, clickType, - this.mc.thePlayer); - } - - /** - * + - * Fired when a key is typed (except F11 which toggles full - * screen). This is the equivalent of - * KeyListener.keyTyped(KeyEvent e). Args : character (character - * on the key), keyCode (lwjgl Keyboard key code) - */ - protected void keyTyped(char parChar1, int parInt1) { - if (parInt1 == this.mc.gameSettings.keyBindClose.getKeyCode() - || parInt1 == this.mc.gameSettings.keyBindInventory.getKeyCode() - || (parInt1 == 1 && (this.mc.gameSettings.keyBindClose.getKeyCode() == 0 || this.mc.areKeysLocked()))) { - this.mc.thePlayer.closeScreen(); - if (this.mc.currentScreen == null) { - this.mc.setIngameFocus(); - } - } else if (parInt1 == 1) { - showingCloseKey = System.currentTimeMillis(); - } else { - this.checkHotbarKeys(parInt1); - if (this.theSlot != null && this.theSlot.getHasStack()) { - if (parInt1 == this.mc.gameSettings.keyBindPickBlock.getKeyCode()) { - this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, 0, 3); - } else if (parInt1 == this.mc.gameSettings.keyBindDrop.getKeyCode()) { - this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, isCtrlKeyDown() ? 1 : 0, 4); - } - } - } - } - - /** - * + - * This function is what controls the hotbar shortcut check when - * you press a number key when hovering a stack. Args : keyCode, - * Returns true if a Hotbar key is pressed, else false - */ - protected boolean checkHotbarKeys(int keyCode) { - if (this.mc.thePlayer.inventory.getItemStack() == null && this.theSlot != null) { - for (int i = 0; i < 9; ++i) { - if (keyCode == this.mc.gameSettings.keyBindsHotbar[i].getKeyCode()) { - this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, i, 2); - return true; - } - } - } - - return false; - } - - /** - * + - * Called when the screen is unloaded. Used to disable keyboard - * repeat events - */ - public void onGuiClosed() { - if (this.mc.thePlayer != null) { - this.inventorySlots.onContainerClosed(this.mc.thePlayer); - } - } - - /** - * + - * Returns true if this GUI should pause the game when it is - * displayed in single-player - */ - public boolean doesGuiPauseGame() { - return false; - } - - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - super.updateScreen(); - if (!this.mc.thePlayer.isEntityAlive() || this.mc.thePlayer.isDead) { - this.mc.thePlayer.closeScreen(); - } - - } +package net.minecraft.client.gui.inventory; + +import java.util.List; +import java.util.Set; + +import com.google.common.collect.Sets; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class GuiContainer extends GuiScreen { + /** + * + + * The location of the inventory background texture + */ + protected static final ResourceLocation inventoryBackground = new ResourceLocation( + "textures/gui/container/inventory.png"); + /** + * + + * The X size of the inventory window in pixels. + */ + protected int xSize = 176; + /** + * + + * The Y size of the inventory window in pixels. + */ + protected int ySize = 166; + public Container inventorySlots; + protected int guiLeft; + protected int guiTop; + private Slot theSlot; + private Slot clickedSlot; + private boolean isRightMouseClick; + private ItemStack draggedStack; + private int touchUpX; + private int touchUpY; + private Slot returningStackDestSlot; + private long returningStackTime; + private ItemStack returningStack; + private Slot currentDragTargetSlot; + private long dragItemDropDelay; + protected final Set dragSplittingSlots = Sets.newHashSet(); + protected boolean dragSplitting; + private int dragSplittingLimit; + private int dragSplittingButton; + private boolean ignoreMouseUp; + private int dragSplittingRemnant; + private long lastClickTime; + private Slot lastClickSlot; + private int lastClickButton; + private boolean doubleClick; + private ItemStack shiftClickedSlot; + + public GuiContainer(Container inventorySlotsIn) { + this.inventorySlots = inventorySlotsIn; + this.ignoreMouseUp = true; + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + super.initGui(); + if (primaryTouchPoint != -1 && Touch.fetchPointIdx(primaryTouchPoint) == -1) { + primaryTouchPoint = -1; + mouseReleased(lastTouchX, lastTouchY, 0); + } + this.mc.thePlayer.openContainer = this.inventorySlots; + this.guiLeft = (this.width - this.xSize) / 2; + this.guiTop = (this.height - this.ySize) / 2; + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + this.drawDefaultBackground(); + int k = this.guiLeft; + int l = this.guiTop; + this.drawGuiContainerBackgroundLayer(f, i, j); + GlStateManager.disableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + super.drawScreen(i, j, f); + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.pushMatrix(); + GlStateManager.translate((float) k, (float) l, 0.0F); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.enableRescaleNormal(); + this.theSlot = null; + short short1 = 240; + short short2 = 240; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) short1 / 1.0F, + (float) short2 / 1.0F); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + for (int i1 = 0; i1 < this.inventorySlots.inventorySlots.size(); ++i1) { + Slot slot = (Slot) this.inventorySlots.inventorySlots.get(i1); + this.drawSlot(slot); + if (!this.mc.gameSettings.touchscreen && slot.canBeHovered() && this.isMouseOverSlot(slot, i, j)) { + this.theSlot = slot; + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + int j1 = slot.xDisplayPosition; + int k1 = slot.yDisplayPosition; + GlStateManager.colorMask(true, true, true, false); + this.drawGradientRect(j1, k1, j1 + 16, k1 + 16, -2130706433, -2130706433); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + } + GlStateManager.enableAlpha(); + } + + RenderHelper.disableStandardItemLighting(); + this.drawGuiContainerForegroundLayer(i, j); + RenderHelper.enableGUIStandardItemLighting(); + InventoryPlayer inventoryplayer = this.mc.thePlayer.inventory; + ItemStack itemstack = this.draggedStack == null ? inventoryplayer.getItemStack() : this.draggedStack; + if (itemstack != null) { + byte b0 = 8; + int j2 = this.draggedStack == null ? 8 : 16; + String s = null; + if (this.draggedStack != null && this.isRightMouseClick) { + itemstack = itemstack.copy(); + itemstack.stackSize = MathHelper.ceiling_float_int((float) itemstack.stackSize / 2.0F); + } else if (this.dragSplitting && this.dragSplittingSlots.size() > 1) { + itemstack = itemstack.copy(); + itemstack.stackSize = this.dragSplittingRemnant; + if (itemstack.stackSize == 0) { + s = "" + EnumChatFormatting.YELLOW + "0"; + } + } + this.drawItemStack(itemstack, i - k - b0, j - l - j2, s); + } + + if (this.returningStack != null) { + float f1 = (float) (Minecraft.getSystemTime() - this.returningStackTime) / 100.0F; + if (f1 >= 1.0F) { + f1 = 1.0F; + this.returningStack = null; + } + + int k2 = this.returningStackDestSlot.xDisplayPosition - this.touchUpX; + int l2 = this.returningStackDestSlot.yDisplayPosition - this.touchUpY; + int l1 = this.touchUpX + (int) ((float) k2 * f1); + int i2 = this.touchUpY + (int) ((float) l2 * f1); + this.drawItemStack(this.returningStack, l1, i2, (String) null); + } + + GlStateManager.popMatrix(); + if (!this.mc.gameSettings.touchscreen && inventoryplayer.getItemStack() == null && this.theSlot != null + && this.theSlot.getHasStack()) { + ItemStack itemstack1 = this.theSlot.getStack(); + this.renderToolTip(itemstack1, i, j); + } + + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + RenderHelper.enableStandardItemLighting(); + } + + /** + * + + * Render an ItemStack. Args : stack, x, y, format + */ + private void drawItemStack(ItemStack stack, int x, int y, String altText) { + GlStateManager.translate(0.0F, 0.0F, 32.0F); + this.zLevel = 200.0F; + this.itemRender.zLevel = 200.0F; + this.itemRender.renderItemAndEffectIntoGUI(stack, x, y); + this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, stack, x, + y - (this.draggedStack == null ? 0 : 8), altText); + this.zLevel = 0.0F; + this.itemRender.zLevel = 0.0F; + } + + /** + * + + * Draw the foreground layer for the GuiContainer (everything in + * front of the items). Args : mouseX, mouseY + */ + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + } + + protected abstract void drawGuiContainerBackgroundLayer(float var1, int var2, int var3); + + private void drawSlot(Slot slotIn) { + int i = slotIn.xDisplayPosition; + int j = slotIn.yDisplayPosition; + ItemStack itemstack = slotIn.getStack(); + boolean flag = false; + boolean flag1 = slotIn == this.clickedSlot && this.draggedStack != null && !this.isRightMouseClick; + ItemStack itemstack1 = this.mc.thePlayer.inventory.getItemStack(); + String s = null; + if (slotIn == this.clickedSlot && this.draggedStack != null && this.isRightMouseClick && itemstack != null) { + itemstack = itemstack.copy(); + itemstack.stackSize /= 2; + } else if (this.dragSplitting && this.dragSplittingSlots.contains(slotIn) && itemstack1 != null) { + if (this.dragSplittingSlots.size() == 1) { + return; + } + + if (Container.canAddItemToSlot(slotIn, itemstack1, true) && this.inventorySlots.canDragIntoSlot(slotIn)) { + itemstack = itemstack1.copy(); + flag = true; + Container.computeStackSize(this.dragSplittingSlots, this.dragSplittingLimit, itemstack, + slotIn.getStack() == null ? 0 : slotIn.getStack().stackSize); + if (itemstack.stackSize > itemstack.getMaxStackSize()) { + s = EnumChatFormatting.YELLOW + "" + itemstack.getMaxStackSize(); + itemstack.stackSize = itemstack.getMaxStackSize(); + } + + if (itemstack.stackSize > slotIn.getItemStackLimit(itemstack)) { + s = EnumChatFormatting.YELLOW + "" + slotIn.getItemStackLimit(itemstack); + itemstack.stackSize = slotIn.getItemStackLimit(itemstack); + } + } else { + this.dragSplittingSlots.remove(slotIn); + this.updateDragSplitting(); + } + } + + this.zLevel = 100.0F; + this.itemRender.zLevel = 100.0F; + if (itemstack == null) { + String s1 = slotIn.getSlotTexture(); + if (s1 != null) { + EaglerTextureAtlasSprite textureatlassprite = this.mc.getTextureMapBlocks().getAtlasSprite(s1); + GlStateManager.disableLighting(); + this.mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); + this.drawTexturedModalRect(i, j, textureatlassprite, 16, 16); + GlStateManager.enableLighting(); + flag1 = true; + } + } + + if (!flag1) { + if (flag) { + drawRect(i, j, i + 16, j + 16, -2130706433); + } + + GlStateManager.enableDepth(); + this.itemRender.renderItemAndEffectIntoGUI(itemstack, i, j); + this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, itemstack, i, j, s); + } + + this.itemRender.zLevel = 0.0F; + this.zLevel = 0.0F; + } + + private void updateDragSplitting() { + ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack(); + if (itemstack != null && this.dragSplitting) { + this.dragSplittingRemnant = itemstack.stackSize; + + for (Slot slot : this.dragSplittingSlots) { + ItemStack itemstack1 = itemstack.copy(); + int i = slot.getStack() == null ? 0 : slot.getStack().stackSize; + Container.computeStackSize(this.dragSplittingSlots, this.dragSplittingLimit, itemstack1, i); + if (itemstack1.stackSize > itemstack1.getMaxStackSize()) { + itemstack1.stackSize = itemstack1.getMaxStackSize(); + } + + if (itemstack1.stackSize > slot.getItemStackLimit(itemstack1)) { + itemstack1.stackSize = slot.getItemStackLimit(itemstack1); + } + + this.dragSplittingRemnant -= itemstack1.stackSize - i; + } + + } + } + + /** + * + + * Returns the slot at the given coordinates or null if there is + * none. + */ + private Slot getSlotAtPosition(int x, int y) { + for (int i = 0; i < this.inventorySlots.inventorySlots.size(); ++i) { + Slot slot = (Slot) this.inventorySlots.inventorySlots.get(i); + if (this.isMouseOverSlot(slot, x, y)) { + return slot; + } + } + + return null; + } + + /** + * + + * Called when the mouse is clicked. Args : mouseX, mouseY, + * clickedButton + */ + protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + super.mouseClicked(parInt1, parInt2, parInt3); + boolean flag = parInt3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100; + Slot slot = this.getSlotAtPosition(parInt1, parInt2); + long i = Minecraft.getSystemTime(); + this.doubleClick = this.lastClickSlot == slot && i - this.lastClickTime < 250L + && this.lastClickButton == parInt3; + this.ignoreMouseUp = false; + if (parInt3 == 0 || parInt3 == 1 || flag) { + int j = this.guiLeft; + int k = this.guiTop; + boolean flag1 = parInt1 < j || parInt2 < k || parInt1 >= j + this.xSize || parInt2 >= k + this.ySize; + int l = -1; + if (slot != null) { + l = slot.slotNumber; + } + + if (flag1) { + l = -999; + } + +// if (this.mc.gameSettings.touchscreen && flag1 && this.mc.thePlayer.inventory.getItemStack() == null) { +// this.mc.displayGuiScreen((GuiScreen) null); +// return; +// } + + if (l != -1) { + if (this.mc.gameSettings.touchscreen) { + if (slot != null && slot.getHasStack()) { + this.clickedSlot = slot; + this.draggedStack = null; + this.isRightMouseClick = parInt3 == 1; + } else { + this.clickedSlot = null; + } + } else if (!this.dragSplitting) { + if (this.mc.thePlayer.inventory.getItemStack() == null) { + if (parInt3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) { + this.handleMouseClick(slot, l, parInt3, 3); + } else { + boolean flag2 = l != -999 && (Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54)); + byte b0 = 0; + if (flag2) { + this.shiftClickedSlot = slot != null && slot.getHasStack() ? slot.getStack() : null; + b0 = 1; + } else if (l == -999) { + b0 = 4; + } + + this.handleMouseClick(slot, l, parInt3, b0); + } + + this.ignoreMouseUp = true; + } else { + this.dragSplitting = true; + this.dragSplittingButton = parInt3; + this.dragSplittingSlots.clear(); + if (parInt3 == 0) { + this.dragSplittingLimit = 0; + } else if (parInt3 == 1) { + this.dragSplittingLimit = 1; + } else if (parInt3 == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) { + this.dragSplittingLimit = 2; + } + } + } + } + } + + this.lastClickSlot = slot; + this.lastClickTime = i; + this.lastClickButton = parInt3; + } + + /** + * + + * Called when a mouse button is pressed and the mouse is moved + * around. Parameters are : mouseX, mouseY, lastButtonClicked & + * timeSinceMouseClick. + */ + protected void mouseClickMove(int i, int j, int k, long var4) { + Slot slot = this.getSlotAtPosition(i, j); + ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack(); + if (this.clickedSlot != null && this.mc.gameSettings.touchscreen) { + if (k == 0 || k == 1) { + if (this.draggedStack == null) { + if (slot != this.clickedSlot && this.clickedSlot.getStack() != null) { + this.draggedStack = this.clickedSlot.getStack().copy(); + } + } else if (this.draggedStack.stackSize > 1 && slot != null + && Container.canAddItemToSlot(slot, this.draggedStack, false)) { + long l = Minecraft.getSystemTime(); + if (this.currentDragTargetSlot == slot) { + if (l - this.dragItemDropDelay > 500L) { + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, 0, 0); + this.handleMouseClick(slot, slot.slotNumber, 1, 0); + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, 0, 0); + this.dragItemDropDelay = l + 750L; + --this.draggedStack.stackSize; + } + } else { + this.currentDragTargetSlot = slot; + this.dragItemDropDelay = l; + } + } + } + } else if (this.dragSplitting && slot != null && itemstack != null + && itemstack.stackSize > this.dragSplittingSlots.size() + && Container.canAddItemToSlot(slot, itemstack, true) && slot.isItemValid(itemstack) + && this.inventorySlots.canDragIntoSlot(slot)) { + this.dragSplittingSlots.add(slot); + this.updateDragSplitting(); + } + + } + + /** + * + + * Called when a mouse button is released. Args : mouseX, + * mouseY, releaseButton + */ + protected void mouseReleased(int i, int j, int k) { + Slot slot = this.getSlotAtPosition(i, j); + int l = this.guiLeft; + int i1 = this.guiTop; + boolean flag = i < l || j < i1 || i >= l + this.xSize || j >= i1 + this.ySize; + int j1 = -1; + if (slot != null) { + j1 = slot.slotNumber; + } + + if (flag) { + j1 = -999; + } + + if (this.doubleClick && slot != null && k == 0 && this.inventorySlots.canMergeSlot((ItemStack) null, slot)) { + if (isShiftKeyDown()) { + if (slot != null && slot.inventory != null && this.shiftClickedSlot != null) { + List lst = this.inventorySlots.inventorySlots; + for (int n = 0, m = lst.size(); n < m; ++n) { + Slot slot2 = lst.get(n); + if (slot2 != null && slot2.canTakeStack(this.mc.thePlayer) && slot2.getHasStack() + && slot2.inventory == slot.inventory + && Container.canAddItemToSlot(slot2, this.shiftClickedSlot, true)) { + this.handleMouseClick(slot2, slot2.slotNumber, k, 1); + } + } + } + } else { + this.handleMouseClick(slot, j1, k, 6); + } + + this.doubleClick = false; + this.lastClickTime = 0L; + } else { + if (this.dragSplitting && this.dragSplittingButton != k) { + this.dragSplitting = false; + this.dragSplittingSlots.clear(); + this.ignoreMouseUp = true; + return; + } + + if (this.ignoreMouseUp) { + this.ignoreMouseUp = false; + return; + } + + if (this.clickedSlot != null && this.mc.gameSettings.touchscreen) { + if (k == 0 || k == 1) { + if (this.draggedStack == null && slot != this.clickedSlot) { + this.draggedStack = this.clickedSlot.getStack(); + } + + boolean flag2 = Container.canAddItemToSlot(slot, this.draggedStack, false); + if (j1 != -1 && this.draggedStack != null && flag2) { + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, k, 0); + this.handleMouseClick(slot, j1, 0, 0); + if (this.mc.thePlayer.inventory.getItemStack() != null) { + this.handleMouseClick(this.clickedSlot, this.clickedSlot.slotNumber, k, 0); + this.touchUpX = i - l; + this.touchUpY = j - i1; + this.returningStackDestSlot = this.clickedSlot; + this.returningStack = this.draggedStack; + this.returningStackTime = Minecraft.getSystemTime(); + } else { + this.returningStack = null; + } + } else if (this.draggedStack != null) { + this.touchUpX = i - l; + this.touchUpY = j - i1; + this.returningStackDestSlot = this.clickedSlot; + this.returningStack = this.draggedStack; + this.returningStackTime = Minecraft.getSystemTime(); + } + + this.draggedStack = null; + this.clickedSlot = null; + } + } else if (this.dragSplitting && !this.dragSplittingSlots.isEmpty()) { + this.handleMouseClick((Slot) null, -999, Container.func_94534_d(0, this.dragSplittingLimit), 5); + + for (Slot slot1 : this.dragSplittingSlots) { + this.handleMouseClick(slot1, slot1.slotNumber, Container.func_94534_d(1, this.dragSplittingLimit), + 5); + } + + this.handleMouseClick((Slot) null, -999, Container.func_94534_d(2, this.dragSplittingLimit), 5); + } else if (this.mc.thePlayer.inventory.getItemStack() != null) { + if (k == this.mc.gameSettings.keyBindPickBlock.getKeyCode() + 100) { + this.handleMouseClick(slot, j1, k, 3); + } else { + boolean flag1 = j1 != -999 && (Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54)); + if (flag1) { + this.shiftClickedSlot = slot != null && slot.getHasStack() ? slot.getStack() : null; + } + + this.handleMouseClick(slot, j1, k, flag1 ? 1 : 0); + } + } + } + + if (this.mc.thePlayer.inventory.getItemStack() == null) { + this.lastClickTime = 0L; + } + + this.dragSplitting = false; + } + + /** + * + + * Returns if the passed mouse position is over the specified + * slot. Args : slot, mouseX, mouseY + */ + private boolean isMouseOverSlot(Slot slotIn, int mouseX, int mouseY) { + return this.isPointInRegion(slotIn.xDisplayPosition, slotIn.yDisplayPosition, 16, 16, mouseX, mouseY); + } + + /** + * + + * Test if the 2D point is in a rectangle (relative to the GUI). + * Args : rectX, rectY, rectWidth, rectHeight, pointX, pointY + */ + protected boolean isPointInRegion(int left, int top, int right, int bottom, int pointX, int pointY) { + int i = this.guiLeft; + int j = this.guiTop; + pointX = pointX - i; + pointY = pointY - j; + return pointX >= left - 1 && pointX < left + right + 1 && pointY >= top - 1 && pointY < top + bottom + 1; + } + + /** + * + + * Called when the mouse is clicked over a slot or outside the + * gui. + */ + protected void handleMouseClick(Slot slotIn, int slotId, int clickedButton, int clickType) { + if (slotIn != null) { + slotId = slotIn.slotNumber; + } + + this.mc.playerController.windowClick(this.inventorySlots.windowId, slotId, clickedButton, clickType, + this.mc.thePlayer); + } + + /** + * + + * Fired when a key is typed (except F11 which toggles full + * screen). This is the equivalent of + * KeyListener.keyTyped(KeyEvent e). Args : character (character + * on the key), keyCode (lwjgl Keyboard key code) + */ + protected void keyTyped(char parChar1, int parInt1) { + if (parInt1 == this.mc.gameSettings.keyBindClose.getKeyCode() + || parInt1 == this.mc.gameSettings.keyBindInventory.getKeyCode() + || (parInt1 == 1 && (this.mc.gameSettings.keyBindClose.getKeyCode() == 0 || this.mc.areKeysLocked()))) { + this.mc.thePlayer.closeScreen(); + if (this.mc.currentScreen == null) { + this.mc.setIngameFocus(); + } + } else if (parInt1 == 1) { + showingCloseKey = EagRuntime.steadyTimeMillis(); + } else { + this.checkHotbarKeys(parInt1); + if (this.theSlot != null && this.theSlot.getHasStack()) { + if (parInt1 == this.mc.gameSettings.keyBindPickBlock.getKeyCode()) { + this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, 0, 3); + } else if (parInt1 == this.mc.gameSettings.keyBindDrop.getKeyCode()) { + this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, isCtrlKeyDown() ? 1 : 0, 4); + } + } + } + } + + /** + * + + * This function is what controls the hotbar shortcut check when + * you press a number key when hovering a stack. Args : keyCode, + * Returns true if a Hotbar key is pressed, else false + */ + protected boolean checkHotbarKeys(int keyCode) { + if (this.mc.thePlayer.inventory.getItemStack() == null && this.theSlot != null) { + for (int i = 0; i < 9; ++i) { + if (keyCode == this.mc.gameSettings.keyBindsHotbar[i].getKeyCode()) { + this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, i, 2); + return true; + } + } + } + + return false; + } + + /** + * + + * Called when the screen is unloaded. Used to disable keyboard + * repeat events + */ + public void onGuiClosed() { + if (this.mc.thePlayer != null) { + this.inventorySlots.onContainerClosed(this.mc.thePlayer); + } + } + + /** + * + + * Returns true if this GUI should pause the game when it is + * displayed in single-player + */ + public boolean doesGuiPauseGame() { + return false; + } + + /** + * + + * Called from the main game loop to update the screen. + */ + public void updateScreen() { + super.updateScreen(); + if (!this.mc.thePlayer.isEntityAlive() || this.mc.thePlayer.isDead) { + this.mc.thePlayer.closeScreen(); + return; + } + if (primaryTouchPoint != -1 && Touch.fetchPointIdx(primaryTouchPoint) == -1) { + primaryTouchPoint = -1; + mouseReleased(lastTouchX, lastTouchY, 0); + } + } + + protected float getTouchModeScale() { + return 1.25f; + } + + private int primaryTouchPoint = -1; + private int lastTouchX = -1; + private int lastTouchY = -1; + + protected void touchStarted(int touchX, int touchY, int uid) { + if (primaryTouchPoint == -1) { + primaryTouchPoint = uid; + lastTouchX = touchX; + lastTouchY = touchY; + mouseClicked(touchX, touchY, 0); + } + } + + protected void touchMoved(int touchX, int touchY, int uid) { + if (primaryTouchPoint == uid) { + lastTouchX = touchX; + lastTouchY = touchY; + mouseClickMove(touchX, touchY, 0, 0l); + } + } + + protected void touchEndMove(int touchX, int touchY, int uid) { + if (primaryTouchPoint == uid) { + primaryTouchPoint = -1; + lastTouchX = touchX; + lastTouchY = touchY; + mouseReleased(touchX, touchY, 0); + } + } + + protected void touchTapped(int touchX, int touchY, int uid) { + if (primaryTouchPoint == uid) { + primaryTouchPoint = -1; + lastTouchX = touchX; + lastTouchY = touchY; + mouseReleased(touchX, touchY, 0); + } + } + + protected boolean shouldTouchGenerateMouseEvents() { + return false; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java b/src/game/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java similarity index 93% rename from src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java index a65faab..15192a0 100644 --- a/src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java +++ b/src/game/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java @@ -1,886 +1,915 @@ -package net.minecraft.client.gui.inventory; - -import java.io.IOException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import com.google.common.collect.Lists; - -import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.lax1dude.eaglercraft.v1_8.Mouse; -import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiTextField; -import net.minecraft.client.gui.achievement.GuiAchievements; -import net.minecraft.client.gui.achievement.GuiStats; -import net.minecraft.client.renderer.InventoryEffectRenderer; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.resources.I18n; -import net.minecraft.client.settings.GameSettings; -import net.minecraft.creativetab.CreativeTabs; -import net.minecraft.enchantment.Enchantment; -import net.minecraft.enchantment.EnchantmentHelper; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.init.Items; -import net.minecraft.inventory.Container; -import net.minecraft.inventory.IInventory; -import net.minecraft.inventory.InventoryBasic; -import net.minecraft.inventory.Slot; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.MathHelper; -import net.minecraft.util.ResourceLocation; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiContainerCreative extends InventoryEffectRenderer { - /** - * + - * The location of the creative inventory tabs texture - */ - private static final ResourceLocation creativeInventoryTabs = new ResourceLocation( - "textures/gui/container/creative_inventory/tabs.png"); - private static InventoryBasic field_147060_v = new InventoryBasic("tmp", true, 45); - /** - * + - * Currently selected creative inventory tab index. - */ - private static int selectedTabIndex = CreativeTabs.tabBlock.getTabIndex(); - private float currentScroll; - private boolean isScrolling; - private boolean wasClicking; - private GuiTextField searchField; - private List field_147063_B; - private Slot field_147064_C; - private boolean field_147057_D; - private CreativeCrafting field_147059_E; - - public GuiContainerCreative(EntityPlayer parEntityPlayer) { - super(new GuiContainerCreative.ContainerCreative(parEntityPlayer)); - parEntityPlayer.openContainer = this.inventorySlots; - this.allowUserInput = true; - this.ySize = 136; - this.xSize = 195; - } - - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - if (!this.mc.playerController.isInCreativeMode()) { - this.mc.displayGuiScreen(new GuiInventory(this.mc.thePlayer)); - } - - this.updateActivePotionEffects(); - } - - /** - * + - * Called when the mouse is clicked over a slot or outside the - * gui. - */ - protected void handleMouseClick(Slot slot, int i, int j, int k) { - this.field_147057_D = true; - boolean flag = k == 1; - k = i == -999 && k == 0 ? 4 : k; - if (slot == null && selectedTabIndex != CreativeTabs.tabInventory.getTabIndex() && k != 5) { - InventoryPlayer inventoryplayer1 = this.mc.thePlayer.inventory; - if (inventoryplayer1.getItemStack() != null) { - if (j == 0) { - this.mc.thePlayer.dropPlayerItemWithRandomChoice(inventoryplayer1.getItemStack(), true); - this.mc.playerController.sendPacketDropItem(inventoryplayer1.getItemStack()); - inventoryplayer1.setItemStack((ItemStack) null); - } - - if (j == 1) { - ItemStack itemstack5 = inventoryplayer1.getItemStack().splitStack(1); - this.mc.thePlayer.dropPlayerItemWithRandomChoice(itemstack5, true); - this.mc.playerController.sendPacketDropItem(itemstack5); - if (inventoryplayer1.getItemStack().stackSize == 0) { - inventoryplayer1.setItemStack((ItemStack) null); - } - } - } - } else if (slot == this.field_147064_C && flag) { - for (int i1 = 0; i1 < this.mc.thePlayer.inventoryContainer.getInventory().size(); ++i1) { - this.mc.playerController.sendSlotPacket((ItemStack) null, i1); - } - } else if (selectedTabIndex == CreativeTabs.tabInventory.getTabIndex()) { - if (slot == this.field_147064_C) { - this.mc.thePlayer.inventory.setItemStack((ItemStack) null); - } else if (k == 4 && slot != null && slot.getHasStack()) { - ItemStack itemstack = slot.decrStackSize(j == 0 ? 1 : slot.getStack().getMaxStackSize()); - this.mc.thePlayer.dropPlayerItemWithRandomChoice(itemstack, true); - this.mc.playerController.sendPacketDropItem(itemstack); - } else if (k == 4 && this.mc.thePlayer.inventory.getItemStack() != null) { - this.mc.thePlayer.dropPlayerItemWithRandomChoice(this.mc.thePlayer.inventory.getItemStack(), true); - this.mc.playerController.sendPacketDropItem(this.mc.thePlayer.inventory.getItemStack()); - this.mc.thePlayer.inventory.setItemStack((ItemStack) null); - } else { - this.mc.thePlayer.inventoryContainer.slotClick( - slot == null ? i : ((GuiContainerCreative.CreativeSlot) slot).slot.slotNumber, j, k, - this.mc.thePlayer); - this.mc.thePlayer.inventoryContainer.detectAndSendChanges(); - } - } else if (k != 5 && slot.inventory == field_147060_v) { - InventoryPlayer inventoryplayer = this.mc.thePlayer.inventory; - ItemStack itemstack1 = inventoryplayer.getItemStack(); - ItemStack itemstack2 = slot.getStack(); - if (k == 2) { - if (itemstack2 != null && j >= 0 && j < 9) { - ItemStack itemstack7 = itemstack2.copy(); - itemstack7.stackSize = itemstack7.getMaxStackSize(); - this.mc.thePlayer.inventory.setInventorySlotContents(j, itemstack7); - this.mc.thePlayer.inventoryContainer.detectAndSendChanges(); - } - - return; - } - - if (k == 3) { - if (inventoryplayer.getItemStack() == null && slot.getHasStack()) { - ItemStack itemstack6 = slot.getStack().copy(); - itemstack6.stackSize = itemstack6.getMaxStackSize(); - inventoryplayer.setItemStack(itemstack6); - } - - return; - } - - if (k == 4) { - if (itemstack2 != null) { - ItemStack itemstack3 = itemstack2.copy(); - itemstack3.stackSize = j == 0 ? 1 : itemstack3.getMaxStackSize(); - this.mc.thePlayer.dropPlayerItemWithRandomChoice(itemstack3, true); - this.mc.playerController.sendPacketDropItem(itemstack3); - } - - return; - } - - if (itemstack1 != null && itemstack2 != null && itemstack1.isItemEqual(itemstack2)) { - if (j == 0) { - if (flag) { - itemstack1.stackSize = itemstack1.getMaxStackSize(); - } else if (itemstack1.stackSize < itemstack1.getMaxStackSize()) { - ++itemstack1.stackSize; - } - } else if (itemstack1.stackSize <= 1) { - inventoryplayer.setItemStack((ItemStack) null); - } else { - --itemstack1.stackSize; - } - } else if (itemstack2 != null && itemstack1 == null) { - inventoryplayer.setItemStack(ItemStack.copyItemStack(itemstack2)); - itemstack1 = inventoryplayer.getItemStack(); - if (flag) { - itemstack1.stackSize = itemstack1.getMaxStackSize(); - } - } else { - inventoryplayer.setItemStack((ItemStack) null); - } - } else { - this.inventorySlots.slotClick(slot == null ? i : slot.slotNumber, j, k, this.mc.thePlayer); - if (Container.getDragEvent(j) == 2) { - for (int l = 0; l < 9; ++l) { - this.mc.playerController.sendSlotPacket(this.inventorySlots.getSlot(45 + l).getStack(), 36 + l); - } - } else if (slot != null) { - ItemStack itemstack4 = this.inventorySlots.getSlot(slot.slotNumber).getStack(); - this.mc.playerController.sendSlotPacket(itemstack4, - slot.slotNumber - this.inventorySlots.inventorySlots.size() + 9 + 36); - } - } - - } - - protected void updateActivePotionEffects() { - int i = this.guiLeft; - super.updateActivePotionEffects(); - if (this.searchField != null && this.guiLeft != i) { - this.searchField.xPosition = this.guiLeft + 82; - } - - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - if (this.mc.playerController.isInCreativeMode()) { - super.initGui(); - this.buttonList.clear(); - Keyboard.enableRepeatEvents(true); - this.searchField = new GuiTextField(0, this.fontRendererObj, this.guiLeft + 82, this.guiTop + 6, 89, - this.fontRendererObj.FONT_HEIGHT); - this.searchField.setMaxStringLength(15); - this.searchField.setEnableBackgroundDrawing(false); - this.searchField.setVisible(false); - this.searchField.setTextColor(16777215); - int i = selectedTabIndex; - selectedTabIndex = -1; - this.setCurrentCreativeTab(CreativeTabs.creativeTabArray[i]); - this.field_147059_E = new CreativeCrafting(this.mc); - this.mc.thePlayer.inventoryContainer.onCraftGuiOpened(this.field_147059_E); - } else { - this.mc.displayGuiScreen(new GuiInventory(this.mc.thePlayer)); - } - - } - - /** - * + - * Called when the screen is unloaded. Used to disable keyboard - * repeat events - */ - public void onGuiClosed() { - super.onGuiClosed(); - if (this.mc.thePlayer != null && this.mc.thePlayer.inventory != null) { - this.mc.thePlayer.inventoryContainer.removeCraftingFromCrafters(this.field_147059_E); - } - - Keyboard.enableRepeatEvents(false); - } - - /** - * + - * Fired when a key is typed (except F11 which toggles full - * screen). This is the equivalent of - * KeyListener.keyTyped(KeyEvent e). Args : character (character - * on the key), keyCode (lwjgl Keyboard key code) - */ - protected void keyTyped(char parChar1, int parInt1) { - if (selectedTabIndex != CreativeTabs.tabAllSearch.getTabIndex()) { - if (GameSettings.isKeyDown(this.mc.gameSettings.keyBindChat)) { - this.setCurrentCreativeTab(CreativeTabs.tabAllSearch); - } else { - super.keyTyped(parChar1, parInt1); - } - - } else { - if (this.field_147057_D) { - this.field_147057_D = false; - this.searchField.setText(""); - } - - if (parInt1 == getCloseKey() || (parInt1 == 1 && this.mc.areKeysLocked())) { - mc.displayGuiScreen(null); - } else if (!this.checkHotbarKeys(parInt1)) { - if (this.searchField.textboxKeyTyped(parChar1, parInt1)) { - this.updateCreativeSearch(); - } else { - super.keyTyped(parChar1, parInt1); - } - - } - } - } - - protected int getCloseKey() { - return selectedTabIndex != CreativeTabs.tabAllSearch.getTabIndex() ? super.getCloseKey() - : mc.gameSettings.keyBindClose.getKeyCode(); - } - - private void updateCreativeSearch() { - GuiContainerCreative.ContainerCreative guicontainercreative$containercreative = (GuiContainerCreative.ContainerCreative) this.inventorySlots; - guicontainercreative$containercreative.itemList.clear(); - - for (Item item : Item.itemRegistry) { - if (item != null && item.getCreativeTab() != null) { - item.getSubItems(item, (CreativeTabs) null, guicontainercreative$containercreative.itemList); - } - } - - for (int i = 0; i < Enchantment.enchantmentsBookList.length; ++i) { - Enchantment enchantment = Enchantment.enchantmentsBookList[i]; - if (enchantment != null && enchantment.type != null) { - Items.enchanted_book.getAll(enchantment, guicontainercreative$containercreative.itemList); - } - } - - Iterator iterator = guicontainercreative$containercreative.itemList.iterator(); - String s1 = this.searchField.getText().toLowerCase(); - - while (iterator.hasNext()) { - ItemStack itemstack = (ItemStack) iterator.next(); - boolean flag = false; - - List lst = itemstack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); - for (int i = 0, l = lst.size(); i < l; ++i) { - if (EnumChatFormatting.getTextWithoutFormattingCodes(lst.get(i)).toLowerCase().contains(s1)) { - flag = true; - break; - } - } - - if (!flag) { - iterator.remove(); - } - } - - this.currentScroll = 0.0F; - guicontainercreative$containercreative.scrollTo(0.0F); - } - - /** - * + - * Draw the foreground layer for the GuiContainer (everything in - * front of the items). Args : mouseX, mouseY - */ - protected void drawGuiContainerForegroundLayer(int var1, int var2) { - CreativeTabs creativetabs = CreativeTabs.creativeTabArray[selectedTabIndex]; - if (creativetabs.drawInForegroundOfTab()) { - GlStateManager.disableBlend(); - this.fontRendererObj.drawString(I18n.format(creativetabs.getTranslatedTabLabel(), new Object[0]), 8, 6, - 4210752); - } - - } - - /** - * + - * Called when the mouse is clicked. Args : mouseX, mouseY, - * clickedButton - */ - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { - if (parInt3 == 0) { - int i = parInt1 - this.guiLeft; - int j = parInt2 - this.guiTop; - - for (int k = 0; k < CreativeTabs.creativeTabArray.length; ++k) { - if (this.func_147049_a(CreativeTabs.creativeTabArray[k], i, j)) { - return; - } - } - } - - super.mouseClicked(parInt1, parInt2, parInt3); - } - - /** - * + - * Called when a mouse button is released. Args : mouseX, - * mouseY, releaseButton - */ - protected void mouseReleased(int i, int j, int k) { - if (k == 0) { - int l = i - this.guiLeft; - int i1 = j - this.guiTop; - - for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { - CreativeTabs creativetabs = CreativeTabs.creativeTabArray[m]; - if (this.func_147049_a(creativetabs, l, i1)) { - this.setCurrentCreativeTab(creativetabs); - return; - } - } - } - - super.mouseReleased(i, j, k); - } - - /** - * + - * returns (if you are not on the inventoryTab) and (the flag - * isn't set) and (you have more than 1 page of items) - */ - private boolean needsScrollBars() { - return selectedTabIndex != CreativeTabs.tabInventory.getTabIndex() - && CreativeTabs.creativeTabArray[selectedTabIndex].shouldHidePlayerInventory() - && ((GuiContainerCreative.ContainerCreative) this.inventorySlots).func_148328_e(); - } - - private void setCurrentCreativeTab(CreativeTabs parCreativeTabs) { - int i = selectedTabIndex; - selectedTabIndex = parCreativeTabs.getTabIndex(); - GuiContainerCreative.ContainerCreative guicontainercreative$containercreative = (GuiContainerCreative.ContainerCreative) this.inventorySlots; - this.dragSplittingSlots.clear(); - guicontainercreative$containercreative.itemList.clear(); - parCreativeTabs.displayAllReleventItems(guicontainercreative$containercreative.itemList); - if (parCreativeTabs == CreativeTabs.tabInventory) { - Container container = this.mc.thePlayer.inventoryContainer; - if (this.field_147063_B == null) { - this.field_147063_B = guicontainercreative$containercreative.inventorySlots; - } - - guicontainercreative$containercreative.inventorySlots = Lists.newArrayList(); - - for (int j = 0; j < container.inventorySlots.size(); ++j) { - GuiContainerCreative.CreativeSlot guicontainercreative$creativeslot = new GuiContainerCreative.CreativeSlot( - (Slot) container.inventorySlots.get(j), j); - guicontainercreative$containercreative.inventorySlots.add(guicontainercreative$creativeslot); - if (j >= 5 && j < 9) { - int j1 = j - 5; - int k1 = j1 / 2; - int l1 = j1 % 2; - guicontainercreative$creativeslot.xDisplayPosition = 9 + k1 * 54; - guicontainercreative$creativeslot.yDisplayPosition = 6 + l1 * 27; - } else if (j >= 0 && j < 5) { - guicontainercreative$creativeslot.yDisplayPosition = -2000; - guicontainercreative$creativeslot.xDisplayPosition = -2000; - } else if (j < container.inventorySlots.size()) { - int k = j - 9; - int l = k % 9; - int i1 = k / 9; - guicontainercreative$creativeslot.xDisplayPosition = 9 + l * 18; - if (j >= 36) { - guicontainercreative$creativeslot.yDisplayPosition = 112; - } else { - guicontainercreative$creativeslot.yDisplayPosition = 54 + i1 * 18; - } - } - } - - this.field_147064_C = new Slot(field_147060_v, 0, 173, 112); - guicontainercreative$containercreative.inventorySlots.add(this.field_147064_C); - } else if (i == CreativeTabs.tabInventory.getTabIndex()) { - guicontainercreative$containercreative.inventorySlots = this.field_147063_B; - this.field_147063_B = null; - } - - if (this.searchField != null) { - if (parCreativeTabs == CreativeTabs.tabAllSearch) { - this.searchField.setVisible(true); - this.searchField.setCanLoseFocus(false); - this.searchField.setFocused(true); - this.searchField.setText(""); - this.updateCreativeSearch(); - } else { - this.searchField.setVisible(false); - this.searchField.setCanLoseFocus(true); - this.searchField.setFocused(false); - } - } - - this.currentScroll = 0.0F; - guicontainercreative$containercreative.scrollTo(0.0F); - } - - /** - * + - * Handles mouse input. - */ - public void handleMouseInput() throws IOException { - super.handleMouseInput(); - int i = Mouse.getEventDWheel(); - if (i != 0 && this.needsScrollBars()) { - int j = ((GuiContainerCreative.ContainerCreative) this.inventorySlots).itemList.size() / 9 - 5; - if (i > 0) { - i = 1; - } - - if (i < 0) { - i = -1; - } - - this.currentScroll = (float) ((double) this.currentScroll - (double) i / (double) j); - this.currentScroll = MathHelper.clamp_float(this.currentScroll, 0.0F, 1.0F); - ((GuiContainerCreative.ContainerCreative) this.inventorySlots).scrollTo(this.currentScroll); - } - - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - boolean flag = Mouse.isButtonDown(0); - int k = this.guiLeft; - int l = this.guiTop; - int i1 = k + 175; - int j1 = l + 18; - int k1 = i1 + 14; - int l1 = j1 + 112; - if (!this.wasClicking && flag && i >= i1 && j >= j1 && i < k1 && j < l1) { - this.isScrolling = this.needsScrollBars(); - } - - if (!flag) { - this.isScrolling = false; - } - - this.wasClicking = flag; - if (this.isScrolling) { - this.currentScroll = ((float) (j - j1) - 7.5F) / ((float) (l1 - j1) - 15.0F); - this.currentScroll = MathHelper.clamp_float(this.currentScroll, 0.0F, 1.0F); - ((GuiContainerCreative.ContainerCreative) this.inventorySlots).scrollTo(this.currentScroll); - } - - super.drawScreen(i, j, f); - - for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { - if (this.renderCreativeInventoryHoveringText(CreativeTabs.creativeTabArray[m], i, j)) { - Mouse.showCursor(EnumCursorType.HAND); - break; - } - } - - if (this.field_147064_C != null && selectedTabIndex == CreativeTabs.tabInventory.getTabIndex() - && this.isPointInRegion(this.field_147064_C.xDisplayPosition, this.field_147064_C.yDisplayPosition, 16, - 16, i, j)) { - this.drawCreativeTabHoveringText(I18n.format("inventory.binSlot", new Object[0]), i, j); - } - - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.disableLighting(); - } - - protected void renderToolTip(ItemStack itemstack, int i, int j) { - if (selectedTabIndex == CreativeTabs.tabAllSearch.getTabIndex()) { - List list = itemstack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); - CreativeTabs creativetabs = itemstack.getItem().getCreativeTab(); - if (creativetabs == null && itemstack.getItem() == Items.enchanted_book) { - Map map = EnchantmentHelper.getEnchantments(itemstack); - if (map.size() == 1) { - Enchantment enchantment = Enchantment - .getEnchantmentById(((Integer) map.keySet().iterator().next()).intValue()); - - for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { - CreativeTabs creativetabs1 = CreativeTabs.creativeTabArray[m]; - if (creativetabs1.hasRelevantEnchantmentType(enchantment.type)) { - creativetabs = creativetabs1; - break; - } - } - } - } - - if (creativetabs != null) { - list.add(1, "" + EnumChatFormatting.BOLD + EnumChatFormatting.BLUE - + I18n.format(creativetabs.getTranslatedTabLabel(), new Object[0])); - } - - for (int k = 0; k < list.size(); ++k) { - if (k == 0) { - list.set(k, itemstack.getRarity().rarityColor + (String) list.get(k)); - } else { - list.set(k, EnumChatFormatting.GRAY + (String) list.get(k)); - } - } - - this.drawHoveringText(list, i, j); - } else { - super.renderToolTip(itemstack, i, j); - } - - } - - /** - * + - * Args : renderPartialTicks, mouseX, mouseY - */ - protected void drawGuiContainerBackgroundLayer(float var1, int i, int j) { - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - RenderHelper.enableGUIStandardItemLighting(); - CreativeTabs creativetabs = CreativeTabs.creativeTabArray[selectedTabIndex]; - - for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { - CreativeTabs creativetabs1 = CreativeTabs.creativeTabArray[m]; - this.mc.getTextureManager().bindTexture(creativeInventoryTabs); - if (creativetabs1.getTabIndex() != selectedTabIndex) { - this.func_147051_a(creativetabs1); - } - } - - this.mc.getTextureManager().bindTexture(new ResourceLocation( - "textures/gui/container/creative_inventory/tab_" + creativetabs.getBackgroundImageName())); - this.drawTexturedModalRect(this.guiLeft, this.guiTop, 0, 0, this.xSize, this.ySize); - this.searchField.drawTextBox(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - int k = this.guiLeft + 175; - int l = this.guiTop + 18; - int i1 = l + 112; - this.mc.getTextureManager().bindTexture(creativeInventoryTabs); - if (creativetabs.shouldHidePlayerInventory()) { - this.drawTexturedModalRect(k, l + (int) ((float) (i1 - l - 17) * this.currentScroll), - 232 + (this.needsScrollBars() ? 0 : 12), 0, 12, 15); - } - - this.func_147051_a(creativetabs); - if (creativetabs == CreativeTabs.tabInventory) { - GuiInventory.drawEntityOnScreen(this.guiLeft + 43, this.guiTop + 45, 20, (float) (this.guiLeft + 43 - i), - (float) (this.guiTop + 45 - 30 - j), this.mc.thePlayer); - } - - } - - protected boolean func_147049_a(CreativeTabs parCreativeTabs, int parInt1, int parInt2) { - int i = parCreativeTabs.getTabColumn(); - int j = 28 * i; - int k = 0; - if (i == 5) { - j = this.xSize - 28 + 2; - } else if (i > 0) { - j += i; - } - - if (parCreativeTabs.isTabInFirstRow()) { - k = k - 32; - } else { - k = k + this.ySize; - } - - return parInt1 >= j && parInt1 <= j + 28 && parInt2 >= k && parInt2 <= k + 32; - } - - /** - * + - * Renders the creative inventory hovering text if mouse is over - * it. Returns true if did render or false otherwise. Params: - * current creative tab to be checked, current mouse x position, - * current mouse y position. - */ - protected boolean renderCreativeInventoryHoveringText(CreativeTabs parCreativeTabs, int parInt1, int parInt2) { - int i = parCreativeTabs.getTabColumn(); - int j = 28 * i; - int k = 0; - if (i == 5) { - j = this.xSize - 28 + 2; - } else if (i > 0) { - j += i; - } - - if (parCreativeTabs.isTabInFirstRow()) { - k = k - 32; - } else { - k = k + this.ySize; - } - - if (this.isPointInRegion(j + 3, k + 3, 23, 27, parInt1, parInt2)) { - this.drawCreativeTabHoveringText(I18n.format(parCreativeTabs.getTranslatedTabLabel(), new Object[0]), - parInt1, parInt2); - return true; - } else { - return false; - } - } - - protected void func_147051_a(CreativeTabs parCreativeTabs) { - boolean flag = parCreativeTabs.getTabIndex() == selectedTabIndex; - boolean flag1 = parCreativeTabs.isTabInFirstRow(); - int i = parCreativeTabs.getTabColumn(); - int j = i * 28; - int k = 0; - int l = this.guiLeft + 28 * i; - int i1 = this.guiTop; - byte b0 = 32; - if (flag) { - k += 32; - } - - if (i == 5) { - l = this.guiLeft + this.xSize - 28; - } else if (i > 0) { - l += i; - } - - if (flag1) { - i1 = i1 - 28; - } else { - k += 64; - i1 = i1 + (this.ySize - 4); - } - - GlStateManager.disableLighting(); - this.drawTexturedModalRect(l, i1, j, k, 28, b0); - this.zLevel = 100.0F; - this.itemRender.zLevel = 100.0F; - l = l + 6; - i1 = i1 + 8 + (flag1 ? 1 : -1); - GlStateManager.enableLighting(); - GlStateManager.enableRescaleNormal(); - ItemStack itemstack = parCreativeTabs.getIconItemStack(); - this.itemRender.renderItemAndEffectIntoGUI(itemstack, l, i1); - this.itemRender.renderItemOverlays(this.fontRendererObj, itemstack, l, i1); - GlStateManager.disableLighting(); - this.itemRender.zLevel = 0.0F; - this.zLevel = 0.0F; - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.id == 0) { - this.mc.displayGuiScreen(new GuiAchievements(this, this.mc.thePlayer.getStatFileWriter())); - } - - if (parGuiButton.id == 1) { - this.mc.displayGuiScreen(new GuiStats(this, this.mc.thePlayer.getStatFileWriter())); - } - - } - - public int getSelectedTabIndex() { - return selectedTabIndex; - } - - static class ContainerCreative extends Container { - public List itemList = Lists.newArrayList(); - - public ContainerCreative(EntityPlayer parEntityPlayer) { - InventoryPlayer inventoryplayer = parEntityPlayer.inventory; - - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 9; ++j) { - this.addSlotToContainer( - new Slot(GuiContainerCreative.field_147060_v, i * 9 + j, 9 + j * 18, 18 + i * 18)); - } - } - - for (int k = 0; k < 9; ++k) { - this.addSlotToContainer(new Slot(inventoryplayer, k, 9 + k * 18, 112)); - } - - this.scrollTo(0.0F); - } - - public boolean canInteractWith(EntityPlayer playerIn) { - return true; - } - - public void scrollTo(float parFloat1) { - int i = (this.itemList.size() + 9 - 1) / 9 - 5; - int j = (int) ((double) (parFloat1 * (float) i) + 0.5D); - if (j < 0) { - j = 0; - } - - for (int k = 0; k < 5; ++k) { - for (int l = 0; l < 9; ++l) { - int i1 = l + (k + j) * 9; - if (i1 >= 0 && i1 < this.itemList.size()) { - GuiContainerCreative.field_147060_v.setInventorySlotContents(l + k * 9, - (ItemStack) this.itemList.get(i1)); - } else { - GuiContainerCreative.field_147060_v.setInventorySlotContents(l + k * 9, (ItemStack) null); - } - } - } - - } - - public boolean func_148328_e() { - return this.itemList.size() > 45; - } - - protected void retrySlotClick(int slotId, int clickedButton, boolean mode, EntityPlayer playerIn) { - } - - public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) { - if (index >= this.inventorySlots.size() - 9 && index < this.inventorySlots.size()) { - Slot slot = (Slot) this.inventorySlots.get(index); - if (slot != null && slot.getHasStack()) { - slot.putStack((ItemStack) null); - } - } - - return null; - } - - public boolean canMergeSlot(ItemStack stack, Slot parSlot) { - return parSlot.yDisplayPosition > 90; - } - - public boolean canDragIntoSlot(Slot parSlot) { - return parSlot.inventory instanceof InventoryPlayer - || parSlot.yDisplayPosition > 90 && parSlot.xDisplayPosition <= 162; - } - } - - class CreativeSlot extends Slot { - private final Slot slot; - - public CreativeSlot(Slot parSlot, int parInt1) { - super(parSlot.inventory, parInt1, 0, 0); - this.slot = parSlot; - } - - public void onPickupFromSlot(EntityPlayer playerIn, ItemStack stack) { - this.slot.onPickupFromSlot(playerIn, stack); - } - - public boolean isItemValid(ItemStack stack) { - return this.slot.isItemValid(stack); - } - - public ItemStack getStack() { - return this.slot.getStack(); - } - - public boolean getHasStack() { - return this.slot.getHasStack(); - } - - public void putStack(ItemStack stack) { - this.slot.putStack(stack); - } - - public void onSlotChanged() { - this.slot.onSlotChanged(); - } - - public int getSlotStackLimit() { - return this.slot.getSlotStackLimit(); - } - - public int getItemStackLimit(ItemStack stack) { - return this.slot.getItemStackLimit(stack); - } - - public String getSlotTexture() { - return this.slot.getSlotTexture(); - } - - public ItemStack decrStackSize(int amount) { - return this.slot.decrStackSize(amount); - } - - public boolean isHere(IInventory inv, int slotIn) { - return this.slot.isHere(inv, slotIn); - } - } - - public boolean blockPTTKey() { - return searchField.isFocused(); - } +package net.minecraft.client.gui.inventory; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.client.gui.achievement.GuiAchievements; +import net.minecraft.client.gui.achievement.GuiStats; +import net.minecraft.client.renderer.InventoryEffectRenderer; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.init.Items; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.InventoryBasic; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiContainerCreative extends InventoryEffectRenderer { + /** + * + + * The location of the creative inventory tabs texture + */ + private static final ResourceLocation creativeInventoryTabs = new ResourceLocation( + "textures/gui/container/creative_inventory/tabs.png"); + private static InventoryBasic field_147060_v = new InventoryBasic("tmp", true, 45); + /** + * + + * Currently selected creative inventory tab index. + */ + private static int selectedTabIndex = CreativeTabs.tabBlock.getTabIndex(); + private float currentScroll; + private boolean isScrolling; + private boolean wasClicking; + private GuiTextField searchField; + private List field_147063_B; + private Slot field_147064_C; + private boolean field_147057_D; + private CreativeCrafting field_147059_E; + + public GuiContainerCreative(EntityPlayer parEntityPlayer) { + super(new GuiContainerCreative.ContainerCreative(parEntityPlayer)); + parEntityPlayer.openContainer = this.inventorySlots; + this.allowUserInput = true; + this.ySize = 136; + this.xSize = 195; + } + + /** + * + + * Called from the main game loop to update the screen. + */ + public void updateScreen() { + if (!this.mc.playerController.isInCreativeMode()) { + this.mc.displayGuiScreen(new GuiInventory(this.mc.thePlayer)); + } + + this.updateActivePotionEffects(); + } + + /** + * + + * Called when the mouse is clicked over a slot or outside the + * gui. + */ + protected void handleMouseClick(Slot slot, int i, int j, int k) { + this.field_147057_D = true; + boolean flag = k == 1; + k = i == -999 && k == 0 ? 4 : k; + if (slot == null && selectedTabIndex != CreativeTabs.tabInventory.getTabIndex() && k != 5) { + InventoryPlayer inventoryplayer1 = this.mc.thePlayer.inventory; + if (inventoryplayer1.getItemStack() != null) { + if (j == 0) { + this.mc.thePlayer.dropPlayerItemWithRandomChoice(inventoryplayer1.getItemStack(), true); + this.mc.playerController.sendPacketDropItem(inventoryplayer1.getItemStack()); + inventoryplayer1.setItemStack((ItemStack) null); + } + + if (j == 1) { + ItemStack itemstack5 = inventoryplayer1.getItemStack().splitStack(1); + this.mc.thePlayer.dropPlayerItemWithRandomChoice(itemstack5, true); + this.mc.playerController.sendPacketDropItem(itemstack5); + if (inventoryplayer1.getItemStack().stackSize == 0) { + inventoryplayer1.setItemStack((ItemStack) null); + } + } + } + } else if (slot == this.field_147064_C && flag) { + for (int i1 = 0; i1 < this.mc.thePlayer.inventoryContainer.getInventory().size(); ++i1) { + this.mc.playerController.sendSlotPacket((ItemStack) null, i1); + } + } else if (selectedTabIndex == CreativeTabs.tabInventory.getTabIndex()) { + if (slot == this.field_147064_C) { + this.mc.thePlayer.inventory.setItemStack((ItemStack) null); + } else if (k == 4 && slot != null && slot.getHasStack()) { + ItemStack itemstack = slot.decrStackSize(j == 0 ? 1 : slot.getStack().getMaxStackSize()); + this.mc.thePlayer.dropPlayerItemWithRandomChoice(itemstack, true); + this.mc.playerController.sendPacketDropItem(itemstack); + } else if (k == 4 && this.mc.thePlayer.inventory.getItemStack() != null) { + this.mc.thePlayer.dropPlayerItemWithRandomChoice(this.mc.thePlayer.inventory.getItemStack(), true); + this.mc.playerController.sendPacketDropItem(this.mc.thePlayer.inventory.getItemStack()); + this.mc.thePlayer.inventory.setItemStack((ItemStack) null); + } else { + this.mc.thePlayer.inventoryContainer.slotClick( + slot == null ? i : ((GuiContainerCreative.CreativeSlot) slot).slot.slotNumber, j, k, + this.mc.thePlayer); + this.mc.thePlayer.inventoryContainer.detectAndSendChanges(); + } + } else if (k != 5 && slot.inventory == field_147060_v) { + InventoryPlayer inventoryplayer = this.mc.thePlayer.inventory; + ItemStack itemstack1 = inventoryplayer.getItemStack(); + ItemStack itemstack2 = slot.getStack(); + if (k == 2) { + if (itemstack2 != null && j >= 0 && j < 9) { + ItemStack itemstack7 = itemstack2.copy(); + itemstack7.stackSize = itemstack7.getMaxStackSize(); + this.mc.thePlayer.inventory.setInventorySlotContents(j, itemstack7); + this.mc.thePlayer.inventoryContainer.detectAndSendChanges(); + } + + return; + } + + if (k == 3) { + if (inventoryplayer.getItemStack() == null && slot.getHasStack()) { + ItemStack itemstack6 = slot.getStack().copy(); + itemstack6.stackSize = itemstack6.getMaxStackSize(); + inventoryplayer.setItemStack(itemstack6); + } + + return; + } + + if (k == 4) { + if (itemstack2 != null) { + ItemStack itemstack3 = itemstack2.copy(); + itemstack3.stackSize = j == 0 ? 1 : itemstack3.getMaxStackSize(); + this.mc.thePlayer.dropPlayerItemWithRandomChoice(itemstack3, true); + this.mc.playerController.sendPacketDropItem(itemstack3); + } + + return; + } + + if (itemstack1 != null && itemstack2 != null && itemstack1.isItemEqual(itemstack2)) { + if (j == 0) { + if (flag) { + itemstack1.stackSize = itemstack1.getMaxStackSize(); + } else if (itemstack1.stackSize < itemstack1.getMaxStackSize()) { + ++itemstack1.stackSize; + } + } else if (itemstack1.stackSize <= 1) { + inventoryplayer.setItemStack((ItemStack) null); + } else { + --itemstack1.stackSize; + } + } else if (itemstack2 != null && itemstack1 == null) { + inventoryplayer.setItemStack(ItemStack.copyItemStack(itemstack2)); + itemstack1 = inventoryplayer.getItemStack(); + if (flag) { + itemstack1.stackSize = itemstack1.getMaxStackSize(); + } + } else { + inventoryplayer.setItemStack((ItemStack) null); + } + } else { + this.inventorySlots.slotClick(slot == null ? i : slot.slotNumber, j, k, this.mc.thePlayer); + if (Container.getDragEvent(j) == 2) { + for (int l = 0; l < 9; ++l) { + this.mc.playerController.sendSlotPacket(this.inventorySlots.getSlot(45 + l).getStack(), 36 + l); + } + } else if (slot != null) { + ItemStack itemstack4 = this.inventorySlots.getSlot(slot.slotNumber).getStack(); + this.mc.playerController.sendSlotPacket(itemstack4, + slot.slotNumber - this.inventorySlots.inventorySlots.size() + 9 + 36); + } + } + + } + + protected void updateActivePotionEffects() { + int i = this.guiLeft; + super.updateActivePotionEffects(); + if (this.searchField != null && this.guiLeft != i) { + this.searchField.xPosition = this.guiLeft + 82; + } + + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + if (this.mc.playerController.isInCreativeMode()) { + super.initGui(); + this.buttonList.clear(); + Keyboard.enableRepeatEvents(true); + this.searchField = new GuiTextField(0, this.fontRendererObj, this.guiLeft + 82, this.guiTop + 6, 89, + this.fontRendererObj.FONT_HEIGHT); + this.searchField.setMaxStringLength(15); + this.searchField.setEnableBackgroundDrawing(false); + this.searchField.setVisible(false); + this.searchField.setTextColor(16777215); + int i = selectedTabIndex; + selectedTabIndex = -1; + this.setCurrentCreativeTab(CreativeTabs.creativeTabArray[i]); + this.field_147059_E = new CreativeCrafting(this.mc); + this.mc.thePlayer.inventoryContainer.onCraftGuiOpened(this.field_147059_E); + } else { + this.mc.displayGuiScreen(new GuiInventory(this.mc.thePlayer)); + } + + } + + /** + * + + * Called when the screen is unloaded. Used to disable keyboard + * repeat events + */ + public void onGuiClosed() { + super.onGuiClosed(); + if (this.mc.thePlayer != null && this.mc.thePlayer.inventory != null) { + this.mc.thePlayer.inventoryContainer.removeCraftingFromCrafters(this.field_147059_E); + } + + Keyboard.enableRepeatEvents(false); + } + + /** + * + + * Fired when a key is typed (except F11 which toggles full + * screen). This is the equivalent of + * KeyListener.keyTyped(KeyEvent e). Args : character (character + * on the key), keyCode (lwjgl Keyboard key code) + */ + protected void keyTyped(char parChar1, int parInt1) { + if (selectedTabIndex != CreativeTabs.tabAllSearch.getTabIndex()) { + if (GameSettings.isKeyDown(this.mc.gameSettings.keyBindChat)) { + this.setCurrentCreativeTab(CreativeTabs.tabAllSearch); + } else { + super.keyTyped(parChar1, parInt1); + } + + } else { + if (this.field_147057_D) { + this.field_147057_D = false; + this.searchField.setText(""); + } + + if (parInt1 == getCloseKey() || (parInt1 == 1 && this.mc.areKeysLocked())) { + mc.displayGuiScreen(null); + } else if (!this.checkHotbarKeys(parInt1)) { + if (this.searchField.textboxKeyTyped(parChar1, parInt1)) { + this.updateCreativeSearch(); + } else { + super.keyTyped(parChar1, parInt1); + } + + } + } + } + + protected int getCloseKey() { + return selectedTabIndex != CreativeTabs.tabAllSearch.getTabIndex() ? super.getCloseKey() + : mc.gameSettings.keyBindClose.getKeyCode(); + } + + private void updateCreativeSearch() { + GuiContainerCreative.ContainerCreative guicontainercreative$containercreative = (GuiContainerCreative.ContainerCreative) this.inventorySlots; + guicontainercreative$containercreative.itemList.clear(); + + for (Item item : Item.itemRegistry) { + if (item != null && item.getCreativeTab() != null) { + item.getSubItems(item, (CreativeTabs) null, guicontainercreative$containercreative.itemList); + } + } + + for (int i = 0; i < Enchantment.enchantmentsBookList.length; ++i) { + Enchantment enchantment = Enchantment.enchantmentsBookList[i]; + if (enchantment != null && enchantment.type != null) { + Items.enchanted_book.getAll(enchantment, guicontainercreative$containercreative.itemList); + } + } + + Iterator iterator = guicontainercreative$containercreative.itemList.iterator(); + String s1 = this.searchField.getText().toLowerCase(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + boolean flag = false; + + List lst = itemstack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); + for (int i = 0, l = lst.size(); i < l; ++i) { + if (EnumChatFormatting.getTextWithoutFormattingCodes(lst.get(i)).toLowerCase().contains(s1)) { + flag = true; + break; + } + } + + if (!flag) { + iterator.remove(); + } + } + + this.currentScroll = 0.0F; + guicontainercreative$containercreative.scrollTo(0.0F); + } + + /** + * + + * Draw the foreground layer for the GuiContainer (everything in + * front of the items). Args : mouseX, mouseY + */ + protected void drawGuiContainerForegroundLayer(int var1, int var2) { + CreativeTabs creativetabs = CreativeTabs.creativeTabArray[selectedTabIndex]; + if (creativetabs.drawInForegroundOfTab()) { + GlStateManager.disableBlend(); + this.fontRendererObj.drawString(I18n.format(creativetabs.getTranslatedTabLabel(), new Object[0]), 8, 6, + 4210752); + } + + } + + /** + * + + * Called when the mouse is clicked. Args : mouseX, mouseY, + * clickedButton + */ + protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + if (parInt3 == 0) { + int i = parInt1 - this.guiLeft; + int j = parInt2 - this.guiTop; + + for (int k = 0; k < CreativeTabs.creativeTabArray.length; ++k) { + if (this.func_147049_a(CreativeTabs.creativeTabArray[k], i, j)) { + return; + } + } + } + + super.mouseClicked(parInt1, parInt2, parInt3); + } + + /** + * + + * Called when a mouse button is released. Args : mouseX, + * mouseY, releaseButton + */ + protected void mouseReleased(int i, int j, int k) { + if (k == 0) { + int l = i - this.guiLeft; + int i1 = j - this.guiTop; + + for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { + CreativeTabs creativetabs = CreativeTabs.creativeTabArray[m]; + if (this.func_147049_a(creativetabs, l, i1)) { + this.setCurrentCreativeTab(creativetabs); + return; + } + } + } + + super.mouseReleased(i, j, k); + } + + @Override + protected void touchTapped(int touchX, int touchY, int uid) { + int l = touchX - this.guiLeft; + int i1 = touchY - this.guiTop; + + for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { + CreativeTabs creativetabs = CreativeTabs.creativeTabArray[m]; + if (this.func_147049_a(creativetabs, l, i1)) { + this.setCurrentCreativeTab(creativetabs); + break; + } + } + + super.touchTapped(touchX, touchY, uid); + } + + /**+ + * returns (if you are not on the inventoryTab) and (the flag + * isn't set) and (you have more than 1 page of items) + */ + private boolean needsScrollBars() { + return selectedTabIndex != CreativeTabs.tabInventory.getTabIndex() + && CreativeTabs.creativeTabArray[selectedTabIndex].shouldHidePlayerInventory() + && ((GuiContainerCreative.ContainerCreative) this.inventorySlots).func_148328_e(); + } + + private void setCurrentCreativeTab(CreativeTabs parCreativeTabs) { + int i = selectedTabIndex; + selectedTabIndex = parCreativeTabs.getTabIndex(); + GuiContainerCreative.ContainerCreative guicontainercreative$containercreative = (GuiContainerCreative.ContainerCreative) this.inventorySlots; + this.dragSplittingSlots.clear(); + guicontainercreative$containercreative.itemList.clear(); + parCreativeTabs.displayAllReleventItems(guicontainercreative$containercreative.itemList); + if (parCreativeTabs == CreativeTabs.tabInventory) { + Container container = this.mc.thePlayer.inventoryContainer; + if (this.field_147063_B == null) { + this.field_147063_B = guicontainercreative$containercreative.inventorySlots; + } + + guicontainercreative$containercreative.inventorySlots = Lists.newArrayList(); + + for (int j = 0; j < container.inventorySlots.size(); ++j) { + GuiContainerCreative.CreativeSlot guicontainercreative$creativeslot = new GuiContainerCreative.CreativeSlot( + (Slot) container.inventorySlots.get(j), j); + guicontainercreative$containercreative.inventorySlots.add(guicontainercreative$creativeslot); + if (j >= 5 && j < 9) { + int j1 = j - 5; + int k1 = j1 / 2; + int l1 = j1 % 2; + guicontainercreative$creativeslot.xDisplayPosition = 9 + k1 * 54; + guicontainercreative$creativeslot.yDisplayPosition = 6 + l1 * 27; + } else if (j >= 0 && j < 5) { + guicontainercreative$creativeslot.yDisplayPosition = -2000; + guicontainercreative$creativeslot.xDisplayPosition = -2000; + } else if (j < container.inventorySlots.size()) { + int k = j - 9; + int l = k % 9; + int i1 = k / 9; + guicontainercreative$creativeslot.xDisplayPosition = 9 + l * 18; + if (j >= 36) { + guicontainercreative$creativeslot.yDisplayPosition = 112; + } else { + guicontainercreative$creativeslot.yDisplayPosition = 54 + i1 * 18; + } + } + } + + this.field_147064_C = new Slot(field_147060_v, 0, 173, 112); + guicontainercreative$containercreative.inventorySlots.add(this.field_147064_C); + } else if (i == CreativeTabs.tabInventory.getTabIndex()) { + guicontainercreative$containercreative.inventorySlots = this.field_147063_B; + this.field_147063_B = null; + } + + if (this.searchField != null) { + if (parCreativeTabs == CreativeTabs.tabAllSearch) { + this.searchField.setVisible(true); + this.searchField.setCanLoseFocus(false); + this.searchField.setFocused(true); + this.searchField.setText(""); + this.updateCreativeSearch(); + } else { + this.searchField.setVisible(false); + this.searchField.setCanLoseFocus(true); + this.searchField.setFocused(false); + } + } + + this.currentScroll = 0.0F; + guicontainercreative$containercreative.scrollTo(0.0F); + } + + /** + * + + * Handles mouse input. + */ + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + int i = Mouse.getEventDWheel(); + if (i != 0 && this.needsScrollBars()) { + int j = ((GuiContainerCreative.ContainerCreative) this.inventorySlots).itemList.size() / 9 - 5; + if (i > 0) { + i = 1; + } + + if (i < 0) { + i = -1; + } + + this.currentScroll = (float) ((double) this.currentScroll - (double) i / (double) j); + this.currentScroll = MathHelper.clamp_float(this.currentScroll, 0.0F, 1.0F); + ((GuiContainerCreative.ContainerCreative) this.inventorySlots).scrollTo(this.currentScroll); + } + + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + boolean flag = PointerInputAbstraction.getVCursorButtonDown(0); + int k = this.guiLeft; + int l = this.guiTop; + int i1 = k + 175; + int j1 = l + 18; + int k1 = i1 + 14; + int l1 = j1 + 112; + if (!this.wasClicking && flag && i >= i1 && j >= j1 && i < k1 && j < l1) { + this.isScrolling = this.needsScrollBars(); + } + + if (!flag) { + this.isScrolling = false; + } + + this.wasClicking = flag; + if (this.isScrolling) { + this.currentScroll = ((float) (j - j1) - 7.5F) / ((float) (l1 - j1) - 15.0F); + this.currentScroll = MathHelper.clamp_float(this.currentScroll, 0.0F, 1.0F); + ((GuiContainerCreative.ContainerCreative) this.inventorySlots).scrollTo(this.currentScroll); + } + + super.drawScreen(i, j, f); + + for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { + if (this.renderCreativeInventoryHoveringText(CreativeTabs.creativeTabArray[m], i, j)) { + Mouse.showCursor(EnumCursorType.HAND); + break; + } + } + + if (this.field_147064_C != null && selectedTabIndex == CreativeTabs.tabInventory.getTabIndex() + && this.isPointInRegion(this.field_147064_C.xDisplayPosition, this.field_147064_C.yDisplayPosition, 16, + 16, i, j)) { + this.drawCreativeTabHoveringText(I18n.format("inventory.binSlot", new Object[0]), i, j); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableLighting(); + } + + protected void renderToolTip(ItemStack itemstack, int i, int j) { + if (selectedTabIndex == CreativeTabs.tabAllSearch.getTabIndex()) { + List list = itemstack.getTooltipProfanityFilter(this.mc.thePlayer, + this.mc.gameSettings.advancedItemTooltips); + CreativeTabs creativetabs = itemstack.getItem().getCreativeTab(); + if (creativetabs == null && itemstack.getItem() == Items.enchanted_book) { + Map map = EnchantmentHelper.getEnchantments(itemstack); + if (map.size() == 1) { + Enchantment enchantment = Enchantment + .getEnchantmentById(((Integer) map.keySet().iterator().next()).intValue()); + + for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { + CreativeTabs creativetabs1 = CreativeTabs.creativeTabArray[m]; + if (creativetabs1.hasRelevantEnchantmentType(enchantment.type)) { + creativetabs = creativetabs1; + break; + } + } + } + } + + if (creativetabs != null) { + list.add(1, "" + EnumChatFormatting.BOLD + EnumChatFormatting.BLUE + + I18n.format(creativetabs.getTranslatedTabLabel(), new Object[0])); + } + + for (int k = 0; k < list.size(); ++k) { + if (k == 0) { + list.set(k, itemstack.getRarity().rarityColor + (String) list.get(k)); + } else { + list.set(k, EnumChatFormatting.GRAY + (String) list.get(k)); + } + } + + this.drawHoveringText(list, i, j); + } else { + super.renderToolTip(itemstack, i, j); + } + + } + + /** + * + + * Args : renderPartialTicks, mouseX, mouseY + */ + protected void drawGuiContainerBackgroundLayer(float var1, int i, int j) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + RenderHelper.enableGUIStandardItemLighting(); + CreativeTabs creativetabs = CreativeTabs.creativeTabArray[selectedTabIndex]; + + for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { + CreativeTabs creativetabs1 = CreativeTabs.creativeTabArray[m]; + this.mc.getTextureManager().bindTexture(creativeInventoryTabs); + if (creativetabs1.getTabIndex() != selectedTabIndex) { + this.func_147051_a(creativetabs1); + } + } + + this.mc.getTextureManager().bindTexture(new ResourceLocation( + "textures/gui/container/creative_inventory/tab_" + creativetabs.getBackgroundImageName())); + this.drawTexturedModalRect(this.guiLeft, this.guiTop, 0, 0, this.xSize, this.ySize); + this.searchField.drawTextBox(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + int k = this.guiLeft + 175; + int l = this.guiTop + 18; + int i1 = l + 112; + this.mc.getTextureManager().bindTexture(creativeInventoryTabs); + if (creativetabs.shouldHidePlayerInventory()) { + this.drawTexturedModalRect(k, l + (int) ((float) (i1 - l - 17) * this.currentScroll), + 232 + (this.needsScrollBars() ? 0 : 12), 0, 12, 15); + } + + this.func_147051_a(creativetabs); + if (creativetabs == CreativeTabs.tabInventory) { + GuiInventory.drawEntityOnScreen(this.guiLeft + 43, this.guiTop + 45, 20, (float) (this.guiLeft + 43 - i), + (float) (this.guiTop + 45 - 30 - j), this.mc.thePlayer); + } + + } + + protected boolean func_147049_a(CreativeTabs parCreativeTabs, int parInt1, int parInt2) { + int i = parCreativeTabs.getTabColumn(); + int j = 28 * i; + int k = 0; + if (i == 5) { + j = this.xSize - 28 + 2; + } else if (i > 0) { + j += i; + } + + if (parCreativeTabs.isTabInFirstRow()) { + k = k - 32; + } else { + k = k + this.ySize; + } + + return parInt1 >= j && parInt1 <= j + 28 && parInt2 >= k && parInt2 <= k + 32; + } + + /** + * + + * Renders the creative inventory hovering text if mouse is over + * it. Returns true if did render or false otherwise. Params: + * current creative tab to be checked, current mouse x position, + * current mouse y position. + */ + protected boolean renderCreativeInventoryHoveringText(CreativeTabs parCreativeTabs, int parInt1, int parInt2) { + int i = parCreativeTabs.getTabColumn(); + int j = 28 * i; + int k = 0; + if (i == 5) { + j = this.xSize - 28 + 2; + } else if (i > 0) { + j += i; + } + + if (parCreativeTabs.isTabInFirstRow()) { + k = k - 32; + } else { + k = k + this.ySize; + } + + if (this.isPointInRegion(j + 3, k + 3, 23, 27, parInt1, parInt2)) { + this.drawCreativeTabHoveringText(I18n.format(parCreativeTabs.getTranslatedTabLabel(), new Object[0]), + parInt1, parInt2); + return true; + } else { + return false; + } + } + + protected void func_147051_a(CreativeTabs parCreativeTabs) { + boolean flag = parCreativeTabs.getTabIndex() == selectedTabIndex; + boolean flag1 = parCreativeTabs.isTabInFirstRow(); + int i = parCreativeTabs.getTabColumn(); + int j = i * 28; + int k = 0; + int l = this.guiLeft + 28 * i; + int i1 = this.guiTop; + byte b0 = 32; + if (flag) { + k += 32; + } + + if (i == 5) { + l = this.guiLeft + this.xSize - 28; + } else if (i > 0) { + l += i; + } + + if (flag1) { + i1 = i1 - 28; + } else { + k += 64; + i1 = i1 + (this.ySize - 4); + } + + GlStateManager.disableLighting(); + this.drawTexturedModalRect(l, i1, j, k, 28, b0); + this.zLevel = 100.0F; + this.itemRender.zLevel = 100.0F; + l = l + 6; + i1 = i1 + 8 + (flag1 ? 1 : -1); + GlStateManager.enableLighting(); + GlStateManager.enableRescaleNormal(); + ItemStack itemstack = parCreativeTabs.getIconItemStack(); + this.itemRender.renderItemAndEffectIntoGUI(itemstack, l, i1); + this.itemRender.renderItemOverlays(this.fontRendererObj, itemstack, l, i1); + GlStateManager.disableLighting(); + this.itemRender.zLevel = 0.0F; + this.zLevel = 0.0F; + } + + /** + * + + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + if (parGuiButton.id == 0) { + this.mc.displayGuiScreen(new GuiAchievements(this, this.mc.thePlayer.getStatFileWriter())); + } + + if (parGuiButton.id == 1) { + this.mc.displayGuiScreen(new GuiStats(this, this.mc.thePlayer.getStatFileWriter())); + } + + } + + public int getSelectedTabIndex() { + return selectedTabIndex; + } + + static class ContainerCreative extends Container { + public List itemList = Lists.newArrayList(); + + public ContainerCreative(EntityPlayer parEntityPlayer) { + InventoryPlayer inventoryplayer = parEntityPlayer.inventory; + + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 9; ++j) { + this.addSlotToContainer( + new Slot(GuiContainerCreative.field_147060_v, i * 9 + j, 9 + j * 18, 18 + i * 18)); + } + } + + for (int k = 0; k < 9; ++k) { + this.addSlotToContainer(new Slot(inventoryplayer, k, 9 + k * 18, 112)); + } + + this.scrollTo(0.0F); + } + + public boolean canInteractWith(EntityPlayer playerIn) { + return true; + } + + public void scrollTo(float parFloat1) { + int i = (this.itemList.size() + 9 - 1) / 9 - 5; + int j = (int) ((double) (parFloat1 * (float) i) + 0.5D); + if (j < 0) { + j = 0; + } + + for (int k = 0; k < 5; ++k) { + for (int l = 0; l < 9; ++l) { + int i1 = l + (k + j) * 9; + if (i1 >= 0 && i1 < this.itemList.size()) { + GuiContainerCreative.field_147060_v.setInventorySlotContents(l + k * 9, + (ItemStack) this.itemList.get(i1)); + } else { + GuiContainerCreative.field_147060_v.setInventorySlotContents(l + k * 9, (ItemStack) null); + } + } + } + + } + + public boolean func_148328_e() { + return this.itemList.size() > 45; + } + + protected void retrySlotClick(int slotId, int clickedButton, boolean mode, EntityPlayer playerIn) { + } + + public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) { + if (index >= this.inventorySlots.size() - 9 && index < this.inventorySlots.size()) { + Slot slot = (Slot) this.inventorySlots.get(index); + if (slot != null && slot.getHasStack()) { + slot.putStack((ItemStack) null); + } + } + + return null; + } + + public boolean canMergeSlot(ItemStack stack, Slot parSlot) { + return parSlot.yDisplayPosition > 90; + } + + public boolean canDragIntoSlot(Slot parSlot) { + return parSlot.inventory instanceof InventoryPlayer + || parSlot.yDisplayPosition > 90 && parSlot.xDisplayPosition <= 162; + } + } + + class CreativeSlot extends Slot { + private final Slot slot; + + public CreativeSlot(Slot parSlot, int parInt1) { + super(parSlot.inventory, parInt1, 0, 0); + this.slot = parSlot; + } + + public void onPickupFromSlot(EntityPlayer playerIn, ItemStack stack) { + this.slot.onPickupFromSlot(playerIn, stack); + } + + public boolean isItemValid(ItemStack stack) { + return this.slot.isItemValid(stack); + } + + public ItemStack getStack() { + return this.slot.getStack(); + } + + public boolean getHasStack() { + return this.slot.getHasStack(); + } + + public void putStack(ItemStack stack) { + this.slot.putStack(stack); + } + + public void onSlotChanged() { + this.slot.onSlotChanged(); + } + + public int getSlotStackLimit() { + return this.slot.getSlotStackLimit(); + } + + public int getItemStackLimit(ItemStack stack) { + return this.slot.getItemStackLimit(stack); + } + + public String getSlotTexture() { + return this.slot.getSlotTexture(); + } + + public ItemStack decrStackSize(int amount) { + return this.slot.decrStackSize(amount); + } + + public boolean isHere(IInventory inv, int slotIn) { + return this.slot.isHere(inv, slotIn); + } + } + + public boolean blockPTTKey() { + return searchField.isFocused(); + } + + @Override + public boolean showCopyPasteButtons() { + return searchField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + searchField.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiCrafting.java b/src/game/java/net/minecraft/client/gui/inventory/GuiCrafting.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiCrafting.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiCrafting.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiDispenser.java b/src/game/java/net/minecraft/client/gui/inventory/GuiDispenser.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiDispenser.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiDispenser.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java b/src/game/java/net/minecraft/client/gui/inventory/GuiEditSign.java similarity index 87% rename from src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiEditSign.java index a746dd0..90c9fa9 100644 --- a/src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java +++ b/src/game/java/net/minecraft/client/gui/inventory/GuiEditSign.java @@ -1,12 +1,16 @@ package net.minecraft.client.gui.inventory; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenVisualViewport; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.block.Block; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.client.renderer.tileentity.TileEntitySignRenderer; import net.minecraft.client.resources.I18n; import net.minecraft.init.Blocks; import net.minecraft.network.play.client.C12PacketUpdateSign; @@ -43,7 +47,7 @@ import net.minecraft.util.ChatComponentText; * POSSIBILITY OF SUCH DAMAGE. * */ -public class GuiEditSign extends GuiScreen { +public class GuiEditSign extends GuiScreenVisualViewport { private TileEntitySign tileSign; private int updateCounter; private int editLine; @@ -83,11 +87,7 @@ public class GuiEditSign extends GuiScreen { this.tileSign.setEditable(true); } - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { + public void updateScreen0() { ++this.updateCounter; } @@ -139,12 +139,7 @@ public class GuiEditSign extends GuiScreen { } - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { + public void drawScreen0(int i, int j, float f) { this.drawDefaultBackground(); this.drawCenteredString(this.fontRendererObj, I18n.format("sign.edit", new Object[0]), this.width / 2, 40, 16777215); @@ -182,13 +177,23 @@ public class GuiEditSign extends GuiScreen { this.tileSign.lineBeingEdited = this.editLine; } - TileEntityRendererDispatcher.instance.renderTileEntityAt(this.tileSign, -0.5D, -0.75D, -0.5D, 0.0F); + try { + TileEntitySignRenderer.disableProfanityFilter = true; + TileEntityRendererDispatcher.instance.renderTileEntityAt(this.tileSign, -0.5D, + (PointerInputAbstraction.isTouchMode() && (Display.getVisualViewportH() / mc.displayHeight) < 0.75f) + ? -0.25D + : -0.75D, + -0.5D, 0.0F); + } finally { + TileEntitySignRenderer.disableProfanityFilter = false; + } this.tileSign.lineBeingEdited = -1; GlStateManager.popMatrix(); - super.drawScreen(i, j, f); + super.drawScreen0(i, j, f); } public boolean blockPTTKey() { return true; } + } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiFurnace.java b/src/game/java/net/minecraft/client/gui/inventory/GuiFurnace.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiFurnace.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiFurnace.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiInventory.java b/src/game/java/net/minecraft/client/gui/inventory/GuiInventory.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiInventory.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiInventory.java diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.java b/src/game/java/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.java rename to src/game/java/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/BaseSpectatorGroup.java b/src/game/java/net/minecraft/client/gui/spectator/BaseSpectatorGroup.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/BaseSpectatorGroup.java rename to src/game/java/net/minecraft/client/gui/spectator/BaseSpectatorGroup.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/ISpectatorMenuObject.java b/src/game/java/net/minecraft/client/gui/spectator/ISpectatorMenuObject.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/ISpectatorMenuObject.java rename to src/game/java/net/minecraft/client/gui/spectator/ISpectatorMenuObject.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.java b/src/game/java/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.java rename to src/game/java/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/ISpectatorMenuView.java b/src/game/java/net/minecraft/client/gui/spectator/ISpectatorMenuView.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/ISpectatorMenuView.java rename to src/game/java/net/minecraft/client/gui/spectator/ISpectatorMenuView.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java b/src/game/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java rename to src/game/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/SpectatorMenu.java b/src/game/java/net/minecraft/client/gui/spectator/SpectatorMenu.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/SpectatorMenu.java rename to src/game/java/net/minecraft/client/gui/spectator/SpectatorMenu.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/categories/SpectatorDetails.java b/src/game/java/net/minecraft/client/gui/spectator/categories/SpectatorDetails.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/categories/SpectatorDetails.java rename to src/game/java/net/minecraft/client/gui/spectator/categories/SpectatorDetails.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.java b/src/game/java/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.java rename to src/game/java/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.java diff --git a/src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java b/src/game/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java similarity index 100% rename from src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java rename to src/game/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java diff --git a/src/main/java/net/minecraft/client/main/GameConfiguration.java b/src/game/java/net/minecraft/client/main/GameConfiguration.java similarity index 100% rename from src/main/java/net/minecraft/client/main/GameConfiguration.java rename to src/game/java/net/minecraft/client/main/GameConfiguration.java diff --git a/src/main/java/net/minecraft/client/main/Main.java b/src/game/java/net/minecraft/client/main/Main.java similarity index 100% rename from src/main/java/net/minecraft/client/main/Main.java rename to src/game/java/net/minecraft/client/main/Main.java diff --git a/src/main/java/net/minecraft/client/model/ModelArmorStand.java b/src/game/java/net/minecraft/client/model/ModelArmorStand.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelArmorStand.java rename to src/game/java/net/minecraft/client/model/ModelArmorStand.java diff --git a/src/main/java/net/minecraft/client/model/ModelArmorStandArmor.java b/src/game/java/net/minecraft/client/model/ModelArmorStandArmor.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelArmorStandArmor.java rename to src/game/java/net/minecraft/client/model/ModelArmorStandArmor.java diff --git a/src/main/java/net/minecraft/client/model/ModelBanner.java b/src/game/java/net/minecraft/client/model/ModelBanner.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBanner.java rename to src/game/java/net/minecraft/client/model/ModelBanner.java diff --git a/src/main/java/net/minecraft/client/model/ModelBase.java b/src/game/java/net/minecraft/client/model/ModelBase.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBase.java rename to src/game/java/net/minecraft/client/model/ModelBase.java diff --git a/src/main/java/net/minecraft/client/model/ModelBat.java b/src/game/java/net/minecraft/client/model/ModelBat.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBat.java rename to src/game/java/net/minecraft/client/model/ModelBat.java diff --git a/src/main/java/net/minecraft/client/model/ModelBiped.java b/src/game/java/net/minecraft/client/model/ModelBiped.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBiped.java rename to src/game/java/net/minecraft/client/model/ModelBiped.java diff --git a/src/main/java/net/minecraft/client/model/ModelBlaze.java b/src/game/java/net/minecraft/client/model/ModelBlaze.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBlaze.java rename to src/game/java/net/minecraft/client/model/ModelBlaze.java diff --git a/src/main/java/net/minecraft/client/model/ModelBoat.java b/src/game/java/net/minecraft/client/model/ModelBoat.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBoat.java rename to src/game/java/net/minecraft/client/model/ModelBoat.java diff --git a/src/main/java/net/minecraft/client/model/ModelBook.java b/src/game/java/net/minecraft/client/model/ModelBook.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBook.java rename to src/game/java/net/minecraft/client/model/ModelBook.java diff --git a/src/main/java/net/minecraft/client/model/ModelBox.java b/src/game/java/net/minecraft/client/model/ModelBox.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelBox.java rename to src/game/java/net/minecraft/client/model/ModelBox.java diff --git a/src/main/java/net/minecraft/client/model/ModelChest.java b/src/game/java/net/minecraft/client/model/ModelChest.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelChest.java rename to src/game/java/net/minecraft/client/model/ModelChest.java diff --git a/src/main/java/net/minecraft/client/model/ModelChicken.java b/src/game/java/net/minecraft/client/model/ModelChicken.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelChicken.java rename to src/game/java/net/minecraft/client/model/ModelChicken.java diff --git a/src/main/java/net/minecraft/client/model/ModelCow.java b/src/game/java/net/minecraft/client/model/ModelCow.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelCow.java rename to src/game/java/net/minecraft/client/model/ModelCow.java diff --git a/src/main/java/net/minecraft/client/model/ModelCreeper.java b/src/game/java/net/minecraft/client/model/ModelCreeper.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelCreeper.java rename to src/game/java/net/minecraft/client/model/ModelCreeper.java diff --git a/src/main/java/net/minecraft/client/model/ModelDragon.java b/src/game/java/net/minecraft/client/model/ModelDragon.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelDragon.java rename to src/game/java/net/minecraft/client/model/ModelDragon.java diff --git a/src/main/java/net/minecraft/client/model/ModelDragonHead.java b/src/game/java/net/minecraft/client/model/ModelDragonHead.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelDragonHead.java rename to src/game/java/net/minecraft/client/model/ModelDragonHead.java diff --git a/src/main/java/net/minecraft/client/model/ModelElytra.java b/src/game/java/net/minecraft/client/model/ModelElytra.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelElytra.java rename to src/game/java/net/minecraft/client/model/ModelElytra.java diff --git a/src/main/java/net/minecraft/client/model/ModelEnderCrystal.java b/src/game/java/net/minecraft/client/model/ModelEnderCrystal.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelEnderCrystal.java rename to src/game/java/net/minecraft/client/model/ModelEnderCrystal.java diff --git a/src/main/java/net/minecraft/client/model/ModelEnderMite.java b/src/game/java/net/minecraft/client/model/ModelEnderMite.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelEnderMite.java rename to src/game/java/net/minecraft/client/model/ModelEnderMite.java diff --git a/src/main/java/net/minecraft/client/model/ModelEnderman.java b/src/game/java/net/minecraft/client/model/ModelEnderman.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelEnderman.java rename to src/game/java/net/minecraft/client/model/ModelEnderman.java diff --git a/src/main/java/net/minecraft/client/model/ModelGhast.java b/src/game/java/net/minecraft/client/model/ModelGhast.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelGhast.java rename to src/game/java/net/minecraft/client/model/ModelGhast.java diff --git a/src/main/java/net/minecraft/client/model/ModelGuardian.java b/src/game/java/net/minecraft/client/model/ModelGuardian.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelGuardian.java rename to src/game/java/net/minecraft/client/model/ModelGuardian.java diff --git a/src/main/java/net/minecraft/client/model/ModelHorse.java b/src/game/java/net/minecraft/client/model/ModelHorse.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelHorse.java rename to src/game/java/net/minecraft/client/model/ModelHorse.java diff --git a/src/main/java/net/minecraft/client/model/ModelHumanoidHead.java b/src/game/java/net/minecraft/client/model/ModelHumanoidHead.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelHumanoidHead.java rename to src/game/java/net/minecraft/client/model/ModelHumanoidHead.java diff --git a/src/main/java/net/minecraft/client/model/ModelIronGolem.java b/src/game/java/net/minecraft/client/model/ModelIronGolem.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelIronGolem.java rename to src/game/java/net/minecraft/client/model/ModelIronGolem.java diff --git a/src/main/java/net/minecraft/client/model/ModelLargeChest.java b/src/game/java/net/minecraft/client/model/ModelLargeChest.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelLargeChest.java rename to src/game/java/net/minecraft/client/model/ModelLargeChest.java diff --git a/src/main/java/net/minecraft/client/model/ModelLeashKnot.java b/src/game/java/net/minecraft/client/model/ModelLeashKnot.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelLeashKnot.java rename to src/game/java/net/minecraft/client/model/ModelLeashKnot.java diff --git a/src/main/java/net/minecraft/client/model/ModelMagmaCube.java b/src/game/java/net/minecraft/client/model/ModelMagmaCube.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelMagmaCube.java rename to src/game/java/net/minecraft/client/model/ModelMagmaCube.java diff --git a/src/main/java/net/minecraft/client/model/ModelMinecart.java b/src/game/java/net/minecraft/client/model/ModelMinecart.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelMinecart.java rename to src/game/java/net/minecraft/client/model/ModelMinecart.java diff --git a/src/main/java/net/minecraft/client/model/ModelOcelot.java b/src/game/java/net/minecraft/client/model/ModelOcelot.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelOcelot.java rename to src/game/java/net/minecraft/client/model/ModelOcelot.java diff --git a/src/main/java/net/minecraft/client/model/ModelPig.java b/src/game/java/net/minecraft/client/model/ModelPig.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelPig.java rename to src/game/java/net/minecraft/client/model/ModelPig.java diff --git a/src/main/java/net/minecraft/client/model/ModelPlayer.java b/src/game/java/net/minecraft/client/model/ModelPlayer.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelPlayer.java rename to src/game/java/net/minecraft/client/model/ModelPlayer.java diff --git a/src/main/java/net/minecraft/client/model/ModelQuadruped.java b/src/game/java/net/minecraft/client/model/ModelQuadruped.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelQuadruped.java rename to src/game/java/net/minecraft/client/model/ModelQuadruped.java diff --git a/src/main/java/net/minecraft/client/model/ModelRabbit.java b/src/game/java/net/minecraft/client/model/ModelRabbit.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelRabbit.java rename to src/game/java/net/minecraft/client/model/ModelRabbit.java diff --git a/src/main/java/net/minecraft/client/model/ModelRenderer.java b/src/game/java/net/minecraft/client/model/ModelRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelRenderer.java rename to src/game/java/net/minecraft/client/model/ModelRenderer.java diff --git a/src/main/java/net/minecraft/client/model/ModelSheep1.java b/src/game/java/net/minecraft/client/model/ModelSheep1.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSheep1.java rename to src/game/java/net/minecraft/client/model/ModelSheep1.java diff --git a/src/main/java/net/minecraft/client/model/ModelSheep2.java b/src/game/java/net/minecraft/client/model/ModelSheep2.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSheep2.java rename to src/game/java/net/minecraft/client/model/ModelSheep2.java diff --git a/src/main/java/net/minecraft/client/model/ModelSign.java b/src/game/java/net/minecraft/client/model/ModelSign.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSign.java rename to src/game/java/net/minecraft/client/model/ModelSign.java diff --git a/src/main/java/net/minecraft/client/model/ModelSilverfish.java b/src/game/java/net/minecraft/client/model/ModelSilverfish.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSilverfish.java rename to src/game/java/net/minecraft/client/model/ModelSilverfish.java diff --git a/src/main/java/net/minecraft/client/model/ModelSkeleton.java b/src/game/java/net/minecraft/client/model/ModelSkeleton.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSkeleton.java rename to src/game/java/net/minecraft/client/model/ModelSkeleton.java diff --git a/src/main/java/net/minecraft/client/model/ModelSkeletonHead.java b/src/game/java/net/minecraft/client/model/ModelSkeletonHead.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSkeletonHead.java rename to src/game/java/net/minecraft/client/model/ModelSkeletonHead.java diff --git a/src/main/java/net/minecraft/client/model/ModelSlime.java b/src/game/java/net/minecraft/client/model/ModelSlime.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSlime.java rename to src/game/java/net/minecraft/client/model/ModelSlime.java diff --git a/src/main/java/net/minecraft/client/model/ModelSnowMan.java b/src/game/java/net/minecraft/client/model/ModelSnowMan.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSnowMan.java rename to src/game/java/net/minecraft/client/model/ModelSnowMan.java diff --git a/src/main/java/net/minecraft/client/model/ModelSpider.java b/src/game/java/net/minecraft/client/model/ModelSpider.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSpider.java rename to src/game/java/net/minecraft/client/model/ModelSpider.java diff --git a/src/main/java/net/minecraft/client/model/ModelSquid.java b/src/game/java/net/minecraft/client/model/ModelSquid.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelSquid.java rename to src/game/java/net/minecraft/client/model/ModelSquid.java diff --git a/src/main/java/net/minecraft/client/model/ModelVillager.java b/src/game/java/net/minecraft/client/model/ModelVillager.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelVillager.java rename to src/game/java/net/minecraft/client/model/ModelVillager.java diff --git a/src/main/java/net/minecraft/client/model/ModelWitch.java b/src/game/java/net/minecraft/client/model/ModelWitch.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelWitch.java rename to src/game/java/net/minecraft/client/model/ModelWitch.java diff --git a/src/main/java/net/minecraft/client/model/ModelWither.java b/src/game/java/net/minecraft/client/model/ModelWither.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelWither.java rename to src/game/java/net/minecraft/client/model/ModelWither.java diff --git a/src/main/java/net/minecraft/client/model/ModelWolf.java b/src/game/java/net/minecraft/client/model/ModelWolf.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelWolf.java rename to src/game/java/net/minecraft/client/model/ModelWolf.java diff --git a/src/main/java/net/minecraft/client/model/ModelZombie.java b/src/game/java/net/minecraft/client/model/ModelZombie.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelZombie.java rename to src/game/java/net/minecraft/client/model/ModelZombie.java diff --git a/src/main/java/net/minecraft/client/model/ModelZombieVillager.java b/src/game/java/net/minecraft/client/model/ModelZombieVillager.java similarity index 100% rename from src/main/java/net/minecraft/client/model/ModelZombieVillager.java rename to src/game/java/net/minecraft/client/model/ModelZombieVillager.java diff --git a/src/main/java/net/minecraft/client/model/PositionTextureVertex.java b/src/game/java/net/minecraft/client/model/PositionTextureVertex.java similarity index 100% rename from src/main/java/net/minecraft/client/model/PositionTextureVertex.java rename to src/game/java/net/minecraft/client/model/PositionTextureVertex.java diff --git a/src/main/java/net/minecraft/client/model/TextureOffset.java b/src/game/java/net/minecraft/client/model/TextureOffset.java similarity index 100% rename from src/main/java/net/minecraft/client/model/TextureOffset.java rename to src/game/java/net/minecraft/client/model/TextureOffset.java diff --git a/src/main/java/net/minecraft/client/model/TexturedQuad.java b/src/game/java/net/minecraft/client/model/TexturedQuad.java similarity index 100% rename from src/main/java/net/minecraft/client/model/TexturedQuad.java rename to src/game/java/net/minecraft/client/model/TexturedQuad.java diff --git a/src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java b/src/game/java/net/minecraft/client/multiplayer/ChunkProviderClient.java similarity index 95% rename from src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java rename to src/game/java/net/minecraft/client/multiplayer/ChunkProviderClient.java index 3a1487a..5c122d3 100644 --- a/src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java +++ b/src/game/java/net/minecraft/client/multiplayer/ChunkProviderClient.java @@ -4,6 +4,7 @@ import java.util.List; import com.google.common.collect.Lists; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.minecraft.entity.EnumCreatureType; @@ -146,15 +147,15 @@ public class ChunkProviderClient implements IChunkProvider { * guaranteed to unload every such chunk. */ public boolean unloadQueuedChunks() { - long i = System.currentTimeMillis(); + long i = EagRuntime.steadyTimeMillis(); for (int j = 0, k = this.chunkListing.size(); j < k; ++j) { - this.chunkListing.get(j).func_150804_b(System.currentTimeMillis() - i > 5L); + this.chunkListing.get(j).func_150804_b(EagRuntime.steadyTimeMillis() - i > 5L); } - if (System.currentTimeMillis() - i > 100L) { + if (EagRuntime.steadyTimeMillis() - i > 100L) { logger.info("Warning: Clientside chunk ticking took {} ms", - new Object[] { Long.valueOf(System.currentTimeMillis() - i) }); + new Object[] { Long.valueOf(EagRuntime.steadyTimeMillis() - i) }); } return false; diff --git a/src/main/java/net/minecraft/client/multiplayer/GuiConnecting.java b/src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java similarity index 62% rename from src/main/java/net/minecraft/client/multiplayer/GuiConnecting.java rename to src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java index 4f98526..6f28f64 100644 --- a/src/main/java/net/minecraft/client/multiplayer/GuiConnecting.java +++ b/src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java @@ -1,279 +1,335 @@ -package net.minecraft.client.multiplayer; - -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; -import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; -import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; -import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; -import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiDisconnected; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.client.resources.I18n; -import net.minecraft.network.EnumConnectionState; -import net.minecraft.util.ChatComponentText; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiConnecting extends GuiScreen { - private static final Logger logger = LogManager.getLogger(); - private EaglercraftNetworkManager networkManager; - private String currentAddress; - private String currentPassword; - private boolean allowPlaintext; - private boolean cancel; - private boolean hasOpened; - private final GuiScreen previousGuiScreen; - private int timer = 0; - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData) { - this(parGuiScreen, mcIn, parServerData, false); - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, boolean allowPlaintext) { - this(parGuiScreen, mcIn, parServerData, null, allowPlaintext); - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, String password) { - this(parGuiScreen, mcIn, parServerData, password, false); - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, String password, - boolean allowPlaintext) { - this.mc = mcIn; - this.previousGuiScreen = parGuiScreen; - String serveraddress = AddressResolver.resolveURI(parServerData); - mcIn.loadWorld((WorldClient) null); - mcIn.setServerData(parServerData); - if (RateLimitTracker.isLockedOut(serveraddress)) { - logger.error("Server locked this client out on a previous connection, will not attempt to reconnect"); - } else { - this.connect(serveraddress, password, allowPlaintext); - } - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port) { - this(parGuiScreen, mcIn, hostName, port, false); - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, boolean allowPlaintext) { - this(parGuiScreen, mcIn, hostName, port, null, allowPlaintext); - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, String password) { - this(parGuiScreen, mcIn, hostName, port, password, false); - } - - public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, String password, - boolean allowPlaintext) { - this.mc = mcIn; - this.previousGuiScreen = parGuiScreen; - mcIn.loadWorld((WorldClient) null); - this.connect(hostName, password, allowPlaintext); - } - - public GuiConnecting(GuiConnecting previous, String password) { - this(previous, password, false); - } - - public GuiConnecting(GuiConnecting previous, String password, boolean allowPlaintext) { - this.mc = previous.mc; - this.previousGuiScreen = previous.previousGuiScreen; - this.connect(previous.currentAddress, password, allowPlaintext); - } - - private void connect(String ip, String password, boolean allowPlaintext) { - this.currentAddress = ip; - this.currentPassword = password; - this.allowPlaintext = allowPlaintext; - } - - /** - * + - * Called from the main game loop to update the screen. - */ - public void updateScreen() { - ++timer; - if (timer > 1) { - if (this.currentAddress == null) { - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); - } else if (this.networkManager == null) { - logger.info("Connecting to: {}", currentAddress); - this.networkManager = new EaglercraftNetworkManager(currentAddress); - this.networkManager.connect(); - } else { - if (this.networkManager.isChannelOpen()) { - if (!hasOpened) { - hasOpened = true; - logger.info("Logging in: {}", currentAddress); - if (ConnectionHandshake.attemptHandshake(this.mc, this, previousGuiScreen, currentPassword, - allowPlaintext)) { - logger.info("Handshake Success"); - this.networkManager.setPluginInfo(ConnectionHandshake.pluginBrand, - ConnectionHandshake.pluginVersion); - mc.bungeeOutdatedMsgTimer = 80; - mc.clearTitles(); - this.networkManager.setConnectionState(EnumConnectionState.PLAY); - this.networkManager.setNetHandler(new NetHandlerPlayClient(this.mc, previousGuiScreen, - this.networkManager, this.mc.getSession().getProfile())); - } else { - if (mc.currentScreen == this) { - checkLowLevelRatelimit(); - } - if (mc.currentScreen == this) { - logger.info("Handshake Failure"); - mc.getSession().reset(); - mc.displayGuiScreen( - new GuiDisconnected(previousGuiScreen, "connect.failed", new ChatComponentText( - "Handshake Failure\n\nAre you sure this is an eagler 1.8 server?"))); - } - if (!PlatformNetworking.playConnectionState().isClosed()) { - PlatformNetworking.playDisconnect(); - } - return; - } - } - try { - this.networkManager.processReceivedPackets(); - } catch (IOException ex) { - } - } else { - if (PlatformNetworking.playConnectionState() == EnumEaglerConnectionState.FAILED) { - if (!hasOpened) { - mc.getSession().reset(); - checkLowLevelRatelimit(); - if (mc.currentScreen == this) { - if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); - } else { - mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", - new ChatComponentText("Connection Refused"))); - } - } - } - } else { - if (this.networkManager.checkDisconnected()) { - this.mc.getSession().reset(); - checkLowLevelRatelimit(); - if (mc.currentScreen == this) { - if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); - } else { - mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", - new ChatComponentText("Connection Refused"))); - } - } - } - } - } - } - } - - } - - /** - * + - * Fired when a key is typed (except F11 which toggles full - * screen). This is the equivalent of - * KeyListener.keyTyped(KeyEvent e). Args : character (character - * on the key), keyCode (lwjgl Keyboard key code) - */ - protected void keyTyped(char parChar1, int parInt1) { - } - - /** - * + - * Adds the buttons (and other controls) to the screen in - * question. Called when the GUI is displayed and when the - * window resizes, the buttonList is cleared beforehand. - */ - public void initGui() { - this.buttonList.clear(); - this.buttonList.add( - new GuiButton(0, this.width / 2 - 100, this.height / 2 - 10, I18n.format("gui.cancel", new Object[0]))); - } - - /** - * + - * Called by the controls from the buttonList when activated. - * (Mouse pressed for buttons) - */ - protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.id == 0) { - this.cancel = true; - if (this.networkManager != null) { - this.networkManager.closeChannel(new ChatComponentText("Aborted")); - } - - this.mc.displayGuiScreen(new GuiMainMenu()); - } - - } - - /** - * + - * Draws the screen and all the components in it. Args : mouseX, - * mouseY, renderPartialTicks - */ - public void drawScreen(int i, int j, float f) { - this.drawDefaultBackground(); - if (this.networkManager == null || !this.networkManager.isChannelOpen()) { - this.drawCenteredString(this.fontRendererObj, I18n.format("connect.connecting", new Object[0]), - this.width / 2, this.height / 2 - 50, 16777215); - } else { - this.drawCenteredString(this.fontRendererObj, I18n.format("connect.authorizing", new Object[0]), - this.width / 2, this.height / 2 - 50, 16777215); - } - - super.drawScreen(i, j, f); - } - - private void checkLowLevelRatelimit() { - EnumServerRateLimit rateLimit = PlatformNetworking.getRateLimit(); - if (rateLimit == EnumServerRateLimit.BLOCKED) { - RateLimitTracker.registerBlock(currentAddress); - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); - logger.info("Handshake Failure: Too Many Requests!"); - } else if (rateLimit == EnumServerRateLimit.LOCKED_OUT) { - RateLimitTracker.registerLockOut(currentAddress); - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); - logger.info("Handshake Failure: Too Many Requests!"); - logger.info("Server has locked this client out"); - } - } +package net.minecraft.client.multiplayer; + +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; +import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; +import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; +import net.lax1dude.eaglercraft.v1_8.socket.WebSocketNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiDisconnected; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.resources.I18n; +import net.minecraft.network.EnumConnectionState; +import net.minecraft.network.play.client.C17PacketCustomPayload; +import net.minecraft.util.ChatComponentText; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiConnecting extends GuiScreen { + private static final Logger logger = LogManager.getLogger(); + private IWebSocketClient webSocket; + private EaglercraftNetworkManager networkManager; + private String currentAddress; + private String currentPassword; + private boolean allowPlaintext; + private boolean allowCookies; + private boolean cancel; + private boolean hasOpened; + private final GuiScreen previousGuiScreen; + private int timer = 0; + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData) { + this(parGuiScreen, mcIn, parServerData, false); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, boolean allowPlaintext) { + this(parGuiScreen, mcIn, parServerData, null, allowPlaintext); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, String password) { + this(parGuiScreen, mcIn, parServerData, password, false); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, String password, + boolean allowPlaintext) { + this.mc = mcIn; + this.previousGuiScreen = parGuiScreen; + String serveraddress = AddressResolver.resolveURI(parServerData); + mcIn.loadWorld((WorldClient) null); + mcIn.setServerData(parServerData); + if (RateLimitTracker.isLockedOut(serveraddress)) { + logger.error("Server locked this client out on a previous connection, will not attempt to reconnect"); + } else { + this.connect(serveraddress, password, allowPlaintext, + parServerData.enableCookies && EagRuntime.getConfiguration().isEnableServerCookies()); + } + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port) { + this(parGuiScreen, mcIn, hostName, port, false, EagRuntime.getConfiguration().isEnableServerCookies()); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, boolean allowCookies) { + this(parGuiScreen, mcIn, hostName, port, false, allowCookies); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, boolean allowPlaintext, + boolean allowCookies) { + this(parGuiScreen, mcIn, hostName, port, null, allowPlaintext, allowCookies); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, String password, + boolean allowCookies) { + this(parGuiScreen, mcIn, hostName, port, password, false, allowCookies); + } + + public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, String password, + boolean allowPlaintext, boolean allowCookies) { + this.mc = mcIn; + this.previousGuiScreen = parGuiScreen; + mcIn.loadWorld((WorldClient) null); + this.connect(hostName, password, allowPlaintext, + allowCookies && EagRuntime.getConfiguration().isEnableServerCookies()); + } + + public GuiConnecting(GuiConnecting previous, String password) { + this(previous, password, false); + } + + public GuiConnecting(GuiConnecting previous, String password, boolean allowPlaintext) { + this.mc = previous.mc; + this.previousGuiScreen = previous.previousGuiScreen; + this.connect(previous.currentAddress, password, allowPlaintext, previous.allowCookies); + } + + private void connect(String ip, String password, boolean allowPlaintext, boolean allowCookies) { + this.currentAddress = ip; + this.currentPassword = password; + this.allowPlaintext = allowPlaintext; + this.allowCookies = allowCookies; + } + + /** + * + + * Called from the main game loop to update the screen. + */ + public void updateScreen() { + ++timer; + if (timer > 1) { + if (this.currentAddress == null) { + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); + } else if (webSocket == null) { + logger.info("Connecting to: {}", currentAddress); + webSocket = PlatformNetworking.openWebSocket(currentAddress); + if (webSocket == null) { + mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", + new ChatComponentText("Could not open WebSocket to \"" + currentAddress + "\"!"))); + } + } else { + if (webSocket.getState() == EnumEaglerConnectionState.CONNECTED) { + if (!hasOpened) { + hasOpened = true; + logger.info("Logging in: {}", currentAddress); + byte[] cookieData = null; + if (allowCookies) { + ServerCookieDataStore.ServerCookie cookie = ServerCookieDataStore + .loadCookie(currentAddress); + if (cookie != null) { + cookieData = cookie.cookie; + } + } + if (ConnectionHandshake.attemptHandshake(this.mc, webSocket, this, previousGuiScreen, + currentPassword, allowPlaintext, allowCookies, cookieData)) { + logger.info("Handshake Success"); + this.networkManager = new WebSocketNetworkManager(webSocket); + this.networkManager.setPluginInfo(ConnectionHandshake.pluginBrand, + ConnectionHandshake.pluginVersion); + mc.bungeeOutdatedMsgTimer = 80; + mc.clearTitles(); + this.networkManager.setConnectionState(EnumConnectionState.PLAY); + NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, previousGuiScreen, + this.networkManager, this.mc.getSession().getProfile()); + this.networkManager.setNetHandler(netHandler); + netHandler.setEaglerMessageController(new GameProtocolMessageController( + GamePluginMessageProtocol.getByVersion(ConnectionHandshake.protocolVersion), + GamePluginMessageConstants.CLIENT_TO_SERVER, + GameProtocolMessageController + .createClientHandler(ConnectionHandshake.protocolVersion, netHandler), + (ch, msg) -> netHandler.addToSendQueue(new C17PacketCustomPayload(ch, msg)))); + } else { + if (mc.currentScreen == this) { + checkRatelimit(); + logger.info("Handshake Failure"); + mc.getSession().reset(); + mc.displayGuiScreen( + new GuiDisconnected(previousGuiScreen, "connect.failed", new ChatComponentText( + "Handshake Failure\n\nAre you sure this is an eagler 1.8 server?"))); + } + webSocket.close(); + return; + } + } + if (this.networkManager != null) { + try { + this.networkManager.processReceivedPackets(); + } catch (IOException ex) { + } + } + } else { + if (webSocket.getState() == EnumEaglerConnectionState.FAILED) { + if (!hasOpened) { + mc.getSession().reset(); + checkRatelimit(); + if (mc.currentScreen == this) { + if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); + } else { + mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", + new ChatComponentText("Connection Refused"))); + } + } + } + } else { + if (this.networkManager != null && this.networkManager.checkDisconnected()) { + this.mc.getSession().reset(); + checkRatelimit(); + if (mc.currentScreen == this) { + if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); + } else { + mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", + new ChatComponentText("Connection Refused"))); + } + } + } + } + } + } + if (timer > 200) { + if (webSocket != null) { + webSocket.close(); + } + mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", + new ChatComponentText("Handshake timed out"))); + } + } + } + + /** + * + + * Fired when a key is typed (except F11 which toggles full + * screen). This is the equivalent of + * KeyListener.keyTyped(KeyEvent e). Args : character (character + * on the key), keyCode (lwjgl Keyboard key code) + */ + protected void keyTyped(char parChar1, int parInt1) { + } + + /** + * + + * Adds the buttons (and other controls) to the screen in + * question. Called when the GUI is displayed and when the + * window resizes, the buttonList is cleared beforehand. + */ + public void initGui() { + this.buttonList.clear(); + this.buttonList.add( + new GuiButton(0, this.width / 2 - 100, this.height / 2 - 10, I18n.format("gui.cancel", new Object[0]))); + } + + /** + * + + * Called by the controls from the buttonList when activated. + * (Mouse pressed for buttons) + */ + protected void actionPerformed(GuiButton parGuiButton) { + if (parGuiButton.id == 0) { + this.cancel = true; + if (this.networkManager != null) { + this.networkManager.closeChannel(new ChatComponentText("Aborted")); + } else if (this.webSocket != null) { + this.webSocket.close(); + } + + this.mc.displayGuiScreen(new GuiMainMenu()); + } + + } + + /** + * + + * Draws the screen and all the components in it. Args : mouseX, + * mouseY, renderPartialTicks + */ + public void drawScreen(int i, int j, float f) { + this.drawDefaultBackground(); + if (this.networkManager == null || !this.networkManager.isChannelOpen()) { + this.drawCenteredString(this.fontRendererObj, I18n.format("connect.connecting", new Object[0]), + this.width / 2, this.height / 2 - 50, 16777215); + } else { + this.drawCenteredString(this.fontRendererObj, I18n.format("connect.authorizing", new Object[0]), + this.width / 2, this.height / 2 - 50, 16777215); + } + + super.drawScreen(i, j, f); + } + + private void checkRatelimit() { + if (this.webSocket != null) { + List strFrames = webSocket.getNextStringFrames(); + if (strFrames != null) { + for (int i = 0; i < strFrames.size(); ++i) { + String str = strFrames.get(i).getString(); + if (str.equalsIgnoreCase("BLOCKED")) { + RateLimitTracker.registerBlock(currentAddress); + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); + logger.info("Handshake Failure: Too Many Requests!"); + } else if (str.equalsIgnoreCase("LOCKED")) { + RateLimitTracker.registerLockOut(currentAddress); + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); + logger.info("Handshake Failure: Too Many Requests!"); + logger.info("Server has locked this client out"); + } + } + } + } + } + + public boolean canCloseGui() { + return false; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java b/src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java similarity index 99% rename from src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java rename to src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java index aa3697f..74f5c08 100644 --- a/src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java +++ b/src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java @@ -2,6 +2,7 @@ package net.minecraft.client.multiplayer; import java.io.IOException; +import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; import net.minecraft.block.Block; import net.minecraft.block.material.Material; @@ -333,6 +334,8 @@ public class PlayerControllerMP { } this.netClientHandler.getSkinCache().flush(); this.netClientHandler.getCapeCache().flush(); + this.netClientHandler.getNotifManager().runTick(); + ClientUUIDLoadingCache.update(); } else { this.netClientHandler.getNetworkManager().checkDisconnected(); } diff --git a/src/main/java/net/minecraft/client/multiplayer/ServerAddress.java b/src/game/java/net/minecraft/client/multiplayer/ServerAddress.java similarity index 100% rename from src/main/java/net/minecraft/client/multiplayer/ServerAddress.java rename to src/game/java/net/minecraft/client/multiplayer/ServerAddress.java diff --git a/src/main/java/net/minecraft/client/multiplayer/ServerData.java b/src/game/java/net/minecraft/client/multiplayer/ServerData.java similarity index 94% rename from src/main/java/net/minecraft/client/multiplayer/ServerData.java rename to src/game/java/net/minecraft/client/multiplayer/ServerData.java index 424788f..39e65a8 100644 --- a/src/main/java/net/minecraft/client/multiplayer/ServerData.java +++ b/src/game/java/net/minecraft/client/multiplayer/ServerData.java @@ -5,6 +5,7 @@ import java.io.IOException; import org.json.JSONArray; import org.json.JSONObject; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery; import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -85,6 +86,7 @@ public class ServerData { public boolean hasPing = false; public boolean serverIconEnabled = false; public boolean isDefault = false; + public boolean enableCookies; private static final Logger logger = LogManager.getLogger("MOTDQuery"); @@ -95,6 +97,7 @@ public class ServerData { this.serverIP = parString2; this.field_181042_l = parFlag; this.iconResourceLocation = new ResourceLocation("eagler:servers/icons/tex_" + serverTextureId++); + this.enableCookies = EagRuntime.getConfiguration().isEnableServerCookies(); } /** @@ -114,6 +117,7 @@ public class ServerData { } nbttagcompound.setBoolean("hideAddress", this.hideAddress); + nbttagcompound.setBoolean("enableCookies", this.enableCookies); return nbttagcompound; } @@ -150,6 +154,12 @@ public class ServerData { serverdata.hideAddress = false; } + if (nbtCompound.hasKey("enableCookies", 1)) { + serverdata.enableCookies = nbtCompound.getBoolean("enableCookies"); + } else { + serverdata.enableCookies = true; + } + return serverdata; } @@ -163,6 +173,7 @@ public class ServerData { this.setResourceMode(serverDataIn.getResourceMode()); this.hideAddress = serverDataIn.hideAddress; this.field_181042_l = serverDataIn.field_181042_l; + this.enableCookies = serverDataIn.enableCookies; } public static enum ServerResourceMode { diff --git a/src/main/java/net/minecraft/client/multiplayer/ServerList.java b/src/game/java/net/minecraft/client/multiplayer/ServerList.java similarity index 97% rename from src/main/java/net/minecraft/client/multiplayer/ServerList.java rename to src/game/java/net/minecraft/client/multiplayer/ServerList.java index b4caa96..744ce8a 100644 --- a/src/main/java/net/minecraft/client/multiplayer/ServerList.java +++ b/src/game/java/net/minecraft/client/multiplayer/ServerList.java @@ -100,6 +100,7 @@ public class ServerList { for (DefaultServer srv : EagRuntime.getConfiguration().getDefaultServerList()) { ServerData dat = new ServerData(srv.name, srv.addr, true); dat.isDefault = true; + dat.hideAddress = srv.hideAddress; this.allServers.add(dat); } @@ -265,7 +266,7 @@ public class ServerList { for (int i = 0, l = this.servers.size(); i < l; ++i) { ServerData dat = this.servers.get(i); if (dat.pingSentTime <= 0l) { - dat.pingSentTime = System.currentTimeMillis(); + dat.pingSentTime = EagRuntime.steadyTimeMillis(); if (RateLimitTracker.isLockedOut(dat.serverIP)) { logger.error( "Server {} locked this client out on a previous connection, will not attempt to reconnect", @@ -287,6 +288,7 @@ public class ServerList { } } } else if (dat.currentQuery != null) { + dat.currentQuery.update(); if (!dat.hasPing) { ++total; EnumServerRateLimit rateLimit = dat.currentQuery.getRateLimit(); @@ -323,7 +325,7 @@ public class ServerList { dat.setIconPacket(r); } if (!dat.currentQuery.isOpen() && dat.pingSentTime > 0l - && (System.currentTimeMillis() - dat.pingSentTime) > 2000l && !dat.hasPing) { + && (EagRuntime.steadyTimeMillis() - dat.pingSentTime) > 2000l && !dat.hasPing) { if (RateLimitTracker.isProbablyLockedOut(dat.serverIP)) { logger.error("Server {} ratelimited this client out on a previous connection, assuming lockout", dat.serverIP); diff --git a/src/main/java/net/minecraft/client/multiplayer/WorldClient.java b/src/game/java/net/minecraft/client/multiplayer/WorldClient.java similarity index 97% rename from src/main/java/net/minecraft/client/multiplayer/WorldClient.java rename to src/game/java/net/minecraft/client/multiplayer/WorldClient.java index 211e373..d4e1677 100644 --- a/src/main/java/net/minecraft/client/multiplayer/WorldClient.java +++ b/src/game/java/net/minecraft/client/multiplayer/WorldClient.java @@ -20,7 +20,6 @@ import net.minecraft.entity.item.EntityMinecart; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.profiler.Profiler; import net.minecraft.scoreboard.Scoreboard; import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentText; @@ -87,9 +86,9 @@ public class WorldClient extends World { private final Set previousActiveChunkSet = Sets.newHashSet(); public WorldClient(NetHandlerPlayClient parNetHandlerPlayClient, WorldSettings parWorldSettings, int parInt1, - EnumDifficulty parEnumDifficulty, Profiler parProfiler) { + EnumDifficulty parEnumDifficulty) { super(new SaveHandlerMP(), new WorldInfo(parWorldSettings, "MpServer"), - WorldProvider.getProviderForDimension(parInt1), parProfiler, true); + WorldProvider.getProviderForDimension(parInt1), true); this.sendQueue = parNetHandlerPlayClient; this.getWorldInfo().setDifficulty(parEnumDifficulty); this.setSpawnPoint(new BlockPos(8, 64, 8)); @@ -111,8 +110,6 @@ public class WorldClient extends World { this.setWorldTime(this.getWorldTime() + 1L); } - this.theProfiler.startSection("reEntryProcessing"); - for (int i = 0; i < 10 && !this.entitySpawnQueue.isEmpty(); ++i) { Entity entity = (Entity) this.entitySpawnQueue.iterator().next(); this.entitySpawnQueue.remove(entity); @@ -121,11 +118,8 @@ public class WorldClient extends World { } } - this.theProfiler.endStartSection("chunkCache"); this.clientChunkProvider.unloadQueuedChunks(); - this.theProfiler.endStartSection("blocks"); this.updateBlocks(); - this.theProfiler.endSection(); } /** @@ -161,10 +155,8 @@ public class WorldClient extends World { if (!this.previousActiveChunkSet.contains(chunkcoordintpair)) { int j = chunkcoordintpair.chunkXPos * 16; int k = chunkcoordintpair.chunkZPos * 16; - this.theProfiler.startSection("getChunk"); Chunk chunk = this.getChunkFromChunkCoords(chunkcoordintpair.chunkXPos, chunkcoordintpair.chunkZPos); this.playMoodSoundAndCheckLight(j, k, chunk); - this.theProfiler.endSection(); this.previousActiveChunkSet.add(chunkcoordintpair); ++i; if (i >= 10) { diff --git a/src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java b/src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java similarity index 96% rename from src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java rename to src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java index 749f266..bb2a791 100644 --- a/src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java +++ b/src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java @@ -6,22 +6,24 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import com.google.common.collect.Maps; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.lax1dude.eaglercraft.v1_8.profile.CapePackets; +import net.lax1dude.eaglercraft.v1_8.notifications.ServerNotificationManager; import net.lax1dude.eaglercraft.v1_8.profile.ServerCapeCache; import net.lax1dude.eaglercraft.v1_8.profile.ServerSkinCache; -import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; @@ -268,7 +270,12 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { private final EaglercraftRandom avRandomizer = new EaglercraftRandom(); private final ServerSkinCache skinCache; private final ServerCapeCache capeCache; + private final ServerNotificationManager notifManager; public boolean currentFNAWSkinAllowedState = true; + public boolean currentFNAWSkinForcedState = true; + private GameProtocolMessageController eaglerMessageController = null; + public boolean hasRequestedServerInfo = false; + public byte[] cachedServerInfoData = null; public NetHandlerPlayClient(Minecraft mcIn, GuiScreen parGuiScreen, EaglercraftNetworkManager parNetworkManager, GameProfile parGameProfile) { @@ -276,8 +283,9 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.guiScreenServer = parGuiScreen; this.netManager = parNetworkManager; this.profile = parGameProfile; - this.skinCache = new ServerSkinCache(parNetworkManager, mcIn.getTextureManager()); - this.capeCache = new ServerCapeCache(parNetworkManager, mcIn.getTextureManager()); + this.skinCache = new ServerSkinCache(this, mcIn.getTextureManager()); + this.capeCache = new ServerCapeCache(this, mcIn.getTextureManager()); + this.notifManager = new ServerNotificationManager(); this.isIntegratedServer = (parNetworkManager instanceof ClientIntegratedServerNetworkManager) || (parNetworkManager instanceof LANClientNetworkManager); } @@ -291,6 +299,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.clientWorldController = null; this.skinCache.destroy(); this.capeCache.destroy(); + this.notifManager.destroy(); } public ServerSkinCache getSkinCache() { @@ -301,8 +310,48 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { return this.capeCache; } - /** - * + + public ServerNotificationManager getNotifManager() { + return this.notifManager; + } + + public GameProtocolMessageController getEaglerMessageController() { + return eaglerMessageController; + } + + public void setEaglerMessageController(GameProtocolMessageController eaglerMessageController) { + this.eaglerMessageController = eaglerMessageController; + } + + public GamePluginMessageProtocol getEaglerMessageProtocol() { + return eaglerMessageController != null ? eaglerMessageController.protocol : null; + } + + public void sendEaglerMessage(GameMessagePacket packet) { + try { + eaglerMessageController.sendPacket(packet); + } catch (IOException e) { + logger.error("Failed to send eaglercraft plugin message packet: " + packet); + logger.error(e); + } + } + + public boolean webViewSendHandler(GameMessagePacket pkt) { + if (eaglerMessageController == null) { + return false; + } + if (this.gameController.thePlayer == null || this.gameController.thePlayer.sendQueue != this) { + logger.error("WebView sent message on a dead handler!"); + return false; + } + if (eaglerMessageController.protocol.ver >= 4) { + sendEaglerMessage(pkt); + return true; + } else { + return false; + } + } + + /**+ * Registers some server properties * (gametype,hardcore-mode,terraintype,difficulty,player limit), * creates a new WorldClient and sets the player initial @@ -310,10 +359,8 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { */ public void handleJoinGame(S01PacketJoinGame packetIn) { this.gameController.playerController = new PlayerControllerMP(this.gameController, this); - this.clientWorldController = new WorldClient(this, - new WorldSettings(0L, packetIn.getGameType(), false, packetIn.isHardcoreMode(), - packetIn.getWorldType()), - packetIn.getDimension(), packetIn.getDifficulty(), this.gameController.mcProfiler); + this.clientWorldController = new WorldClient(this, new WorldSettings(0L, packetIn.getGameType(), false, + packetIn.isHardcoreMode(), packetIn.getWorldType()), packetIn.getDimension(), packetIn.getDifficulty()); this.gameController.gameSettings.difficulty = packetIn.getDifficulty(); this.gameController.loadWorld(this.clientWorldController); this.gameController.thePlayer.dimension = packetIn.getDimension(); @@ -326,9 +373,9 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.netManager.sendPacket(new C17PacketCustomPayload("MC|Brand", (new PacketBuffer(Unpooled.buffer())).writeString(ClientBrandRetriever.getClientModName()))); if (VoiceClientController.isClientSupported()) { - VoiceClientController.initializeVoiceClient((pkt) -> this.netManager - .sendPacket(new C17PacketCustomPayload(VoiceClientController.SIGNAL_CHANNEL, pkt))); + VoiceClientController.initializeVoiceClient(this::sendEaglerMessage, eaglerMessageController.protocol.ver); } + WebViewOverlayController.setPacketSendCallback(this::webViewSendHandler); } /** @@ -989,7 +1036,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { Scoreboard scoreboard = this.clientWorldController.getScoreboard(); this.clientWorldController = new WorldClient(this, new WorldSettings(0L, packetIn.getGameType(), false, this.gameController.theWorld.getWorldInfo().isHardcoreModeEnabled(), packetIn.getWorldType()), - packetIn.getDimensionID(), packetIn.getDifficulty(), this.gameController.mcProfiler); + packetIn.getDimensionID(), packetIn.getDifficulty()); this.clientWorldController.setWorldScoreboard(scoreboard); this.gameController.loadWorld(this.clientWorldController); this.gameController.thePlayer.dimension = packetIn.getDimensionID(); @@ -1146,6 +1193,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { if (tileentitysign.getIsEditable()) { System.arraycopy(packetIn.getLines(), 0, tileentitysign.signText, 0, 4); tileentitysign.markDirty(); + tileentitysign.clearProfanityFilterCache(); } flag = true; @@ -1442,6 +1490,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.playerInfoMap.remove(uuid); this.skinCache.evictSkin(uuid); this.capeCache.evictCape(uuid); + ClientUUIDLoadingCache.evict(uuid); } else { NetworkPlayerInfo networkplayerinfo = (NetworkPlayerInfo) this.playerInfoMap .get(s38packetplayerlistitem$addplayerdata.getProfile().getId()); @@ -1611,45 +1660,18 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.gameController .displayGuiScreen(new GuiScreenBook(this.gameController.thePlayer, itemstack, false)); } - } else if ("EAG|Skins-1.8".equals(packetIn.getChannelName())) { + } else { try { - SkinPackets.readPluginMessage(packetIn.getBufferData(), skinCache); + eaglerMessageController.handlePacket(packetIn.getChannelName(), packetIn.getBufferData()); } catch (IOException e) { - logger.error("Couldn't read EAG|Skins-1.8 packet!"); + logger.error("Couldn't read \"{}\" packet as an eaglercraft plugin message!", + packetIn.getChannelName()); logger.error(e); } - } else if ("EAG|Capes-1.8".equals(packetIn.getChannelName())) { - try { - CapePackets.readPluginMessage(packetIn.getBufferData(), capeCache); - } catch (IOException e) { - logger.error("Couldn't read EAG|Capes-1.8 packet!"); - logger.error(e); - } - } else if ("EAG|UpdateCert-1.8".equals(packetIn.getChannelName())) { - if (EagRuntime.getConfiguration().allowUpdateSvc()) { - try { - PacketBuffer pb = packetIn.getBufferData(); - byte[] c = new byte[pb.readableBytes()]; - pb.readBytes(c); - UpdateService.addCertificateToSet(c); - } catch (Throwable e) { - logger.error("Couldn't process EAG|UpdateCert-1.8 packet!"); - logger.error(e); - } - } - } else if (VoiceClientController.SIGNAL_CHANNEL.equals(packetIn.getChannelName())) { - if (VoiceClientController.isClientSupported()) { - VoiceClientController.handleVoiceSignalPacket(packetIn.getBufferData()); - } - } else if ("EAG|FNAWSEn-1.8".equals(packetIn.getChannelName())) { - this.currentFNAWSkinAllowedState = packetIn.getBufferData().readBoolean(); - Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins( - this.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins); } } - /** - * + + /**+ * May create a scoreboard objective, remove an objective from * the scoreboard or update an objectives' displayname */ diff --git a/src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java b/src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java similarity index 78% rename from src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java rename to src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java index 52a9cd5..b685db2 100644 --- a/src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java +++ b/src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java @@ -1,162 +1,195 @@ -package net.minecraft.client.network; - -import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; -import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; -import net.minecraft.client.Minecraft; -import net.minecraft.network.play.server.S38PacketPlayerListItem; -import net.minecraft.scoreboard.ScorePlayerTeam; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.WorldSettings; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class NetworkPlayerInfo { - private final GameProfile gameProfile; - private WorldSettings.GameType gameType; - private int responseTime; - private String skinType; - private IChatComponent displayName; - private int field_178873_i = 0; - private int field_178870_j = 0; - private long field_178871_k = 0L; - private long field_178868_l = 0L; - private long field_178869_m = 0L; - - public NetworkPlayerInfo(GameProfile parGameProfile) { - this.gameProfile = parGameProfile; - } - - public NetworkPlayerInfo(S38PacketPlayerListItem.AddPlayerData parAddPlayerData) { - this.gameProfile = parAddPlayerData.getProfile(); - this.gameType = parAddPlayerData.getGameMode(); - this.responseTime = parAddPlayerData.getPing(); - this.displayName = parAddPlayerData.getDisplayName(); - } - - /** - * + - * Returns the GameProfile for the player represented by this - * NetworkPlayerInfo instance - */ - public GameProfile getGameProfile() { - return this.gameProfile; - } - - public WorldSettings.GameType getGameType() { - return this.gameType; - } - - public int getResponseTime() { - return this.responseTime; - } - - protected void setGameType(WorldSettings.GameType parGameType) { - this.gameType = parGameType; - } - - protected void setResponseTime(int parInt1) { - this.responseTime = parInt1; - } - - public boolean hasLocationSkin() { - return true; - } - - public String getSkinType() { - return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile) - .getSkinModel().profileSkinType; - } - - public SkinModel getEaglerSkinModel() { - return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getSkinModel(); - } - - public ResourceLocation getLocationSkin() { - return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getResourceLocation(); - } - - public ResourceLocation getLocationCape() { - return Minecraft.getMinecraft().getNetHandler().getCapeCache().getCape(this.gameProfile.getId()) - .getResourceLocation(); - } - - public ScorePlayerTeam getPlayerTeam() { - return Minecraft.getMinecraft().theWorld.getScoreboard().getPlayersTeam(this.getGameProfile().getName()); - } - - public void setDisplayName(IChatComponent displayNameIn) { - this.displayName = displayNameIn; - } - - public IChatComponent getDisplayName() { - return this.displayName; - } - - public int func_178835_l() { - return this.field_178873_i; - } - - public void func_178836_b(int parInt1) { - this.field_178873_i = parInt1; - } - - public int func_178860_m() { - return this.field_178870_j; - } - - public void func_178857_c(int parInt1) { - this.field_178870_j = parInt1; - } - - public long func_178847_n() { - return this.field_178871_k; - } - - public void func_178846_a(long parLong1) { - this.field_178871_k = parLong1; - } - - public long func_178858_o() { - return this.field_178868_l; - } - - public void func_178844_b(long parLong1) { - this.field_178868_l = parLong1; - } - - public long func_178855_p() { - return this.field_178869_m; - } - - public void func_178843_c(long parLong1) { - this.field_178869_m = parLong1; - } +package net.minecraft.client.network; + +import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.minecraft.client.Minecraft; +import net.minecraft.network.play.server.S38PacketPlayerListItem; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.WorldSettings; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class NetworkPlayerInfo { + private final GameProfile gameProfile; + private String gameProfileProfanityFilter; + private WorldSettings.GameType gameType; + private int responseTime; + private String skinType; + private IChatComponent displayName; + private IChatComponent displayNameProfanityFilter; + private int field_178873_i = 0; + private int field_178870_j = 0; + private long field_178871_k = 0L; + private long field_178868_l = 0L; + private long field_178869_m = 0L; + + public NetworkPlayerInfo(GameProfile parGameProfile) { + this.gameProfile = parGameProfile; + } + + public NetworkPlayerInfo(S38PacketPlayerListItem.AddPlayerData parAddPlayerData) { + this.gameProfile = parAddPlayerData.getProfile(); + this.gameType = parAddPlayerData.getGameMode(); + this.responseTime = parAddPlayerData.getPing(); + this.displayName = parAddPlayerData.getDisplayName(); + this.displayNameProfanityFilter = null; + } + + /** + * + + * Returns the GameProfile for the player represented by this + * NetworkPlayerInfo instance + */ + public GameProfile getGameProfile() { + return this.gameProfile; + } + + public WorldSettings.GameType getGameType() { + return this.gameType; + } + + public int getResponseTime() { + return this.responseTime; + } + + protected void setGameType(WorldSettings.GameType parGameType) { + this.gameType = parGameType; + } + + protected void setResponseTime(int parInt1) { + this.responseTime = parInt1; + } + + public boolean hasLocationSkin() { + return true; + } + + public String getSkinType() { + return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile) + .getSkinModel().profileSkinType; + } + + public SkinModel getEaglerSkinModel() { + return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getSkinModel(); + } + + public ResourceLocation getLocationSkin() { + return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getResourceLocation(); + } + + public ResourceLocation getLocationCape() { + return Minecraft.getMinecraft().getNetHandler().getCapeCache().getCape(this.gameProfile.getId()) + .getResourceLocation(); + } + + public ScorePlayerTeam getPlayerTeam() { + return Minecraft.getMinecraft().theWorld.getScoreboard().getPlayersTeam(this.getGameProfile().getName()); + } + + public void setDisplayName(IChatComponent displayNameIn) { + this.displayName = displayNameIn; + this.displayNameProfanityFilter = null; + } + + public IChatComponent getDisplayName() { + return this.displayName; + } + + public IChatComponent getDisplayNameProfanityFilter() { + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (this.displayName != null) { + if (this.displayNameProfanityFilter == null) { + this.displayNameProfanityFilter = ProfanityFilter.getInstance() + .profanityFilterChatComponent(this.displayName); + } + return this.displayNameProfanityFilter; + } else { + return null; + } + } else { + return this.displayName; + } + } + + public String getGameProfileNameProfanityFilter() { + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (this.gameProfileProfanityFilter == null) { + this.gameProfileProfanityFilter = ProfanityFilter.getInstance() + .profanityFilterString(this.gameProfile.getName()); + } + return this.gameProfileProfanityFilter; + } else { + return this.gameProfile.getName(); + } + } + + public int func_178835_l() { + return this.field_178873_i; + } + + public void func_178836_b(int parInt1) { + this.field_178873_i = parInt1; + } + + public int func_178860_m() { + return this.field_178870_j; + } + + public void func_178857_c(int parInt1) { + this.field_178870_j = parInt1; + } + + public long func_178847_n() { + return this.field_178871_k; + } + + public void func_178846_a(long parLong1) { + this.field_178871_k = parLong1; + } + + public long func_178858_o() { + return this.field_178868_l; + } + + public void func_178844_b(long parLong1) { + this.field_178868_l = parLong1; + } + + public long func_178855_p() { + return this.field_178869_m; + } + + public void func_178843_c(long parLong1) { + this.field_178869_m = parLong1; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/particle/Barrier.java b/src/game/java/net/minecraft/client/particle/Barrier.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/Barrier.java rename to src/game/java/net/minecraft/client/particle/Barrier.java diff --git a/src/main/java/net/minecraft/client/particle/EffectRenderer.java b/src/game/java/net/minecraft/client/particle/EffectRenderer.java similarity index 97% rename from src/main/java/net/minecraft/client/particle/EffectRenderer.java rename to src/game/java/net/minecraft/client/particle/EffectRenderer.java index cbe77f5..96facb0 100644 --- a/src/main/java/net/minecraft/client/particle/EffectRenderer.java +++ b/src/game/java/net/minecraft/client/particle/EffectRenderer.java @@ -16,6 +16,7 @@ import java.util.concurrent.Callable; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; @@ -85,7 +86,7 @@ public class EffectRenderer { private Map particleTypes = Maps.newHashMap(); public static final AcceleratedEffectRenderer vanillaAcceleratedParticleRenderer = new AcceleratedEffectRenderer(); - public IAcceleratedParticleEngine acceleratedParticleRenderer = vanillaAcceleratedParticleRenderer; + public IAcceleratedParticleEngine acceleratedParticleRenderer = null; public EffectRenderer(World worldIn, TextureManager rendererIn) { this.worldObj = worldIn; @@ -100,6 +101,8 @@ public class EffectRenderer { } this.registerVanillaParticles(); + this.acceleratedParticleRenderer = EaglercraftGPU.checkInstancingCapable() ? vanillaAcceleratedParticleRenderer + : null; } private void registerVanillaParticles() { @@ -331,14 +334,17 @@ public class EffectRenderer { boolean legacyRenderingHasOccured = false; - acceleratedParticleRenderer.begin(partialTicks); + if (acceleratedParticleRenderer != null) { + acceleratedParticleRenderer.begin(partialTicks); + } for (int k = 0; k < this.fxLayers[i][j].size(); ++k) { final EntityFX entityfx = (EntityFX) this.fxLayers[i][j].get(k); try { - if (!entityfx.renderAccelerated(acceleratedParticleRenderer, entityIn, partialTicks, f, f4, - f1, f2, f3)) { + if (acceleratedParticleRenderer == null + || !entityfx.renderAccelerated(acceleratedParticleRenderer, entityIn, partialTicks, + f, f4, f1, f2, f3)) { entityfx.renderParticle(worldrenderer, entityIn, partialTicks, f, f4, f1, f2, f3); legacyRenderingHasOccured = true; } @@ -369,7 +375,9 @@ public class EffectRenderer { worldrenderer.finishDrawing(); } - acceleratedParticleRenderer.draw(texCoordWidth, texCoordHeight); + if (acceleratedParticleRenderer != null) { + acceleratedParticleRenderer.draw(texCoordWidth, texCoordHeight); + } } } } diff --git a/src/main/java/net/minecraft/client/particle/EntityAuraFX.java b/src/game/java/net/minecraft/client/particle/EntityAuraFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityAuraFX.java rename to src/game/java/net/minecraft/client/particle/EntityAuraFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityBlockDustFX.java b/src/game/java/net/minecraft/client/particle/EntityBlockDustFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityBlockDustFX.java rename to src/game/java/net/minecraft/client/particle/EntityBlockDustFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityBreakingFX.java b/src/game/java/net/minecraft/client/particle/EntityBreakingFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityBreakingFX.java rename to src/game/java/net/minecraft/client/particle/EntityBreakingFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityBubbleFX.java b/src/game/java/net/minecraft/client/particle/EntityBubbleFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityBubbleFX.java rename to src/game/java/net/minecraft/client/particle/EntityBubbleFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityCloudFX.java b/src/game/java/net/minecraft/client/particle/EntityCloudFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityCloudFX.java rename to src/game/java/net/minecraft/client/particle/EntityCloudFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityCrit2FX.java b/src/game/java/net/minecraft/client/particle/EntityCrit2FX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityCrit2FX.java rename to src/game/java/net/minecraft/client/particle/EntityCrit2FX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityCritFX.java b/src/game/java/net/minecraft/client/particle/EntityCritFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityCritFX.java rename to src/game/java/net/minecraft/client/particle/EntityCritFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityDiggingFX.java b/src/game/java/net/minecraft/client/particle/EntityDiggingFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityDiggingFX.java rename to src/game/java/net/minecraft/client/particle/EntityDiggingFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityDropParticleFX.java b/src/game/java/net/minecraft/client/particle/EntityDropParticleFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityDropParticleFX.java rename to src/game/java/net/minecraft/client/particle/EntityDropParticleFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.java b/src/game/java/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.java rename to src/game/java/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityExplodeFX.java b/src/game/java/net/minecraft/client/particle/EntityExplodeFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityExplodeFX.java rename to src/game/java/net/minecraft/client/particle/EntityExplodeFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityFX.java b/src/game/java/net/minecraft/client/particle/EntityFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityFX.java rename to src/game/java/net/minecraft/client/particle/EntityFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityFirework.java b/src/game/java/net/minecraft/client/particle/EntityFirework.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityFirework.java rename to src/game/java/net/minecraft/client/particle/EntityFirework.java diff --git a/src/main/java/net/minecraft/client/particle/EntityFishWakeFX.java b/src/game/java/net/minecraft/client/particle/EntityFishWakeFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityFishWakeFX.java rename to src/game/java/net/minecraft/client/particle/EntityFishWakeFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityFlameFX.java b/src/game/java/net/minecraft/client/particle/EntityFlameFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityFlameFX.java rename to src/game/java/net/minecraft/client/particle/EntityFlameFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityFootStepFX.java b/src/game/java/net/minecraft/client/particle/EntityFootStepFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityFootStepFX.java rename to src/game/java/net/minecraft/client/particle/EntityFootStepFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityHeartFX.java b/src/game/java/net/minecraft/client/particle/EntityHeartFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityHeartFX.java rename to src/game/java/net/minecraft/client/particle/EntityHeartFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityHugeExplodeFX.java b/src/game/java/net/minecraft/client/particle/EntityHugeExplodeFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityHugeExplodeFX.java rename to src/game/java/net/minecraft/client/particle/EntityHugeExplodeFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityLargeExplodeFX.java b/src/game/java/net/minecraft/client/particle/EntityLargeExplodeFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityLargeExplodeFX.java rename to src/game/java/net/minecraft/client/particle/EntityLargeExplodeFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityLavaFX.java b/src/game/java/net/minecraft/client/particle/EntityLavaFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityLavaFX.java rename to src/game/java/net/minecraft/client/particle/EntityLavaFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityNoteFX.java b/src/game/java/net/minecraft/client/particle/EntityNoteFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityNoteFX.java rename to src/game/java/net/minecraft/client/particle/EntityNoteFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityParticleEmitter.java b/src/game/java/net/minecraft/client/particle/EntityParticleEmitter.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityParticleEmitter.java rename to src/game/java/net/minecraft/client/particle/EntityParticleEmitter.java diff --git a/src/main/java/net/minecraft/client/particle/EntityPickupFX.java b/src/game/java/net/minecraft/client/particle/EntityPickupFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityPickupFX.java rename to src/game/java/net/minecraft/client/particle/EntityPickupFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityPortalFX.java b/src/game/java/net/minecraft/client/particle/EntityPortalFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityPortalFX.java rename to src/game/java/net/minecraft/client/particle/EntityPortalFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityRainFX.java b/src/game/java/net/minecraft/client/particle/EntityRainFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityRainFX.java rename to src/game/java/net/minecraft/client/particle/EntityRainFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntityReddustFX.java b/src/game/java/net/minecraft/client/particle/EntityReddustFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntityReddustFX.java rename to src/game/java/net/minecraft/client/particle/EntityReddustFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntitySmokeFX.java b/src/game/java/net/minecraft/client/particle/EntitySmokeFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntitySmokeFX.java rename to src/game/java/net/minecraft/client/particle/EntitySmokeFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntitySnowShovelFX.java b/src/game/java/net/minecraft/client/particle/EntitySnowShovelFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntitySnowShovelFX.java rename to src/game/java/net/minecraft/client/particle/EntitySnowShovelFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntitySpellParticleFX.java b/src/game/java/net/minecraft/client/particle/EntitySpellParticleFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntitySpellParticleFX.java rename to src/game/java/net/minecraft/client/particle/EntitySpellParticleFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntitySplashFX.java b/src/game/java/net/minecraft/client/particle/EntitySplashFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntitySplashFX.java rename to src/game/java/net/minecraft/client/particle/EntitySplashFX.java diff --git a/src/main/java/net/minecraft/client/particle/EntitySuspendFX.java b/src/game/java/net/minecraft/client/particle/EntitySuspendFX.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/EntitySuspendFX.java rename to src/game/java/net/minecraft/client/particle/EntitySuspendFX.java diff --git a/src/main/java/net/minecraft/client/particle/IParticleFactory.java b/src/game/java/net/minecraft/client/particle/IParticleFactory.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/IParticleFactory.java rename to src/game/java/net/minecraft/client/particle/IParticleFactory.java diff --git a/src/main/java/net/minecraft/client/particle/MobAppearance.java b/src/game/java/net/minecraft/client/particle/MobAppearance.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/MobAppearance.java rename to src/game/java/net/minecraft/client/particle/MobAppearance.java diff --git a/src/main/java/net/minecraft/client/particle/ParticleDragonBreath.java b/src/game/java/net/minecraft/client/particle/ParticleDragonBreath.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/ParticleDragonBreath.java rename to src/game/java/net/minecraft/client/particle/ParticleDragonBreath.java diff --git a/src/main/java/net/minecraft/client/particle/ParticleEndRod.java b/src/game/java/net/minecraft/client/particle/ParticleEndRod.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/ParticleEndRod.java rename to src/game/java/net/minecraft/client/particle/ParticleEndRod.java diff --git a/src/main/java/net/minecraft/client/particle/ParticleSimpleAnimated.java b/src/game/java/net/minecraft/client/particle/ParticleSimpleAnimated.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/ParticleSimpleAnimated.java rename to src/game/java/net/minecraft/client/particle/ParticleSimpleAnimated.java diff --git a/src/main/java/net/minecraft/client/particle/ParticleSweepAttack.java b/src/game/java/net/minecraft/client/particle/ParticleSweepAttack.java similarity index 100% rename from src/main/java/net/minecraft/client/particle/ParticleSweepAttack.java rename to src/game/java/net/minecraft/client/particle/ParticleSweepAttack.java diff --git a/src/main/java/net/minecraft/client/player/inventory/ContainerLocalMenu.java b/src/game/java/net/minecraft/client/player/inventory/ContainerLocalMenu.java similarity index 100% rename from src/main/java/net/minecraft/client/player/inventory/ContainerLocalMenu.java rename to src/game/java/net/minecraft/client/player/inventory/ContainerLocalMenu.java diff --git a/src/main/java/net/minecraft/client/player/inventory/LocalBlockIntercommunication.java b/src/game/java/net/minecraft/client/player/inventory/LocalBlockIntercommunication.java similarity index 100% rename from src/main/java/net/minecraft/client/player/inventory/LocalBlockIntercommunication.java rename to src/game/java/net/minecraft/client/player/inventory/LocalBlockIntercommunication.java diff --git a/src/main/java/net/minecraft/client/renderer/ActiveRenderInfo.java b/src/game/java/net/minecraft/client/renderer/ActiveRenderInfo.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ActiveRenderInfo.java rename to src/game/java/net/minecraft/client/renderer/ActiveRenderInfo.java diff --git a/src/main/java/net/minecraft/client/renderer/BlockFluidRenderer.java b/src/game/java/net/minecraft/client/renderer/BlockFluidRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/BlockFluidRenderer.java rename to src/game/java/net/minecraft/client/renderer/BlockFluidRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/BlockModelRenderer.java b/src/game/java/net/minecraft/client/renderer/BlockModelRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/BlockModelRenderer.java rename to src/game/java/net/minecraft/client/renderer/BlockModelRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/BlockModelShapes.java b/src/game/java/net/minecraft/client/renderer/BlockModelShapes.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/BlockModelShapes.java rename to src/game/java/net/minecraft/client/renderer/BlockModelShapes.java diff --git a/src/main/java/net/minecraft/client/renderer/BlockRendererDispatcher.java b/src/game/java/net/minecraft/client/renderer/BlockRendererDispatcher.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/BlockRendererDispatcher.java rename to src/game/java/net/minecraft/client/renderer/BlockRendererDispatcher.java diff --git a/src/main/java/net/minecraft/client/renderer/ChestRenderer.java b/src/game/java/net/minecraft/client/renderer/ChestRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ChestRenderer.java rename to src/game/java/net/minecraft/client/renderer/ChestRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/ChunkRenderContainer.java b/src/game/java/net/minecraft/client/renderer/ChunkRenderContainer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ChunkRenderContainer.java rename to src/game/java/net/minecraft/client/renderer/ChunkRenderContainer.java diff --git a/src/main/java/net/minecraft/client/renderer/DestroyBlockProgress.java b/src/game/java/net/minecraft/client/renderer/DestroyBlockProgress.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/DestroyBlockProgress.java rename to src/game/java/net/minecraft/client/renderer/DestroyBlockProgress.java diff --git a/src/main/java/net/minecraft/client/renderer/EntityRenderer.java b/src/game/java/net/minecraft/client/renderer/EntityRenderer.java similarity index 95% rename from src/main/java/net/minecraft/client/renderer/EntityRenderer.java rename to src/game/java/net/minecraft/client/renderer/EntityRenderer.java index 451e0ae..cf5caff 100644 --- a/src/main/java/net/minecraft/client/renderer/EntityRenderer.java +++ b/src/game/java/net/minecraft/client/renderer/EntityRenderer.java @@ -8,6 +8,8 @@ import java.util.Arrays; import java.util.List; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; + import java.util.concurrent.Callable; import com.google.common.base.Predicate; @@ -15,7 +17,7 @@ import com.google.common.base.Predicates; import net.hoosiertransfer.Config; import net.lax1dude.eaglercraft.v1_8.Display; -import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; @@ -44,6 +46,7 @@ import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.MapItemRenderer; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.multiplayer.WorldClient; @@ -311,7 +314,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { Entity entity = this.mc.getRenderViewEntity(); if (entity != null) { if (this.mc.theWorld != null) { - this.mc.mcProfiler.startSection("pick"); this.mc.pointedEntity = null; double d0 = (double) this.mc.playerController.getBlockReachDistance(); this.mc.objectMouseOver = entity.rayTrace(d0, partialTicks); @@ -391,8 +393,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { this.mc.pointedEntity = this.pointedEntity; } } - - this.mc.mcProfiler.endSection(); } } } @@ -758,7 +758,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { private void updateLightmap(float partialTicks) { if (this.lightmapUpdateNeeded) { - this.mc.mcProfiler.startSection("lightTex"); WorldClient worldclient = this.mc.theWorld; if (worldclient != null) { float f = worldclient.getSunBrightness(1.0F); @@ -885,7 +884,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); this.lightmapUpdateNeeded = false; - this.mc.mcProfiler.endSection(); } } } @@ -896,9 +894,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { } public void func_181560_a(float parFloat1, long parLong1) { - boolean flag = Display.isActive(); + boolean flag = Display.isActive() || mc.gameSettings.touchscreen; if (!flag && this.mc.gameSettings.pauseOnLostFocus - && (!this.mc.gameSettings.touchscreen || !Mouse.isButtonDown(1))) { + && (!this.mc.gameSettings.touchscreen || !PointerInputAbstraction.getVCursorButtonDown(1))) { if (Minecraft.getSystemTime() - this.prevFrameTime > 500L) { this.mc.displayInGameMenu(); } @@ -906,8 +904,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { this.prevFrameTime = Minecraft.getSystemTime(); } - this.mc.mcProfiler.startSection("mouse"); - if (this.mc.inGameHasFocus && flag) { this.mc.mouseHelper.mouseXYChange(); float f = this.mc.gameSettings.mouseSensitivity * 0.6F + 0.2F; @@ -937,25 +933,23 @@ public class EntityRenderer implements IResourceManagerReloadListener { } } - this.mc.mcProfiler.endSection(); if (!this.mc.skipRenderWorld) { anaglyphEnable = this.mc.gameSettings.anaglyph; - final ScaledResolution scaledresolution = new ScaledResolution(this.mc); + final ScaledResolution scaledresolution = mc.scaledResolution; int l = scaledresolution.getScaledWidth(); int i1 = scaledresolution.getScaledHeight(); - final int j1 = Mouse.getX() * l / this.mc.displayWidth; - final int k1 = i1 - Mouse.getY() * i1 / this.mc.displayHeight - 1; + final int j1 = PointerInputAbstraction.getVCursorX() * l / this.mc.displayWidth; + final int k1 = i1 - PointerInputAbstraction.getVCursorY() * i1 / this.mc.displayHeight - 1; int l1 = this.mc.gameSettings.limitFramerate; if (this.mc.theWorld != null) { - this.mc.mcProfiler.startSection("level"); int i = Math.min(Minecraft.getDebugFPS(), l1); i = Math.max(i, 60); long j = System.nanoTime() - parLong1; long k = Math.max((long) (1000000000 / i / 4) - j, 0L); this.renderWorld(parFloat1, System.nanoTime() + k); this.renderEndNanoTime = System.nanoTime(); - this.mc.mcProfiler.endStartSection("gui"); - if (!this.mc.gameSettings.hideGUI || this.mc.currentScreen != null) { + final boolean b = !this.mc.gameSettings.hideGUI || this.mc.currentScreen != null; + if (b) { GlStateManager.alphaFunc(GL_GREATER, 0.1F); long framebufferAge = this.overlayFramebuffer.getAge(); if (framebufferAge == -1l || framebufferAge > (Minecraft.getDebugFPS() < 25 ? 125l : 75l)) { @@ -964,10 +958,14 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 0.0f); GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GlStateManager.enableOverlayFramebufferBlending(); - this.mc.ingameGUI.renderGameOverlay(parFloat1); + if (b) { + this.mc.ingameGUI.renderGameOverlay(parFloat1); + } GlStateManager.disableOverlayFramebufferBlending(); this.overlayFramebuffer.endRender(); } + } + if (b) { this.setupOverlayRendering(); GlStateManager.disableLighting(); GlStateManager.enableBlend(); @@ -979,7 +977,7 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); GlStateManager.enableBlend(); GlStateManager.blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.disableAlpha(); + GlStateManager.enableAlpha(); GlStateManager.disableDepth(); GlStateManager.depthMask(false); Tessellator tessellator = Tessellator.getInstance(); @@ -992,15 +990,12 @@ public class EntityRenderer implements IResourceManagerReloadListener { tessellator.draw(); GlStateManager.depthMask(true); GlStateManager.enableDepth(); - GlStateManager.enableAlpha(); GlStateManager.disableBlend(); if (this.mc.gameSettings.hudPlayer) { // give the player model HUD good fps this.mc.ingameGUI.drawEaglerPlayerOverlay(l - 3, 3 + this.mc.ingameGUI.overlayDebug.playerOffset, parFloat1); } } - - this.mc.mcProfiler.endSection(); } else { GlStateManager.viewport(0, 0, this.mc.displayWidth, this.mc.displayHeight); GlStateManager.matrixMode(GL_PROJECTION); @@ -1011,11 +1006,32 @@ public class EntityRenderer implements IResourceManagerReloadListener { this.renderEndNanoTime = System.nanoTime(); } + this.mc.notifRenderer.renderOverlay(j1, k1); + if (this.mc.currentScreen != null) { GlStateManager.clear(GL_DEPTH_BUFFER_BIT); + float f = 1.0f; + final float[] ff = new float[] { 1.0f }; try { - this.mc.currentScreen.drawScreen(j1, k1, parFloat1); + f = mc.currentScreen.getEaglerScale(); + int mx, my; + if (f == 1.0f) { + mx = j1; + my = k1; + } else { + mx = GuiScreen.applyEaglerScale(f, j1, l); + my = GuiScreen.applyEaglerScale(f, k1, i1); + GlStateManager.pushMatrix(); + float fff = (1.0f - f) * 0.5f; + GlStateManager.translate(fff * l, fff * i1, 0.0f); + GlStateManager.scale(f, f, f); + } + ff[0] = f; + this.mc.currentScreen.drawScreen(mx, my, parFloat1); + if (f != 1.0f) { + GlStateManager.popMatrix(); + } } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Rendering screen"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Screen render details"); @@ -1028,7 +1044,8 @@ public class EntityRenderer implements IResourceManagerReloadListener { public String call() throws Exception { return HString.format("Scaled: (%d, %d). Absolute: (%d, %d)", new Object[] { Integer.valueOf(j1), Integer.valueOf(k1), - Integer.valueOf(Mouse.getX()), Integer.valueOf(Mouse.getY()) }); + Integer.valueOf(PointerInputAbstraction.getVCursorX()), + Integer.valueOf(PointerInputAbstraction.getVCursorY()) }); } }); crashreportcategory.addCrashSectionCallable("Screen size", new Callable() { @@ -1041,11 +1058,17 @@ public class EntityRenderer implements IResourceManagerReloadListener { Integer.valueOf(scaledresolution.getScaleFactor()) }); } }); + crashreportcategory.addCrashSectionCallable("Eagler Scale", new Callable() { + public String call() throws Exception { + return "" + ff[0]; + } + }); throw new ReportedException(crashreport); } } - this.mc.voiceOverlay.drawOverlay(); + this.mc.voiceOverlay.drawOverlay(); + } } } @@ -1119,15 +1142,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { VoiceTagRenderer.clearTagsDrawnSet(); - if (Config.disableAlpha()) { - GlStateManager.disableAlpha(); - GlStateManager.disableBlend(); - } else { - GlStateManager.enableDepth(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(GL_GREATER, 0.5F); - } - this.mc.mcProfiler.startSection("center"); + GlStateManager.enableDepth(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(GL_GREATER, 0.5F); boolean dlights = DynamicLightsStateManager.isDynamicLightsRender(); if (dlights) { updateDynamicLightListEagler(partialTicks); @@ -1163,7 +1180,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { } mc.effectRenderer.acceleratedParticleRenderer = EffectRenderer.vanillaAcceleratedParticleRenderer; } else { - mc.effectRenderer.acceleratedParticleRenderer = EffectRenderer.vanillaAcceleratedParticleRenderer; + mc.effectRenderer.acceleratedParticleRenderer = EaglercraftGPU.checkInstancingCapable() + ? EffectRenderer.vanillaAcceleratedParticleRenderer + : null; if (dlights) { GlStateManager.enableExtensionPipeline(); } @@ -1180,8 +1199,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { if (fxaa) { EffectPipelineFXAA.end(); } - - this.mc.mcProfiler.endSection(); } private void renderWorldPass(int pass, float partialTicks, long finishTimeNano) { @@ -1189,18 +1206,15 @@ public class EntityRenderer implements IResourceManagerReloadListener { EffectRenderer effectrenderer = this.mc.effectRenderer; boolean flag = this.isDrawBlockOutline(); GlStateManager.enableCull(); - this.mc.mcProfiler.endStartSection("clear"); GlStateManager.viewport(0, 0, this.mc.displayWidth, this.mc.displayHeight); this.updateFogColor(partialTicks); GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - this.mc.mcProfiler.endStartSection("camera"); this.setupCameraTransform(partialTicks, pass); boolean isDynamicLights = DynamicLightsStateManager.isDynamicLightsRender(); if (isDynamicLights) { DynamicLightsStateManager.setupInverseViewMatrix(); } ActiveRenderInfo.updateRenderInfo(this.mc.thePlayer, this.mc.gameSettings.thirdPersonView == 2); - this.mc.mcProfiler.endStartSection("culling"); Frustum frustum = new Frustum(); Entity entity = this.mc.getRenderViewEntity(); double d0 = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double) partialTicks; @@ -1212,7 +1226,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { frustum.setPosition(d0, d1, d2); if (this.mc.gameSettings.renderDistanceChunks >= 4) { this.setupFog(-1, partialTicks); - this.mc.mcProfiler.endStartSection("sky"); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); float vigg = this.getFOVModifier(partialTicks, true); @@ -1233,19 +1246,15 @@ public class EntityRenderer implements IResourceManagerReloadListener { this.renderCloudsCheck(renderglobal, partialTicks, pass); } - this.mc.mcProfiler.endStartSection("prepareterrain"); this.setupFog(0, partialTicks); this.mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); RenderHelper.disableStandardItemLighting(); - this.mc.mcProfiler.endStartSection("terrain_setup"); renderglobal.setupTerrain(entity, (double) partialTicks, frustum, this.frameCount++, this.mc.thePlayer.isSpectator()); if (pass == 0 || pass == 2) { - this.mc.mcProfiler.endStartSection("updatechunks"); this.mc.renderGlobal.updateChunks(finishTimeNano); } - this.mc.mcProfiler.endStartSection("terrain"); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.pushMatrix(); GlStateManager.disableAlpha(); @@ -1263,7 +1272,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.popMatrix(); GlStateManager.pushMatrix(); RenderHelper.enableStandardItemLighting(); - this.mc.mcProfiler.endStartSection("entities"); renderglobal.renderEntities(entity, frustum, partialTicks); RenderHelper.disableStandardItemLighting(); this.disableLightmap(); @@ -1273,7 +1281,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { if (this.mc.objectMouseOver != null && entity.isInsideOfMaterial(Material.water) && flag) { EntityPlayer entityplayer = (EntityPlayer) entity; GlStateManager.disableAlpha(); - this.mc.mcProfiler.endStartSection("outline"); if (isDynamicLights) { GlStateManager.disableExtensionPipeline(); } @@ -1293,7 +1300,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { if (isDynamicLights) { GlStateManager.disableExtensionPipeline(); } - this.mc.mcProfiler.endStartSection("outline"); renderglobal.drawSelectionBox(entityplayer1, this.mc.objectMouseOver, 0, partialTicks); GlStateManager.enableAlpha(); if (isDynamicLights) { @@ -1301,7 +1307,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { } } - this.mc.mcProfiler.endStartSection("destroyProgress"); GlStateManager.enableBlend(); GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, 1, 1, 0); this.mc.getTextureManager().getTexture(TextureMap.locationBlocksTexture).setBlurMipmap(false, false); @@ -1311,11 +1316,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.disableBlend(); if (!this.debugView) { this.enableLightmap(); - this.mc.mcProfiler.endStartSection("litParticles"); effectrenderer.renderLitParticles(entity, partialTicks); RenderHelper.disableStandardItemLighting(); this.setupFog(0, partialTicks); - this.mc.mcProfiler.endStartSection("particles"); if (isDynamicLights) { DynamicLightsStateManager.bindAcceleratedEffectRenderer(effectrenderer); DynamicLightsStateManager.reportForwardRenderObjectPosition2(0.0f, 0.0f, 0.0f); @@ -1329,7 +1332,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.depthMask(false); GlStateManager.enableCull(); - this.mc.mcProfiler.endStartSection("weather"); this.renderRainSnow(partialTicks); GlStateManager.depthMask(true); renderglobal.renderWorldBorder(entity, partialTicks); @@ -1342,11 +1344,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.depthMask(false); this.mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); GlStateManager.shadeModel(GL_SMOOTH); - this.mc.mcProfiler.endStartSection("translucent"); - // thing from patcher i might want to disable this because i dont know if - // injecting this here is right - GlStateManager.enablePolygonOffset(); - GlStateManager.doPolygonOffset(-0.325F, -0.325F); renderglobal.renderBlockLayer(EnumWorldBlockLayer.TRANSLUCENT, (double) partialTicks, pass, entity); GlStateManager.disablePolygonOffset(); GlStateManager.shadeModel(GL_FLAT); @@ -1355,11 +1352,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.disableBlend(); GlStateManager.disableFog(); if (entity.posY + (double) entity.getEyeHeight() >= 128.0D) { - this.mc.mcProfiler.endStartSection("aboveClouds"); this.renderCloudsCheck(renderglobal, partialTicks, pass); } - this.mc.mcProfiler.endStartSection("hand"); if (this.renderHand) { GlStateManager.clear(GL_DEPTH_BUFFER_BIT); this.renderHand(partialTicks, pass); @@ -1384,7 +1379,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { private void renderCloudsCheck(RenderGlobal renderGlobalIn, float partialTicks, int pass) { if (this.mc.gameSettings.func_181147_e() != 0) { - this.mc.mcProfiler.endStartSection("clouds"); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), @@ -1674,7 +1668,7 @@ public class EntityRenderer implements IResourceManagerReloadListener { * Setup orthogonal projection for rendering GUI screen overlays */ public void setupOverlayRendering() { - ScaledResolution scaledresolution = new ScaledResolution(this.mc); + ScaledResolution scaledresolution = mc.scaledResolution; GlStateManager.clear(GL_DEPTH_BUFFER_BIT); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); @@ -1876,7 +1870,7 @@ public class EntityRenderer implements IResourceManagerReloadListener { } else if (block.getMaterial() == Material.lava) { GlStateManager.setFog(GL_EXP); GlStateManager.setFogDensity(2.0F); - } else if (!this.mc.gameSettings.fog) { + } else if (partialTicks != -1 && !this.mc.gameSettings.fog) { GlStateManager.setFog(GL_EXP); GlStateManager.setFogDensity(0.0F); } else { @@ -1933,28 +1927,23 @@ public class EntityRenderer implements IResourceManagerReloadListener { EaglerDeferredPipeline.renderSuspended(); return; } - mc.mcProfiler.endStartSection("eaglercraftShaders"); EaglerDeferredPipeline.instance.setPartialTicks(partialTicks); eagPartialTicks = partialTicks; EaglerDeferredConfig conf = mc.gameSettings.deferredShaderConf; boolean flag = isDrawBlockOutline(); GlStateManager.viewport(0, 0, mc.displayWidth, mc.displayHeight); - mc.mcProfiler.startSection("camera"); setupCameraTransform(partialTicks, 2); EaglerDeferredPipeline.instance.loadViewMatrix(); ActiveRenderInfo.updateRenderInfo(mc.thePlayer, mc.gameSettings.thirdPersonView == 2); - mc.mcProfiler.endStartSection("culling"); Frustum frustum = new Frustum(); Entity entity = mc.getRenderViewEntity(); if (entity == null) { entity = mc.thePlayer; } - double d0 = EaglerDeferredPipeline.instance.currentRenderX = entity.lastTickPosX - + (entity.posX - entity.lastTickPosX) * (double) partialTicks; - double d1 = EaglerDeferredPipeline.instance.currentRenderY = entity.lastTickPosY - + (entity.posY - entity.lastTickPosY) * (double) partialTicks; - double d2 = EaglerDeferredPipeline.instance.currentRenderZ = entity.lastTickPosZ - + (entity.posZ - entity.lastTickPosZ) * (double) partialTicks; + double d0 = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double) partialTicks; + double d1 = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * (double) partialTicks; + double d2 = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * (double) partialTicks; + EaglerDeferredPipeline.instance.setRenderPosGlobal(d0, d1, d2); EaglerDeferredPipeline.instance.updateReprojectionCoordinates(d0, d1, d2); float eyeHeight = entity.getEyeHeight(); frustum.setPosition(d0, d1, d2); @@ -1966,7 +1955,7 @@ public class EntityRenderer implements IResourceManagerReloadListener { // } // System.out.println(builder.toString()); - float waveTimer = (float) ((System.currentTimeMillis() % 600000l) * 0.001); + float waveTimer = (float) ((EagRuntime.steadyTimeMillis() % 600000l) * 0.001); DeferredStateManager.setWaterWindOffset(0.0f, 0.0f, waveTimer, waveTimer); float blockWaveDistX = (float) (d0 - blockWaveOffsetX); @@ -1991,7 +1980,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { // if (mc.gameSettings.renderDistanceChunks >= 4) vanilla shows sky not fog - mc.mcProfiler.endStartSection("terrain_setup"); mc.renderGlobal.setupTerrain(entity, (double) partialTicks, frustum, frameCount++, mc.thePlayer.isSpectator()); // clear some state: @@ -2009,11 +1997,8 @@ public class EntityRenderer implements IResourceManagerReloadListener { EaglerDeferredPipeline.instance.beginDrawMainGBufferTerrain(); - mc.mcProfiler.endStartSection("updatechunks"); mc.renderGlobal.updateChunks(finishTimeNano); - mc.mcProfiler.endStartSection("terrain"); - mc.renderGlobal.renderBlockLayer(EnumWorldBlockLayer.SOLID, (double) partialTicks, 2, entity); GlStateManager.enableAlpha(); GlStateManager.alphaFunc(GL_GREATER, 0.5F); @@ -2042,20 +2027,17 @@ public class EntityRenderer implements IResourceManagerReloadListener { NameTagRenderer.doRenderNameTags = true; NameTagRenderer.nameTagsCount = 0; GlStateManager.pushMatrix(); - mc.mcProfiler.endStartSection("entities"); DeferredStateManager.setDefaultMaterialConstants(); DeferredStateManager.startUsingEnvMap(); mc.renderGlobal.renderEntities(entity, frustum, partialTicks); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.popMatrix(); - mc.mcProfiler.endStartSection("litParticles"); EntityFX.interpPosX = d0; EntityFX.interpPosY = d1; EntityFX.interpPosZ = d2; enableLightmap(); GlStateManager.pushMatrix(); mc.effectRenderer.renderLitParticles(entity, partialTicks); - mc.mcProfiler.endStartSection("gbufferParticles"); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.popMatrix(); GlStateManager.pushMatrix(); @@ -2069,9 +2051,7 @@ public class EntityRenderer implements IResourceManagerReloadListener { DynamicLightManager.setIsRenderingLights(false); NameTagRenderer.doRenderNameTags = false; - mc.mcProfiler.endStartSection("endDrawMainGBuffer"); EaglerDeferredPipeline.instance.endDrawMainGBuffer(); - mc.mcProfiler.endStartSection("shadowSetup"); // calculate sun matrix and angle: @@ -2476,11 +2456,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { } } - mc.mcProfiler.endStartSection("combineGBuffersAndIlluminate"); EaglerDeferredPipeline.instance.combineGBuffersAndIlluminate(); if (conf.is_rendering_useEnvMap) { - mc.mcProfiler.endStartSection("envMap"); DeferredStateManager.forwardCallbackHandler = null; EaglerDeferredPipeline.instance.beginDrawEnvMap(); GlStateManager.enableCull(); @@ -2551,7 +2529,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { } if (conf.is_rendering_realisticWater) { - mc.mcProfiler.endStartSection("realisticWaterMask"); EaglerDeferredPipeline.instance.beginDrawRealisticWaterMask(); enableLightmap(); mc.renderGlobal.renderBlockLayer(EnumWorldBlockLayer.REALISTIC_WATER, (double) partialTicks, 2, entity); @@ -2559,8 +2536,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { EaglerDeferredPipeline.instance.endDrawRealisticWaterMask(); } - mc.mcProfiler.endStartSection("setupShaderFog"); - int dim = mc.theWorld.provider.getDimensionId(); float ff; if (dim == 0) { @@ -2625,16 +2600,13 @@ public class EntityRenderer implements IResourceManagerReloadListener { DeferredStateManager.setDefaultMaterialConstants(); if (conf.is_rendering_realisticWater) { - mc.mcProfiler.endStartSection("realisticWaterSurface"); EaglerDeferredPipeline.instance.beginDrawRealisticWaterSurface(); mc.renderGlobal.renderBlockLayer(EnumWorldBlockLayer.REALISTIC_WATER, (double) partialTicks, 2, entity); EaglerDeferredPipeline.instance.endDrawRealisticWaterSurface(); } - mc.mcProfiler.endStartSection("gbufferFog"); EaglerDeferredPipeline.instance.applyGBufferFog(); - mc.mcProfiler.endStartSection("translucentEntities"); EaglerDeferredPipeline.instance.beginDrawTranslucentEntities(); TileEntityRendererDispatcher.staticPlayerX = d0; @@ -2658,14 +2630,11 @@ public class EntityRenderer implements IResourceManagerReloadListener { DeferredStateManager.forwardCallbackGBuffer.reset(); EaglerDeferredPipeline.instance.beginDrawTranslucentBlocks(); - mc.mcProfiler.endStartSection("translucentBlocks"); mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture); mc.renderGlobal.renderBlockLayer(EnumWorldBlockLayer.TRANSLUCENT, (double) partialTicks, 2, entity); EaglerDeferredPipeline.instance.beginDrawMainGBufferDestroyProgress(); - mc.mcProfiler.endStartSection("destroyProgress"); - GlStateManager.enableBlend(); GlStateManager.tryBlendFuncSeparate(0, GL_SRC_ALPHA, 0, 0); GlStateManager.color(1.0f, 1.0f, 1.0f, 0.5f); @@ -2677,7 +2646,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { EaglerDeferredPipeline.instance.endDrawMainGBufferDestroyProgress(); if (mc.effectRenderer.hasParticlesInAlphaLayer()) { - mc.mcProfiler.endStartSection("transparentParticles"); GlStateManager.pushMatrix(); mc.effectRenderer.acceleratedParticleRenderer = EaglerDeferredPipeline.instance.forwardEffectRenderer; DeferredStateManager.setHDRTranslucentPassBlendFunc(); @@ -2693,22 +2661,18 @@ public class EntityRenderer implements IResourceManagerReloadListener { } if (conf.is_rendering_useEnvMap) { - mc.mcProfiler.endStartSection("glassHighlights"); EaglerDeferredPipeline.instance.beginDrawGlassHighlights(); mc.renderGlobal.renderBlockLayer(EnumWorldBlockLayer.GLASS_HIGHLIGHTS, (double) partialTicks, 2, entity); EaglerDeferredPipeline.instance.endDrawGlassHighlights(); } - mc.mcProfiler.endStartSection("saveReprojData"); EaglerDeferredPipeline.instance.saveReprojData(); - mc.mcProfiler.endStartSection("rainSnow"); renderRainSnow(partialTicks); GlStateManager.disableBlend(); if (renderHand) { - mc.mcProfiler.endStartSection("renderHandOverlay"); EaglerDeferredPipeline.instance.beginDrawHandOverlay(); DeferredStateManager.reportForwardRenderObjectPosition2(0.0f, 0.0f, 0.0f); DeferredStateManager.forwardCallbackHandler = DeferredStateManager.forwardCallbackGBuffer; @@ -2734,7 +2698,6 @@ public class EntityRenderer implements IResourceManagerReloadListener { GlStateManager.disableAlpha(); } - mc.mcProfiler.endStartSection("endDrawDeferred"); EaglerDeferredPipeline.instance.endDrawHDRTranslucent(); EaglerDeferredPipeline.instance.endDrawDeferred(); @@ -2754,11 +2717,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { if (!DebugFramebufferView.debugViewShown) { GlStateManager.disableAlpha(); if (isDrawBlockOutline()) { - this.mc.mcProfiler.endStartSection("outline"); mc.renderGlobal.drawSelectionBox(mc.thePlayer, this.mc.objectMouseOver, 0, partialTicks); } GlStateManager.enableAlpha(); - this.mc.mcProfiler.endStartSection("nameTags"); if (NameTagRenderer.nameTagsCount > 0) { enableLightmap(); Arrays.sort(NameTagRenderer.nameTagsThisFrame, 0, NameTagRenderer.nameTagsCount, (n1, n2) -> { @@ -2785,11 +2746,8 @@ public class EntityRenderer implements IResourceManagerReloadListener { } disableLightmap(); GlStateManager.disableLighting(); - this.mc.mcProfiler.endStartSection("worldBorder"); mc.renderGlobal.renderWorldBorder(entity, partialTicks); } - - mc.mcProfiler.endSection(); } public boolean renderHeldItemLight(EntityLivingBase entityLiving, float mag) { diff --git a/src/main/java/net/minecraft/client/renderer/EnumFaceDirection.java b/src/game/java/net/minecraft/client/renderer/EnumFaceDirection.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/EnumFaceDirection.java rename to src/game/java/net/minecraft/client/renderer/EnumFaceDirection.java diff --git a/src/main/java/net/minecraft/client/renderer/GLAllocation.java b/src/game/java/net/minecraft/client/renderer/GLAllocation.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/GLAllocation.java rename to src/game/java/net/minecraft/client/renderer/GLAllocation.java diff --git a/src/main/java/net/minecraft/client/renderer/IImageBuffer.java b/src/game/java/net/minecraft/client/renderer/IImageBuffer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/IImageBuffer.java rename to src/game/java/net/minecraft/client/renderer/IImageBuffer.java diff --git a/src/main/java/net/minecraft/client/renderer/ImageBufferDownload.java b/src/game/java/net/minecraft/client/renderer/ImageBufferDownload.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ImageBufferDownload.java rename to src/game/java/net/minecraft/client/renderer/ImageBufferDownload.java diff --git a/src/main/java/net/minecraft/client/renderer/InventoryEffectRenderer.java b/src/game/java/net/minecraft/client/renderer/InventoryEffectRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/InventoryEffectRenderer.java rename to src/game/java/net/minecraft/client/renderer/InventoryEffectRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/ItemMeshDefinition.java b/src/game/java/net/minecraft/client/renderer/ItemMeshDefinition.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ItemMeshDefinition.java rename to src/game/java/net/minecraft/client/renderer/ItemMeshDefinition.java diff --git a/src/main/java/net/minecraft/client/renderer/ItemModelMesher.java b/src/game/java/net/minecraft/client/renderer/ItemModelMesher.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ItemModelMesher.java rename to src/game/java/net/minecraft/client/renderer/ItemModelMesher.java diff --git a/src/main/java/net/minecraft/client/renderer/ItemRenderer.java b/src/game/java/net/minecraft/client/renderer/ItemRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ItemRenderer.java rename to src/game/java/net/minecraft/client/renderer/ItemRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/RegionRenderCache.java b/src/game/java/net/minecraft/client/renderer/RegionRenderCache.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/RegionRenderCache.java rename to src/game/java/net/minecraft/client/renderer/RegionRenderCache.java diff --git a/src/main/java/net/minecraft/client/renderer/RegionRenderCacheBuilder.java b/src/game/java/net/minecraft/client/renderer/RegionRenderCacheBuilder.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/RegionRenderCacheBuilder.java rename to src/game/java/net/minecraft/client/renderer/RegionRenderCacheBuilder.java diff --git a/src/main/java/net/minecraft/client/renderer/RenderGlobal.java b/src/game/java/net/minecraft/client/renderer/RenderGlobal.java similarity index 98% rename from src/main/java/net/minecraft/client/renderer/RenderGlobal.java rename to src/game/java/net/minecraft/client/renderer/RenderGlobal.java index 7afc4e9..1ebd04d 100644 --- a/src/main/java/net/minecraft/client/renderer/RenderGlobal.java +++ b/src/game/java/net/minecraft/client/renderer/RenderGlobal.java @@ -10,6 +10,8 @@ import java.util.List; import java.util.Map; import net.hoosiertransfer.Alfheim.ILightUpdatesProcessor; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.Keyboard; @@ -521,7 +523,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene + (renderViewEntity.posY - renderViewEntity.prevPosY) * (double) partialTicks; double d2 = renderViewEntity.prevPosZ + (renderViewEntity.posZ - renderViewEntity.prevPosZ) * (double) partialTicks; - this.theWorld.theProfiler.startSection("prepare"); TileEntityRendererDispatcher.instance.cacheActiveRenderInfo(this.theWorld, this.mc.getTextureManager(), this.mc.fontRendererObj, this.mc.getRenderViewEntity(), partialTicks); this.renderManager.cacheActiveRenderInfo(this.theWorld, this.mc.fontRendererObj, @@ -538,7 +539,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene TileEntityRendererDispatcher.staticPlayerZ = d5; this.renderManager.setRenderPosition(d3, d4, d5); this.mc.entityRenderer.enableLightmap(); - this.theWorld.theProfiler.endStartSection("global"); List list = this.theWorld.getLoadedEntityList(); this.countEntitiesTotal = list.size(); @@ -555,8 +555,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } - this.theWorld.theProfiler.endStartSection("entities"); - label738: for (int ii = 0, ll = this.renderInfos.size(); ii < ll; ++ii) { RenderGlobal.ContainerLocalRenderInformation renderglobal$containerlocalrenderinformation = this.renderInfos .get(ii); @@ -606,7 +604,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } - this.theWorld.theProfiler.endStartSection("blockentities"); RenderHelper.enableStandardItemLighting(); for (int ii = 0, ll = this.renderInfos.size(); ii < ll; ++ii) { @@ -654,7 +651,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene this.postRenderDamagedBlocks(); this.mc.entityRenderer.disableLightmap(); - this.mc.mcProfiler.endSection(); } } @@ -669,8 +665,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene public void renderShadowLODEntities(Entity renderViewEntity, float partialTicks, EntityChunkCullAdapter entityChunkCull, EntityObjectCullAdapter entityObjectCull) { // TODO if (renderEntitiesStartupCounter <= 0) { - theWorld.theProfiler.startSection("shadow_entity_prepare"); - TileEntityRendererDispatcher.instance.cacheActiveRenderInfo(theWorld, mc.getTextureManager(), mc.fontRendererObj, renderViewEntity, partialTicks); renderManager.cacheActiveRenderInfo(theWorld, mc.fontRendererObj, renderViewEntity, mc.pointedEntity, @@ -687,7 +681,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene TileEntityRendererDispatcher.staticPlayerZ = d5; renderManager.setRenderPosition(d3, d4, d5); - this.theWorld.theProfiler.endStartSection("shadow_entities"); for (RenderGlobal.ContainerLocalRenderInformation containerlocalrenderinformation : this.renderInfos) { RenderChunk currentRenderChunk = containerlocalrenderinformation.renderChunk; @@ -741,14 +734,11 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene GlStateManager.depthMask(true); } } - theWorld.theProfiler.endSection(); } } public void renderParaboloidTileEntities(Entity renderViewEntity, float partialTicks, int up) { if (renderEntitiesStartupCounter <= 0) { - theWorld.theProfiler.startSection("paraboloid_entity_prepare"); - TileEntityRendererDispatcher.instance.cacheActiveRenderInfo(theWorld, mc.getTextureManager(), mc.fontRendererObj, renderViewEntity, partialTicks); renderManager.cacheActiveRenderInfo(theWorld, mc.fontRendererObj, renderViewEntity, mc.pointedEntity, @@ -789,7 +779,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene maxY = MathHelper.floor_double(maxY / 16.0) * 16; maxZ = MathHelper.floor_double(maxZ / 16.0) * 16; - this.theWorld.theProfiler.endStartSection("paraboloid_entities"); for (int cx = minX; cx <= maxX; cx += 16) { for (int cz = minZ; cz <= maxZ; cz += 16) { for (int cy = minY; cy <= maxY; cy += 16) { @@ -812,7 +801,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } } - theWorld.theProfiler.endSection(); mc.entityRenderer.disableLightmap(); } } @@ -869,7 +857,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene this.loadRenderers(); } - this.theWorld.theProfiler.startSection("camera"); double d0 = viewEntity.posX - this.frustumUpdatePosX; double d1 = viewEntity.posY - this.frustumUpdatePosY; double d2 = viewEntity.posZ - this.frustumUpdatePosZ; @@ -885,12 +872,10 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene this.viewFrustum.updateChunkPositions(viewEntity.posX, viewEntity.posZ); } - this.theWorld.theProfiler.endStartSection("renderlistcamera"); double d3 = viewEntity.lastTickPosX + (viewEntity.posX - viewEntity.lastTickPosX) * partialTicks; double d4 = viewEntity.lastTickPosY + (viewEntity.posY - viewEntity.lastTickPosY) * partialTicks; double d5 = viewEntity.lastTickPosZ + (viewEntity.posZ - viewEntity.lastTickPosZ) * partialTicks; this.renderContainer.initialize(d3, d4, d5); - this.theWorld.theProfiler.endStartSection("cull"); if (this.debugFixedClippingHelper != null) { Frustum frustum = new Frustum(this.debugFixedClippingHelper); frustum.setPosition(this.debugTerrainFrustumPosition.field_181059_a, @@ -898,7 +883,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene camera = frustum; } - this.mc.mcProfiler.endStartSection("culling"); BlockPos blockpos1 = new BlockPos(d3, d4 + (double) viewEntity.getEyeHeight(), d5); RenderChunk renderchunk = this.viewFrustum.getRenderChunk(blockpos1); BlockPos blockpos = new BlockPos(MathHelper.floor_double(d3 / 16.0D) * 16, @@ -1010,10 +994,8 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene this.displayListEntitiesDirty = true; if (this.mc.gameSettings.chunkFix ? this.isPositionInRenderChunkHack(blockpos1, renderchunk4) : this.isPositionInRenderChunk(blockpos, renderchunk4)) { - this.mc.mcProfiler.startSection("build near"); this.renderDispatcher.updateChunkNow(renderchunk4); renderchunk4.setNeedsUpdate(false); - this.mc.mcProfiler.endSection(); } else { this.chunksToUpdate.add(renderchunk4); } @@ -1021,7 +1003,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } this.chunksToUpdate.addAll(set); - this.mc.mcProfiler.endSection(); } private boolean isPositionInRenderChunk(BlockPos pos, RenderChunk renderChunkIn) { @@ -1120,7 +1101,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene public int renderBlockLayer(EnumWorldBlockLayer blockLayerIn, double partialTicks, int pass, Entity entityIn) { RenderHelper.disableStandardItemLighting(); if (blockLayerIn == EnumWorldBlockLayer.TRANSLUCENT) { - this.mc.mcProfiler.startSection("translucent_sort"); double d0 = entityIn.posX - this.prevRenderSortX; double d1 = entityIn.posY - this.prevRenderSortY; double d2 = entityIn.posZ - this.prevRenderSortZ; @@ -1140,11 +1120,8 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } } - - this.mc.mcProfiler.endSection(); } - this.mc.mcProfiler.startSection("filterempty"); int l = 0; boolean flag = blockLayerIn == EnumWorldBlockLayer.TRANSLUCENT; int i1 = flag ? this.renderInfos.size() - 1 : 0; @@ -1160,9 +1137,7 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } - this.mc.mcProfiler.endStartSection("render_" + blockLayerIn); this.renderBlockLayer(blockLayerIn); - this.mc.mcProfiler.endSection(); return l; } @@ -1197,7 +1172,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } if (i > 0) { - this.mc.mcProfiler.endStartSection("render_shadow_" + blockLayerIn); this.renderContainer.renderChunkLayer(blockLayerIn); } return i; @@ -1256,7 +1230,6 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene } } if (i > 0) { - this.mc.mcProfiler.endStartSection("render_paraboloid_" + up + "_" + blockLayerIn); this.mc.entityRenderer.enableLightmap(); this.renderContainer.renderChunkLayer(blockLayerIn); this.mc.entityRenderer.disableLightmap(); @@ -1839,7 +1812,7 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene renderchunk.setNeedsUpdate(false); iterator.remove(); - long i = finishTimeNano - System.nanoTime(); + long i = finishTimeNano - EagRuntime.nanoTime(); if (i < 0L) { break; } diff --git a/src/main/java/net/minecraft/client/renderer/RenderHelper.java b/src/game/java/net/minecraft/client/renderer/RenderHelper.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/RenderHelper.java rename to src/game/java/net/minecraft/client/renderer/RenderHelper.java diff --git a/src/main/java/net/minecraft/client/renderer/RenderList.java b/src/game/java/net/minecraft/client/renderer/RenderList.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/RenderList.java rename to src/game/java/net/minecraft/client/renderer/RenderList.java diff --git a/src/main/java/net/minecraft/client/renderer/StitcherException.java b/src/game/java/net/minecraft/client/renderer/StitcherException.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/StitcherException.java rename to src/game/java/net/minecraft/client/renderer/StitcherException.java diff --git a/src/main/java/net/minecraft/client/renderer/Tessellator.java b/src/game/java/net/minecraft/client/renderer/Tessellator.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/Tessellator.java rename to src/game/java/net/minecraft/client/renderer/Tessellator.java diff --git a/src/main/java/net/minecraft/client/renderer/ViewFrustum.java b/src/game/java/net/minecraft/client/renderer/ViewFrustum.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/ViewFrustum.java rename to src/game/java/net/minecraft/client/renderer/ViewFrustum.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/BakedQuad.java b/src/game/java/net/minecraft/client/renderer/block/model/BakedQuad.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/BakedQuad.java rename to src/game/java/net/minecraft/client/renderer/block/model/BakedQuad.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/BlockFaceUV.java b/src/game/java/net/minecraft/client/renderer/block/model/BlockFaceUV.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/BlockFaceUV.java rename to src/game/java/net/minecraft/client/renderer/block/model/BlockFaceUV.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/BlockPart.java b/src/game/java/net/minecraft/client/renderer/block/model/BlockPart.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/BlockPart.java rename to src/game/java/net/minecraft/client/renderer/block/model/BlockPart.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/BlockPartFace.java b/src/game/java/net/minecraft/client/renderer/block/model/BlockPartFace.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/BlockPartFace.java rename to src/game/java/net/minecraft/client/renderer/block/model/BlockPartFace.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/BlockPartRotation.java b/src/game/java/net/minecraft/client/renderer/block/model/BlockPartRotation.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/BlockPartRotation.java rename to src/game/java/net/minecraft/client/renderer/block/model/BlockPartRotation.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/BreakingFour.java b/src/game/java/net/minecraft/client/renderer/block/model/BreakingFour.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/BreakingFour.java rename to src/game/java/net/minecraft/client/renderer/block/model/BreakingFour.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/FaceBakery.java b/src/game/java/net/minecraft/client/renderer/block/model/FaceBakery.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/FaceBakery.java rename to src/game/java/net/minecraft/client/renderer/block/model/FaceBakery.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java b/src/game/java/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java rename to src/game/java/net/minecraft/client/renderer/block/model/ItemCameraTransforms.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ItemModelGenerator.java b/src/game/java/net/minecraft/client/renderer/block/model/ItemModelGenerator.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ItemModelGenerator.java rename to src/game/java/net/minecraft/client/renderer/block/model/ItemModelGenerator.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ItemOverride.java b/src/game/java/net/minecraft/client/renderer/block/model/ItemOverride.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ItemOverride.java rename to src/game/java/net/minecraft/client/renderer/block/model/ItemOverride.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ItemOverrideList.java b/src/game/java/net/minecraft/client/renderer/block/model/ItemOverrideList.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ItemOverrideList.java rename to src/game/java/net/minecraft/client/renderer/block/model/ItemOverrideList.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java b/src/game/java/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java rename to src/game/java/net/minecraft/client/renderer/block/model/ItemTransformVec3f.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ModelBlock.java b/src/game/java/net/minecraft/client/renderer/block/model/ModelBlock.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ModelBlock.java rename to src/game/java/net/minecraft/client/renderer/block/model/ModelBlock.java diff --git a/src/main/java/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java b/src/game/java/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java rename to src/game/java/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java diff --git a/src/main/java/net/minecraft/client/renderer/block/statemap/BlockStateMapper.java b/src/game/java/net/minecraft/client/renderer/block/statemap/BlockStateMapper.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/statemap/BlockStateMapper.java rename to src/game/java/net/minecraft/client/renderer/block/statemap/BlockStateMapper.java diff --git a/src/main/java/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.java b/src/game/java/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.java rename to src/game/java/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.java diff --git a/src/main/java/net/minecraft/client/renderer/block/statemap/IStateMapper.java b/src/game/java/net/minecraft/client/renderer/block/statemap/IStateMapper.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/statemap/IStateMapper.java rename to src/game/java/net/minecraft/client/renderer/block/statemap/IStateMapper.java diff --git a/src/main/java/net/minecraft/client/renderer/block/statemap/StateMap.java b/src/game/java/net/minecraft/client/renderer/block/statemap/StateMap.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/statemap/StateMap.java rename to src/game/java/net/minecraft/client/renderer/block/statemap/StateMap.java diff --git a/src/main/java/net/minecraft/client/renderer/block/statemap/StateMapperBase.java b/src/game/java/net/minecraft/client/renderer/block/statemap/StateMapperBase.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/block/statemap/StateMapperBase.java rename to src/game/java/net/minecraft/client/renderer/block/statemap/StateMapperBase.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.java b/src/game/java/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.java rename to src/game/java/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/ChunkRenderWorker.java b/src/game/java/net/minecraft/client/renderer/chunk/ChunkRenderWorker.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/ChunkRenderWorker.java rename to src/game/java/net/minecraft/client/renderer/chunk/ChunkRenderWorker.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/CompiledChunk.java b/src/game/java/net/minecraft/client/renderer/chunk/CompiledChunk.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/CompiledChunk.java rename to src/game/java/net/minecraft/client/renderer/chunk/CompiledChunk.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/IRenderChunkFactory.java b/src/game/java/net/minecraft/client/renderer/chunk/IRenderChunkFactory.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/IRenderChunkFactory.java rename to src/game/java/net/minecraft/client/renderer/chunk/IRenderChunkFactory.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/ListChunkFactory.java b/src/game/java/net/minecraft/client/renderer/chunk/ListChunkFactory.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/ListChunkFactory.java rename to src/game/java/net/minecraft/client/renderer/chunk/ListChunkFactory.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/ListedRenderChunk.java b/src/game/java/net/minecraft/client/renderer/chunk/ListedRenderChunk.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/ListedRenderChunk.java rename to src/game/java/net/minecraft/client/renderer/chunk/ListedRenderChunk.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/RenderChunk.java b/src/game/java/net/minecraft/client/renderer/chunk/RenderChunk.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/RenderChunk.java rename to src/game/java/net/minecraft/client/renderer/chunk/RenderChunk.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/SetVisibility.java b/src/game/java/net/minecraft/client/renderer/chunk/SetVisibility.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/SetVisibility.java rename to src/game/java/net/minecraft/client/renderer/chunk/SetVisibility.java diff --git a/src/main/java/net/minecraft/client/renderer/chunk/VisGraph.java b/src/game/java/net/minecraft/client/renderer/chunk/VisGraph.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/chunk/VisGraph.java rename to src/game/java/net/minecraft/client/renderer/chunk/VisGraph.java diff --git a/src/main/java/net/minecraft/client/renderer/culling/ClippingHelper.java b/src/game/java/net/minecraft/client/renderer/culling/ClippingHelper.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/culling/ClippingHelper.java rename to src/game/java/net/minecraft/client/renderer/culling/ClippingHelper.java diff --git a/src/main/java/net/minecraft/client/renderer/culling/ClippingHelperImpl.java b/src/game/java/net/minecraft/client/renderer/culling/ClippingHelperImpl.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/culling/ClippingHelperImpl.java rename to src/game/java/net/minecraft/client/renderer/culling/ClippingHelperImpl.java diff --git a/src/main/java/net/minecraft/client/renderer/culling/Frustum.java b/src/game/java/net/minecraft/client/renderer/culling/Frustum.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/culling/Frustum.java rename to src/game/java/net/minecraft/client/renderer/culling/Frustum.java diff --git a/src/main/java/net/minecraft/client/renderer/culling/ICamera.java b/src/game/java/net/minecraft/client/renderer/culling/ICamera.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/culling/ICamera.java rename to src/game/java/net/minecraft/client/renderer/culling/ICamera.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/ArmorStandRenderer.java b/src/game/java/net/minecraft/client/renderer/entity/ArmorStandRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/ArmorStandRenderer.java rename to src/game/java/net/minecraft/client/renderer/entity/ArmorStandRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/Render.java b/src/game/java/net/minecraft/client/renderer/entity/Render.java similarity index 99% rename from src/main/java/net/minecraft/client/renderer/entity/Render.java rename to src/game/java/net/minecraft/client/renderer/entity/Render.java index 4aa5001..9945a61 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/Render.java +++ b/src/game/java/net/minecraft/client/renderer/entity/Render.java @@ -103,7 +103,7 @@ public abstract class Render { protected void renderName(T entity, double x, double y, double z) { if (this.canRenderName(entity)) { - this.renderLivingLabel(entity, entity.getDisplayName().getFormattedText(), x, y, z, 64); + this.renderLivingLabel(entity, entity.getDisplayNameProfanityFilter().getFormattedText(), x, y, z, 64); } } diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderArrow.java b/src/game/java/net/minecraft/client/renderer/entity/RenderArrow.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderArrow.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderArrow.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderBat.java b/src/game/java/net/minecraft/client/renderer/entity/RenderBat.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderBat.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderBat.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderBiped.java b/src/game/java/net/minecraft/client/renderer/entity/RenderBiped.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderBiped.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderBiped.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderBlaze.java b/src/game/java/net/minecraft/client/renderer/entity/RenderBlaze.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderBlaze.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderBlaze.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderBoat.java b/src/game/java/net/minecraft/client/renderer/entity/RenderBoat.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderBoat.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderBoat.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderCaveSpider.java b/src/game/java/net/minecraft/client/renderer/entity/RenderCaveSpider.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderCaveSpider.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderCaveSpider.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderChicken.java b/src/game/java/net/minecraft/client/renderer/entity/RenderChicken.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderChicken.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderChicken.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderCow.java b/src/game/java/net/minecraft/client/renderer/entity/RenderCow.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderCow.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderCow.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderCreeper.java b/src/game/java/net/minecraft/client/renderer/entity/RenderCreeper.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderCreeper.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderCreeper.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderDragon.java b/src/game/java/net/minecraft/client/renderer/entity/RenderDragon.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderDragon.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderDragon.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderEnderman.java b/src/game/java/net/minecraft/client/renderer/entity/RenderEnderman.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderEnderman.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderEnderman.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderEndermite.java b/src/game/java/net/minecraft/client/renderer/entity/RenderEndermite.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderEndermite.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderEndermite.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderEntity.java b/src/game/java/net/minecraft/client/renderer/entity/RenderEntity.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderEntity.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderEntity.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderEntityItem.java b/src/game/java/net/minecraft/client/renderer/entity/RenderEntityItem.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderEntityItem.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderEntityItem.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java b/src/game/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderFireball.java b/src/game/java/net/minecraft/client/renderer/entity/RenderFireball.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderFireball.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderFireball.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderFish.java b/src/game/java/net/minecraft/client/renderer/entity/RenderFish.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderFish.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderFish.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderGhast.java b/src/game/java/net/minecraft/client/renderer/entity/RenderGhast.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderGhast.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderGhast.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderGiantZombie.java b/src/game/java/net/minecraft/client/renderer/entity/RenderGiantZombie.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderGiantZombie.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderGiantZombie.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderGuardian.java b/src/game/java/net/minecraft/client/renderer/entity/RenderGuardian.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderGuardian.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderGuardian.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderHorse.java b/src/game/java/net/minecraft/client/renderer/entity/RenderHorse.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderHorse.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderHorse.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderIronGolem.java b/src/game/java/net/minecraft/client/renderer/entity/RenderIronGolem.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderIronGolem.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderIronGolem.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderItem.java b/src/game/java/net/minecraft/client/renderer/entity/RenderItem.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderItem.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderItem.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderLeashKnot.java b/src/game/java/net/minecraft/client/renderer/entity/RenderLeashKnot.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderLeashKnot.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderLeashKnot.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderLightningBolt.java b/src/game/java/net/minecraft/client/renderer/entity/RenderLightningBolt.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderLightningBolt.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderLightningBolt.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderLiving.java b/src/game/java/net/minecraft/client/renderer/entity/RenderLiving.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderLiving.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderLiving.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderMagmaCube.java b/src/game/java/net/minecraft/client/renderer/entity/RenderMagmaCube.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderMagmaCube.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderMagmaCube.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderManager.java b/src/game/java/net/minecraft/client/renderer/entity/RenderManager.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderManager.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderManager.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderMinecart.java b/src/game/java/net/minecraft/client/renderer/entity/RenderMinecart.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderMinecart.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderMinecart.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.java b/src/game/java/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderMooshroom.java b/src/game/java/net/minecraft/client/renderer/entity/RenderMooshroom.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderMooshroom.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderMooshroom.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderOcelot.java b/src/game/java/net/minecraft/client/renderer/entity/RenderOcelot.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderOcelot.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderOcelot.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderPainting.java b/src/game/java/net/minecraft/client/renderer/entity/RenderPainting.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderPainting.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderPainting.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderPig.java b/src/game/java/net/minecraft/client/renderer/entity/RenderPig.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderPig.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderPig.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderPigZombie.java b/src/game/java/net/minecraft/client/renderer/entity/RenderPigZombie.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderPigZombie.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderPigZombie.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java b/src/game/java/net/minecraft/client/renderer/entity/RenderPlayer.java similarity index 99% rename from src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderPlayer.java index b74cd9b..53acfd9 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java +++ b/src/game/java/net/minecraft/client/renderer/entity/RenderPlayer.java @@ -176,7 +176,7 @@ public class RenderPlayer extends RendererLivingEntity { if (scoreobjective != null) { Score score = scoreboard.getValueFromObjective(abstractclientplayer.getName(), scoreobjective); this.renderLivingLabel(abstractclientplayer, - score.getScorePoints() + " " + scoreobjective.getDisplayName(), d0, d1, d2, 64); + score.getScorePoints() + " " + scoreobjective.getDisplayNameProfanityFilter(), d0, d1, d2, 64); d1 += (double) ((float) this.getFontRendererFromRenderManager().FONT_HEIGHT * 1.15F * f); } } diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderPotion.java b/src/game/java/net/minecraft/client/renderer/entity/RenderPotion.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderPotion.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderPotion.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderRabbit.java b/src/game/java/net/minecraft/client/renderer/entity/RenderRabbit.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderRabbit.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderRabbit.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSheep.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSheep.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSheep.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSheep.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSilverfish.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSilverfish.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSilverfish.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSilverfish.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSkeleton.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSkeleton.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSkeleton.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSkeleton.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSlime.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSlime.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSlime.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSlime.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSnowMan.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSnowMan.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSnowMan.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSnowMan.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSnowball.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSnowball.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSnowball.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSnowball.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSpider.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSpider.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSpider.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSpider.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderSquid.java b/src/game/java/net/minecraft/client/renderer/entity/RenderSquid.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderSquid.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderSquid.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderTNTPrimed.java b/src/game/java/net/minecraft/client/renderer/entity/RenderTNTPrimed.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderTNTPrimed.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderTNTPrimed.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderTntMinecart.java b/src/game/java/net/minecraft/client/renderer/entity/RenderTntMinecart.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderTntMinecart.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderTntMinecart.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderVillager.java b/src/game/java/net/minecraft/client/renderer/entity/RenderVillager.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderVillager.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderVillager.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderWitch.java b/src/game/java/net/minecraft/client/renderer/entity/RenderWitch.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderWitch.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderWitch.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderWither.java b/src/game/java/net/minecraft/client/renderer/entity/RenderWither.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderWither.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderWither.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderWolf.java b/src/game/java/net/minecraft/client/renderer/entity/RenderWolf.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderWolf.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderWolf.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderXPOrb.java b/src/game/java/net/minecraft/client/renderer/entity/RenderXPOrb.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderXPOrb.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderXPOrb.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderZombie.java b/src/game/java/net/minecraft/client/renderer/entity/RenderZombie.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/RenderZombie.java rename to src/game/java/net/minecraft/client/renderer/entity/RenderZombie.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java b/src/game/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java similarity index 99% rename from src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java rename to src/game/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java index fd8d05a..426f5e4 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java +++ b/src/game/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java @@ -469,7 +469,7 @@ public abstract class RendererLivingEntity extends R double d3 = entitylivingbase.getDistanceSqToEntity(this.renderManager.livingPlayer); float f = entitylivingbase.isSneaking() ? 32.0F : 64.0F; if (d3 < (double) (f * f)) { - String s = entitylivingbase.getDisplayName().getFormattedText(); + String s = entitylivingbase.getDisplayNameProfanityFilter().getFormattedText(); float f1 = 0.02666667F; GlStateManager.alphaFunc(GL_GREATER, 0.1F); if (entitylivingbase.isSneaking()) { diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerArmorBase.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerArmorBase.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerArmorBase.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerArmorBase.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerArrow.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerArrow.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerArrow.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerArrow.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerCape.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerCape.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerCape.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerCape.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerCustomHead.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerCustomHead.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerCustomHead.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerCustomHead.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerElytra.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerElytra.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerElytra.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerElytra.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerHeldItem.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerHeldItem.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerHeldItem.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerHeldItem.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerRenderer.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerRenderer.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerSaddle.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerSaddle.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerSaddle.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerSaddle.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerSheepWool.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerSheepWool.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerSheepWool.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerSheepWool.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerWitherAura.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerWitherAura.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerWitherAura.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerWitherAura.java diff --git a/src/main/java/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.java b/src/game/java/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.java rename to src/game/java/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java b/src/game/java/net/minecraft/client/renderer/texture/AbstractTexture.java similarity index 82% rename from src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java rename to src/game/java/net/minecraft/client/renderer/texture/AbstractTexture.java index f4d8b62..4283ad2 100644 --- a/src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java +++ b/src/game/java/net/minecraft/client/renderer/texture/AbstractTexture.java @@ -1,91 +1,107 @@ -package net.minecraft.client.renderer.texture; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public abstract class AbstractTexture implements ITextureObject { - protected int glTextureId = -1; - protected boolean blur; - protected boolean mipmap; - protected boolean blurLast; - protected boolean mipmapLast; - - public void setBlurMipmapDirect(boolean parFlag, boolean parFlag2) { - if (blur != parFlag || mipmap != parFlag2) { - this.blur = parFlag; - this.mipmap = parFlag2; - setBlurMipmapDirect0(parFlag, parFlag2); - } - } - - protected void setBlurMipmapDirect0(boolean parFlag, boolean parFlag2) { - int i = -1; - short short1 = -1; - if (parFlag) { - i = parFlag2 ? 9987 : 9729; - short1 = 9729; - } else { - i = parFlag2 ? 9986 : 9728; - short1 = 9728; - } - - EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, i); - EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, short1); - } - - public void setBlurMipmap(boolean parFlag, boolean parFlag2) { - this.blurLast = this.blur; - this.mipmapLast = this.mipmap; - this.setBlurMipmapDirect(parFlag, parFlag2); - } - - public void restoreLastBlurMipmap() { - this.setBlurMipmapDirect(this.blurLast, this.mipmapLast); - } - - public int getGlTextureId() { - if (this.glTextureId == -1) { - this.glTextureId = TextureUtil.glGenTextures(); - } - - return this.glTextureId; - } - - public void deleteGlTexture() { - if (this.glTextureId != -1) { - TextureUtil.deleteTexture(this.glTextureId); - this.glTextureId = -1; - } - - } +package net.minecraft.client.renderer.texture; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class AbstractTexture implements ITextureObject { + protected int glTextureId = -1; + protected boolean blur; + protected boolean mipmap; + protected boolean blurLast; + protected boolean mipmapLast; + + public void setBlurMipmapDirect(boolean parFlag, boolean parFlag2) { + if (blur != parFlag || mipmap != parFlag2) { + this.blur = parFlag; + this.mipmap = parFlag2; + setBlurMipmapDirect0(parFlag, parFlag2); + } + } + + protected void setBlurMipmapDirect0(boolean parFlag, boolean parFlag2) { + int i = -1; + short short1 = -1; + if (parFlag) { + i = parFlag2 ? 9987 : 9729; + short1 = 9729; + } else { + i = parFlag2 ? 9986 : 9728; + short1 = 9728; + } + + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, i); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, short1); + } + + public void setBlurMipmap(boolean parFlag, boolean parFlag2) { + this.blurLast = this.blur; + this.mipmapLast = this.mipmap; + this.setBlurMipmapDirect(parFlag, parFlag2); + } + + public void restoreLastBlurMipmap() { + this.setBlurMipmapDirect(this.blurLast, this.mipmapLast); + } + + public int getGlTextureId() { + if (this.glTextureId == -1) { + this.glTextureId = TextureUtil.glGenTextures(); + } + + return this.glTextureId; + } + + public void deleteGlTexture() { + if (this.glTextureId != -1) { + TextureUtil.deleteTexture(this.glTextureId); + this.glTextureId = -1; + } + + } + + /** + * This function is needed due to EaglercraftX's use of glTexStorage2D to + * allocate memory for textures, some OpenGL implementations don't like it when + * you call glTexStorage2D on the same texture object more than once + */ + protected void regenerateIfNotAllocated() { + if (this.glTextureId != -1) { + if (hasAllocated) { + if (EaglercraftGPU.checkTexStorageCapable()) { + EaglercraftGPU.regenerateTexture(glTextureId); + } + } + hasAllocated = true; + } + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/renderer/texture/DynamicTexture.java b/src/game/java/net/minecraft/client/renderer/texture/DynamicTexture.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/DynamicTexture.java rename to src/game/java/net/minecraft/client/renderer/texture/DynamicTexture.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/IIconCreator.java b/src/game/java/net/minecraft/client/renderer/texture/IIconCreator.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/IIconCreator.java rename to src/game/java/net/minecraft/client/renderer/texture/IIconCreator.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/ITextureObject.java b/src/game/java/net/minecraft/client/renderer/texture/ITextureObject.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/ITextureObject.java rename to src/game/java/net/minecraft/client/renderer/texture/ITextureObject.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/ITickable.java b/src/game/java/net/minecraft/client/renderer/texture/ITickable.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/ITickable.java rename to src/game/java/net/minecraft/client/renderer/texture/ITickable.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/ITickableTextureObject.java b/src/game/java/net/minecraft/client/renderer/texture/ITickableTextureObject.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/ITickableTextureObject.java rename to src/game/java/net/minecraft/client/renderer/texture/ITickableTextureObject.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.java b/src/game/java/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.java rename to src/game/java/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/LayeredTexture.java b/src/game/java/net/minecraft/client/renderer/texture/LayeredTexture.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/LayeredTexture.java rename to src/game/java/net/minecraft/client/renderer/texture/LayeredTexture.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java b/src/game/java/net/minecraft/client/renderer/texture/SimpleTexture.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java rename to src/game/java/net/minecraft/client/renderer/texture/SimpleTexture.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/Stitcher.java b/src/game/java/net/minecraft/client/renderer/texture/Stitcher.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/Stitcher.java rename to src/game/java/net/minecraft/client/renderer/texture/Stitcher.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/TextureClock.java b/src/game/java/net/minecraft/client/renderer/texture/TextureClock.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/TextureClock.java rename to src/game/java/net/minecraft/client/renderer/texture/TextureClock.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/TextureCompass.java b/src/game/java/net/minecraft/client/renderer/texture/TextureCompass.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/TextureCompass.java rename to src/game/java/net/minecraft/client/renderer/texture/TextureCompass.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java b/src/game/java/net/minecraft/client/renderer/texture/TextureManager.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/texture/TextureManager.java rename to src/game/java/net/minecraft/client/renderer/texture/TextureManager.java diff --git a/src/main/java/net/minecraft/client/renderer/texture/TextureMap.java b/src/game/java/net/minecraft/client/renderer/texture/TextureMap.java similarity index 94% rename from src/main/java/net/minecraft/client/renderer/texture/TextureMap.java rename to src/game/java/net/minecraft/client/renderer/texture/TextureMap.java index d48901a..80d8bb8 100644 --- a/src/main/java/net/minecraft/client/renderer/texture/TextureMap.java +++ b/src/game/java/net/minecraft/client/renderer/texture/TextureMap.java @@ -1,592 +1,605 @@ -package net.minecraft.client.renderer.texture; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.Callable; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import net.lax1dude.eaglercraft.v1_8.HString; -import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; -import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.EaglerTextureAtlasSpritePBR; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.PBRTextureMapUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.StitcherException; -import net.minecraft.client.resources.IResource; -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.client.resources.data.AnimationMetadataSection; -import net.minecraft.client.resources.data.TextureMetadataSection; -import net.minecraft.crash.CrashReport; -import net.minecraft.crash.CrashReportCategory; -import net.minecraft.util.MathHelper; -import net.minecraft.util.ReportedException; -import net.minecraft.util.ResourceLocation; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class TextureMap extends AbstractTexture implements ITickableTextureObject { - private static final Logger logger = LogManager.getLogger(); - public static final ResourceLocation LOCATION_MISSING_TEXTURE = new ResourceLocation("missingno"); - public static final ResourceLocation locationBlocksTexture = new ResourceLocation("textures/atlas/blocks.png"); - private final List listAnimatedSprites; - private final Map mapRegisteredSprites; - private final Map mapUploadedSprites; - private final String basePath; - private final IIconCreator iconCreator; - private int mipmapLevels; - private final EaglerTextureAtlasSprite missingImage; - private final EaglerTextureAtlasSpritePBR missingImagePBR; - private int width; - private int height; - private boolean isEaglerPBRMode = false; - public int eaglerPBRMaterialTexture = -1; - - public static final int _GL_FRAMEBUFFER = 0x8D40; - public static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; - - private IFramebufferGL[] copyColorFramebuffer = null; - private IFramebufferGL[] copyMaterialFramebuffer = null; - - public TextureMap(String parString1) { - this(parString1, (IIconCreator) null); - } - - public TextureMap(String parString1, IIconCreator iconCreatorIn) { - this.listAnimatedSprites = Lists.newArrayList(); - this.mapRegisteredSprites = Maps.newHashMap(); - this.mapUploadedSprites = Maps.newHashMap(); - this.missingImage = new EaglerTextureAtlasSprite("missingno"); - this.missingImagePBR = new EaglerTextureAtlasSpritePBR("missingno"); - this.basePath = parString1; - this.iconCreator = iconCreatorIn; - } - - private void initMissingImage() { - int[] aint = TextureUtil.missingTextureData; - this.missingImage.setIconWidth(16); - this.missingImage.setIconHeight(16); - int[][] aint1 = new int[this.mipmapLevels + 1][]; - aint1[0] = aint; - this.missingImage.setFramesTextureData(Lists.newArrayList(new int[][][] { aint1 })); - this.missingImagePBR.setIconWidth(16); - this.missingImagePBR.setIconHeight(16); - int[][][] aint2 = new int[3][this.mipmapLevels + 1][]; - aint2[0][0] = aint; - int[] missingNormals = new int[256]; - for (int i = 0; i < missingNormals.length; ++i) { - missingNormals[i] = 0xFF7F7F; - } - aint2[1][0] = missingNormals; - int[] missingMaterial = new int[256]; - for (int i = 0; i < missingMaterial.length; ++i) { - missingMaterial[i] = 0x00000077; - } - aint2[2][0] = missingMaterial; - this.missingImagePBR.setFramesTextureDataPBR(new List[] { Lists.newArrayList(new int[][][] { aint2[0] }), - Lists.newArrayList(new int[][][] { aint2[1] }), Lists.newArrayList(new int[][][] { aint2[2] }) }); - } - - public void loadTexture(IResourceManager parIResourceManager) throws IOException { - if (this.iconCreator != null) { - this.loadSprites(parIResourceManager, this.iconCreator); - } - } - - public void loadSprites(IResourceManager resourceManager, IIconCreator parIIconCreator) { - destroyAnimationCaches(); - this.mapRegisteredSprites.clear(); - parIIconCreator.registerSprites(this); - this.initMissingImage(); - this.deleteGlTexture(); - this.loadTextureAtlas(resourceManager); - } - - public void deleteGlTexture() { - super.deleteGlTexture(); - if (eaglerPBRMaterialTexture != -1) { - GlStateManager.deleteTexture(eaglerPBRMaterialTexture); - eaglerPBRMaterialTexture = -1; - } - if (copyColorFramebuffer != null) { - for (int i = 0; i < copyColorFramebuffer.length; ++i) { - _wglDeleteFramebuffer(copyColorFramebuffer[i]); - } - copyColorFramebuffer = null; - } - if (copyMaterialFramebuffer != null) { - for (int i = 0; i < copyMaterialFramebuffer.length; ++i) { - _wglDeleteFramebuffer(copyMaterialFramebuffer[i]); - } - copyMaterialFramebuffer = null; - } - } - - public void loadTextureAtlas(IResourceManager resourceManager) { - int i = Minecraft.getGLMaximumTextureSize(); - Stitcher stitcher = new Stitcher(i, i, true, 0, this.mipmapLevels); - this.mapUploadedSprites.clear(); - this.listAnimatedSprites.clear(); - int j = Integer.MAX_VALUE; - int k = 1 << this.mipmapLevels; - - if (copyColorFramebuffer != null) { - for (int l = 0; l < copyColorFramebuffer.length; ++l) { - _wglDeleteFramebuffer(copyColorFramebuffer[l]); - } - copyColorFramebuffer = null; - } - - if (isEaglerPBRMode) { - if (eaglerPBRMaterialTexture == -1) { - eaglerPBRMaterialTexture = GlStateManager.generateTexture(); - } - if (copyMaterialFramebuffer == null) { - GlStateManager.bindTexture(eaglerPBRMaterialTexture); - copyMaterialFramebuffer = new IFramebufferGL[this.mipmapLevels + 1]; - for (int l = 0; l < copyMaterialFramebuffer.length; ++l) { - copyMaterialFramebuffer[l] = _wglCreateFramebuffer(); - _wglBindFramebuffer(_GL_FRAMEBUFFER, copyMaterialFramebuffer[l]); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(eaglerPBRMaterialTexture), l); - } - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - } - } else { - if (eaglerPBRMaterialTexture != -1) { - GlStateManager.deleteTexture(eaglerPBRMaterialTexture); - eaglerPBRMaterialTexture = -1; - } - if (copyMaterialFramebuffer != null) { - for (int l = 0; l < copyMaterialFramebuffer.length; ++l) { - _wglDeleteFramebuffer(copyMaterialFramebuffer[l]); - } - copyMaterialFramebuffer = null; - } - } - - for (Entry entry : this.mapRegisteredSprites.entrySet()) { - EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) entry.getValue(); - ResourceLocation resourcelocation = new ResourceLocation(textureatlassprite.getIconName()); - ResourceLocation resourcelocation1 = this.completeResourceLocation(resourcelocation, 0); - - if (isEaglerPBRMode) { - try { - IResource iresource = resourceManager.getResource(resourcelocation1); - ImageData[] abufferedimageColor = new ImageData[1 + this.mipmapLevels]; - ImageData[] abufferedimageNormal = new ImageData[1 + this.mipmapLevels]; - ImageData[] abufferedimageMaterial = new ImageData[1 + this.mipmapLevels]; - abufferedimageColor[0] = TextureUtil.readBufferedImage(iresource.getInputStream()); - abufferedimageNormal[0] = PBRTextureMapUtils.locateCompanionTexture(resourceManager, iresource, - "_n"); - abufferedimageMaterial[0] = PBRTextureMapUtils.locateCompanionTexture(resourceManager, iresource, - "_s"); - boolean dontAnimateNormals = false; - boolean dontAnimateMaterial = false; - if (abufferedimageNormal[0] == null) { - abufferedimageNormal[0] = PBRTextureMapUtils.defaultNormalsTexture; - dontAnimateNormals = true; - } - if (abufferedimageMaterial[0] == null) { - abufferedimageMaterial[0] = PBRTextureMapUtils.generateMaterialTextureFor( - ((EaglerTextureAtlasSprite) (entry.getValue())).getIconName()); - dontAnimateMaterial = true; - } - PBRTextureMapUtils.unifySizes(0, abufferedimageColor, abufferedimageNormal, abufferedimageMaterial); - - TextureMetadataSection texturemetadatasection = (TextureMetadataSection) iresource - .getMetadata("texture"); - if (texturemetadatasection != null) { - List list = texturemetadatasection.getListMipmaps(); - if (!list.isEmpty()) { - int l = abufferedimageColor[0].width; - int i1 = abufferedimageColor[0].height; - if (MathHelper.roundUpToPowerOfTwo(l) != l || MathHelper.roundUpToPowerOfTwo(i1) != i1) { - throw new RuntimeException( - "Unable to load extra miplevels, source-texture is not power of two"); - } - } - - Iterator iterator = list.iterator(); - - while (iterator.hasNext()) { - int i2 = ((Integer) iterator.next()).intValue(); - if (i2 > 0 && i2 < abufferedimageColor.length - 1 && abufferedimageColor[i2] == null) { - ResourceLocation resourcelocation2 = this.completeResourceLocation(resourcelocation, - i2); - - try { - IResource mipLevelResource = resourceManager.getResource(resourcelocation2); - abufferedimageColor[i2] = TextureUtil - .readBufferedImage(mipLevelResource.getInputStream()); - abufferedimageNormal[i2] = PBRTextureMapUtils - .locateCompanionTexture(resourceManager, mipLevelResource, "_n"); - abufferedimageMaterial[i2] = PBRTextureMapUtils - .locateCompanionTexture(resourceManager, mipLevelResource, "_s"); - if (abufferedimageNormal[i2] == null) { - abufferedimageNormal[i2] = PBRTextureMapUtils.defaultNormalsTexture; - } - if (abufferedimageMaterial[i2] == null) { - abufferedimageMaterial[i2] = PBRTextureMapUtils.generateMaterialTextureFor( - ((EaglerTextureAtlasSprite) (entry.getValue())).getIconName()); - } - PBRTextureMapUtils.unifySizes(i2, abufferedimageColor, abufferedimageNormal, - abufferedimageMaterial); - if ((abufferedimageColor[0].width >> i2) != abufferedimageColor[i2].width) { - throw new IOException("Mipmap level " + i2 + " is the wrong size, should be " - + (abufferedimageColor[0].width >> i2) + " pixels"); - } - } catch (Throwable exc) { - logger.error("Unable to load miplevel {} from: {}", i2, resourcelocation2); - logger.error(exc); - } - } - } - } - - AnimationMetadataSection animationmetadatasection = (AnimationMetadataSection) iresource - .getMetadata("animation"); - textureatlassprite.loadSpritePBR( - new ImageData[][] { abufferedimageColor, abufferedimageNormal, abufferedimageMaterial }, - animationmetadatasection, dontAnimateNormals, dontAnimateMaterial); - } catch (RuntimeException runtimeexception) { - logger.error("Unable to parse metadata from " + resourcelocation1); - logger.error(runtimeexception); - continue; - } catch (IOException ioexception1) { - logger.error("Using missing texture, unable to load " + resourcelocation1); - logger.error(ioexception1); - continue; - } - - j = Math.min(j, Math.min(textureatlassprite.getIconWidth(), textureatlassprite.getIconHeight())); - int l1 = Math.min(Integer.lowestOneBit(textureatlassprite.getIconWidth()), - Integer.lowestOneBit(textureatlassprite.getIconHeight())); - if (l1 < k) { - logger.warn("Texture {} with size {}x{} limits mip level from {} to {}", - new Object[] { resourcelocation1, Integer.valueOf(textureatlassprite.getIconWidth()), - Integer.valueOf(textureatlassprite.getIconHeight()), - Integer.valueOf(MathHelper.calculateLogBaseTwo(k)), - Integer.valueOf(MathHelper.calculateLogBaseTwo(l1)) }); - k = l1; - } - - stitcher.addSprite(textureatlassprite); - continue; - } - - try { - IResource iresource = resourceManager.getResource(resourcelocation1); - ImageData[] abufferedimage = new ImageData[1 + this.mipmapLevels]; - abufferedimage[0] = TextureUtil.readBufferedImage(iresource.getInputStream()); - TextureMetadataSection texturemetadatasection = (TextureMetadataSection) iresource - .getMetadata("texture"); - if (texturemetadatasection != null) { - List list = texturemetadatasection.getListMipmaps(); - if (!list.isEmpty()) { - int l = abufferedimage[0].width; - int i1 = abufferedimage[0].height; - if (MathHelper.roundUpToPowerOfTwo(l) != l || MathHelper.roundUpToPowerOfTwo(i1) != i1) { - throw new RuntimeException( - "Unable to load extra miplevels, source-texture is not power of two"); - } - } - - Iterator iterator = list.iterator(); - - while (iterator.hasNext()) { - int i2 = ((Integer) iterator.next()).intValue(); - if (i2 > 0 && i2 < abufferedimage.length - 1 && abufferedimage[i2] == null) { - ResourceLocation resourcelocation2 = this.completeResourceLocation(resourcelocation, i2); - - try { - abufferedimage[i2] = TextureUtil.readBufferedImage( - resourceManager.getResource(resourcelocation2).getInputStream()); - } catch (IOException ioexception) { - logger.error("Unable to load miplevel {} from: {}", - new Object[] { Integer.valueOf(i2), resourcelocation2 }); - logger.error(ioexception); - } - } - } - } - - AnimationMetadataSection animationmetadatasection = (AnimationMetadataSection) iresource - .getMetadata("animation"); - textureatlassprite.loadSprite(abufferedimage, animationmetadatasection); - } catch (RuntimeException runtimeexception) { - logger.error("Unable to parse metadata from " + resourcelocation1); - logger.error(runtimeexception); - continue; - } catch (IOException ioexception1) { - logger.error("Using missing texture, unable to load " + resourcelocation1); - logger.error(ioexception1); - continue; - } - - j = Math.min(j, Math.min(textureatlassprite.getIconWidth(), textureatlassprite.getIconHeight())); - int l1 = Math.min(Integer.lowestOneBit(textureatlassprite.getIconWidth()), - Integer.lowestOneBit(textureatlassprite.getIconHeight())); - if (l1 < k) { - logger.warn("Texture {} with size {}x{} limits mip level from {} to {}", - new Object[] { resourcelocation1, Integer.valueOf(textureatlassprite.getIconWidth()), - Integer.valueOf(textureatlassprite.getIconHeight()), - Integer.valueOf(MathHelper.calculateLogBaseTwo(k)), - Integer.valueOf(MathHelper.calculateLogBaseTwo(l1)) }); - k = l1; - } - - stitcher.addSprite(textureatlassprite); - } - - int j1 = Math.min(j, k); - int k1 = MathHelper.calculateLogBaseTwo(j1); - if (k1 < this.mipmapLevels) { - logger.warn("{}: dropping miplevel from {} to {}, because of minimum power of two: {}", new Object[] { - this.basePath, Integer.valueOf(this.mipmapLevels), Integer.valueOf(k1), Integer.valueOf(j1) }); - this.mipmapLevels = k1; - } - - for (final EaglerTextureAtlasSprite textureatlassprite1 : this.mapRegisteredSprites.values()) { - try { - textureatlassprite1.generateMipmaps(this.mipmapLevels); - } catch (Throwable throwable1) { - CrashReport crashreport = CrashReport.makeCrashReport(throwable1, "Applying mipmap"); - CrashReportCategory crashreportcategory = crashreport.makeCategory("Sprite being mipmapped"); - crashreportcategory.addCrashSectionCallable("Sprite name", new Callable() { - public String call() throws Exception { - return textureatlassprite1.getIconName(); - } - }); - crashreportcategory.addCrashSectionCallable("Sprite size", new Callable() { - public String call() throws Exception { - return textureatlassprite1.getIconWidth() + " x " + textureatlassprite1.getIconHeight(); - } - }); - crashreportcategory.addCrashSectionCallable("Sprite frames", new Callable() { - public String call() throws Exception { - return textureatlassprite1.getFrameCount() + " frames"; - } - }); - crashreportcategory.addCrashSection("Mipmap levels", Integer.valueOf(this.mipmapLevels)); - throw new ReportedException(crashreport); - } - } - - if (isEaglerPBRMode) { - this.missingImagePBR.generateMipmaps(this.mipmapLevels); - stitcher.addSprite(this.missingImagePBR); - } else { - this.missingImage.generateMipmaps(this.mipmapLevels); - stitcher.addSprite(this.missingImage); - } - - try { - stitcher.doStitch(); - } catch (StitcherException stitcherexception) { - throw stitcherexception; - } - - logger.info("Created: {}x{} {}-atlas", new Object[] { Integer.valueOf(stitcher.getCurrentWidth()), - Integer.valueOf(stitcher.getCurrentHeight()), this.basePath }); - TextureUtil.allocateTextureImpl(this.getGlTextureId(), this.mipmapLevels, stitcher.getCurrentWidth(), - stitcher.getCurrentHeight()); - if (isEaglerPBRMode) { - TextureUtil.allocateTextureImpl(eaglerPBRMaterialTexture, this.mipmapLevels, stitcher.getCurrentWidth(), - stitcher.getCurrentHeight() * 2); - } - - TextureUtil.bindTexture(this.glTextureId); - - copyColorFramebuffer = new IFramebufferGL[this.mipmapLevels + 1]; - for (int l = 0; l < copyColorFramebuffer.length; ++l) { - copyColorFramebuffer[l] = _wglCreateFramebuffer(); - _wglBindFramebuffer(_GL_FRAMEBUFFER, copyColorFramebuffer[l]); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(this.glTextureId), l); - } - - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - - HashMap hashmap = Maps.newHashMap(this.mapRegisteredSprites); - - width = stitcher.getCurrentWidth(); - height = stitcher.getCurrentHeight(); - - List spriteList = stitcher.getStichSlots(); - for (int l = 0, m = spriteList.size(); l < m; ++l) { - EaglerTextureAtlasSprite textureatlassprite2 = spriteList.get(l); - String s = textureatlassprite2.getIconName(); - hashmap.remove(s); - this.mapUploadedSprites.put(s, textureatlassprite2); - - try { - TextureUtil.bindTexture(this.glTextureId); - TextureUtil.uploadTextureMipmap(textureatlassprite2.getFrameTextureData(0), - textureatlassprite2.getIconWidth(), textureatlassprite2.getIconHeight(), - textureatlassprite2.getOriginX(), textureatlassprite2.getOriginY(), false, false); - if (isEaglerPBRMode) { - TextureUtil.bindTexture(eaglerPBRMaterialTexture); - int[][][] pixels = ((EaglerTextureAtlasSpritePBR) textureatlassprite2).getFramePBRTextureData(0); - TextureUtil.uploadTextureMipmap(pixels[1], textureatlassprite2.getIconWidth(), - textureatlassprite2.getIconHeight(), textureatlassprite2.getOriginX(), - textureatlassprite2.getOriginY(), false, false); - TextureUtil.uploadTextureMipmap(pixels[2], textureatlassprite2.getIconWidth(), - textureatlassprite2.getIconHeight(), textureatlassprite2.getOriginX(), - textureatlassprite2.getOriginY() + height, false, false); - } - } catch (Throwable throwable) { - CrashReport crashreport1 = CrashReport.makeCrashReport(throwable, "Stitching texture atlas"); - CrashReportCategory crashreportcategory1 = crashreport1.makeCategory("Texture being stitched together"); - crashreportcategory1.addCrashSection("Atlas path", this.basePath); - crashreportcategory1.addCrashSection("Sprite", textureatlassprite2); - throw new ReportedException(crashreport1); - } - - if (textureatlassprite2.hasAnimationMetadata()) { - this.listAnimatedSprites.add(textureatlassprite2); - } - } - - for (EaglerTextureAtlasSprite textureatlassprite3 : (Collection) hashmap.values()) { - textureatlassprite3.copyFrom(this.missingImage); - } - - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - } - - private ResourceLocation completeResourceLocation(ResourceLocation location, int parInt1) { - return parInt1 == 0 - ? new ResourceLocation(location.getResourceDomain(), - HString.format("%s/%s%s", new Object[] { this.basePath, location.getResourcePath(), ".png" })) - : new ResourceLocation(location.getResourceDomain(), HString.format("%s/mipmaps/%s.%d%s", - new Object[] { this.basePath, location.getResourcePath(), Integer.valueOf(parInt1), ".png" })); - } - - public EaglerTextureAtlasSprite getAtlasSprite(String iconName) { - EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapUploadedSprites.get(iconName); - if (textureatlassprite == null) { - textureatlassprite = isEaglerPBRMode ? missingImagePBR : missingImage; - } - - return textureatlassprite; - } - - public void updateAnimations() { - if (isEaglerPBRMode) { - for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) { - this.listAnimatedSprites.get(i).updateAnimationPBR(copyColorFramebuffer, copyMaterialFramebuffer, - height); - } - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - return; - } - - for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) { - this.listAnimatedSprites.get(i).updateAnimation(copyColorFramebuffer); - } - - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - } - - private void destroyAnimationCaches() { - for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) { - this.listAnimatedSprites.get(i).clearFramesTextureData(); - } - } - - public EaglerTextureAtlasSprite registerSprite(ResourceLocation location) { - if (location == null) { - throw new IllegalArgumentException("Location cannot be null!"); - } else { - EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapRegisteredSprites - .get(location); - if (textureatlassprite == null) { - if (isEaglerPBRMode) { - textureatlassprite = EaglerTextureAtlasSpritePBR.makeAtlasSprite(location); - } else { - textureatlassprite = EaglerTextureAtlasSprite.makeAtlasSprite(location); - } - this.mapRegisteredSprites.put(location.toString(), textureatlassprite); - } - - return textureatlassprite; - } - } - - public void tick() { - this.updateAnimations(); - } - - public void setMipmapLevels(int mipmapLevelsIn) { - this.mipmapLevels = mipmapLevelsIn; - } - - public EaglerTextureAtlasSprite getMissingSprite() { - return isEaglerPBRMode ? missingImagePBR : missingImage; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public void setEnablePBREagler(boolean enable) { - isEaglerPBRMode = enable; - } - - public void setBlurMipmapDirect0(boolean parFlag, boolean parFlag2) { - super.setBlurMipmapDirect0(parFlag, parFlag2); - if (isEaglerPBRMode && eaglerPBRMaterialTexture != -1) { - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(eaglerPBRMaterialTexture); - super.setBlurMipmapDirect0(parFlag, parFlag2); - GlStateManager.setActiveTexture(GL_TEXTURE0); - } - } +package net.minecraft.client.renderer.texture; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Callable; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.EaglerTextureAtlasSpritePBR; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.PBRTextureMapUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.StitcherException; +import net.minecraft.client.resources.IResource; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.data.AnimationMetadataSection; +import net.minecraft.client.resources.data.TextureMetadataSection; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ReportedException; +import net.minecraft.util.ResourceLocation; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TextureMap extends AbstractTexture implements ITickableTextureObject { + private static final Logger logger = LogManager.getLogger(); + public static final ResourceLocation LOCATION_MISSING_TEXTURE = new ResourceLocation("missingno"); + public static final ResourceLocation locationBlocksTexture = new ResourceLocation("textures/atlas/blocks.png"); + private final List listAnimatedSprites; + private final Map mapRegisteredSprites; + private final Map mapUploadedSprites; + private final String basePath; + private final IIconCreator iconCreator; + private int mipmapLevels; + private final EaglerTextureAtlasSprite missingImage; + private final EaglerTextureAtlasSpritePBR missingImagePBR; + private int width; + private int height; + private boolean isEaglerPBRMode = false; + public int eaglerPBRMaterialTexture = -1; + private boolean hasAllocatedEaglerPBRMaterialTexture = false; + private boolean isGLES2 = false; + + public static final int _GL_FRAMEBUFFER = 0x8D40; + public static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; + + private IFramebufferGL[] copyColorFramebuffer = null; + private IFramebufferGL[] copyMaterialFramebuffer = null; + + public TextureMap(String parString1) { + this(parString1, (IIconCreator) null); + } + + public TextureMap(String parString1, IIconCreator iconCreatorIn) { + this.listAnimatedSprites = Lists.newArrayList(); + this.mapRegisteredSprites = Maps.newHashMap(); + this.mapUploadedSprites = Maps.newHashMap(); + this.missingImage = new EaglerTextureAtlasSprite("missingno"); + this.missingImagePBR = new EaglerTextureAtlasSpritePBR("missingno"); + this.basePath = parString1; + this.iconCreator = iconCreatorIn; + this.isGLES2 = EaglercraftGPU.checkOpenGLESVersion() == 200; + } + + private void initMissingImage() { + int[] aint = TextureUtil.missingTextureData; + this.missingImage.setIconWidth(16); + this.missingImage.setIconHeight(16); + int[][] aint1 = new int[this.mipmapLevels + 1][]; + aint1[0] = aint; + this.missingImage.setFramesTextureData(Lists.newArrayList(new int[][][] { aint1 })); + this.missingImagePBR.setIconWidth(16); + this.missingImagePBR.setIconHeight(16); + int[][][] aint2 = new int[3][this.mipmapLevels + 1][]; + aint2[0][0] = aint; + int[] missingNormals = new int[256]; + for (int i = 0; i < missingNormals.length; ++i) { + missingNormals[i] = 0xFF7F7F; + } + aint2[1][0] = missingNormals; + int[] missingMaterial = new int[256]; + for (int i = 0; i < missingMaterial.length; ++i) { + missingMaterial[i] = 0x00000077; + } + aint2[2][0] = missingMaterial; + this.missingImagePBR.setFramesTextureDataPBR(new List[] { Lists.newArrayList(new int[][][] { aint2[0] }), + Lists.newArrayList(new int[][][] { aint2[1] }), Lists.newArrayList(new int[][][] { aint2[2] }) }); + } + + public void loadTexture(IResourceManager parIResourceManager) throws IOException { + if (this.iconCreator != null) { + this.loadSprites(parIResourceManager, this.iconCreator); + } + } + + public void loadSprites(IResourceManager resourceManager, IIconCreator parIIconCreator) { + destroyAnimationCaches(); + this.mapRegisteredSprites.clear(); + parIIconCreator.registerSprites(this); + this.initMissingImage(); + this.deleteGlTexture(); + this.loadTextureAtlas(resourceManager); + } + + public void deleteGlTexture() { + super.deleteGlTexture(); + if (eaglerPBRMaterialTexture != -1) { + GlStateManager.deleteTexture(eaglerPBRMaterialTexture); + eaglerPBRMaterialTexture = -1; + } + if (copyColorFramebuffer != null) { + for (int i = 0; i < copyColorFramebuffer.length; ++i) { + _wglDeleteFramebuffer(copyColorFramebuffer[i]); + } + copyColorFramebuffer = null; + } + if (copyMaterialFramebuffer != null) { + for (int i = 0; i < copyMaterialFramebuffer.length; ++i) { + _wglDeleteFramebuffer(copyMaterialFramebuffer[i]); + } + copyMaterialFramebuffer = null; + } + } + + public void loadTextureAtlas(IResourceManager resourceManager) { + int i = Minecraft.getGLMaximumTextureSize(); + Stitcher stitcher = new Stitcher(i, i, true, 0, this.mipmapLevels); + this.mapUploadedSprites.clear(); + this.listAnimatedSprites.clear(); + int j = Integer.MAX_VALUE; + int k = 1 << this.mipmapLevels; + + if (copyColorFramebuffer != null) { + for (int l = 0; l < copyColorFramebuffer.length; ++l) { + _wglDeleteFramebuffer(copyColorFramebuffer[l]); + } + copyColorFramebuffer = null; + } + + if (isEaglerPBRMode) { + if (eaglerPBRMaterialTexture == -1) { + eaglerPBRMaterialTexture = GlStateManager.generateTexture(); + } + if (copyMaterialFramebuffer == null) { + GlStateManager.bindTexture(eaglerPBRMaterialTexture); + copyMaterialFramebuffer = new IFramebufferGL[this.mipmapLevels + 1]; + for (int l = 0; l < copyMaterialFramebuffer.length; ++l) { + copyMaterialFramebuffer[l] = _wglCreateFramebuffer(); + _wglBindFramebuffer(_GL_FRAMEBUFFER, copyMaterialFramebuffer[l]); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + EaglercraftGPU.getNativeTexture(eaglerPBRMaterialTexture), l); + } + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + } + } else { + if (eaglerPBRMaterialTexture != -1) { + GlStateManager.deleteTexture(eaglerPBRMaterialTexture); + eaglerPBRMaterialTexture = -1; + } + if (copyMaterialFramebuffer != null) { + for (int l = 0; l < copyMaterialFramebuffer.length; ++l) { + _wglDeleteFramebuffer(copyMaterialFramebuffer[l]); + } + copyMaterialFramebuffer = null; + } + } + + for (Entry entry : this.mapRegisteredSprites.entrySet()) { + EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) entry.getValue(); + ResourceLocation resourcelocation = new ResourceLocation(textureatlassprite.getIconName()); + ResourceLocation resourcelocation1 = this.completeResourceLocation(resourcelocation, 0); + + if (isEaglerPBRMode) { + try { + IResource iresource = resourceManager.getResource(resourcelocation1); + ImageData[] abufferedimageColor = new ImageData[1 + this.mipmapLevels]; + ImageData[] abufferedimageNormal = new ImageData[1 + this.mipmapLevels]; + ImageData[] abufferedimageMaterial = new ImageData[1 + this.mipmapLevels]; + abufferedimageColor[0] = TextureUtil.readBufferedImage(iresource.getInputStream()); + abufferedimageNormal[0] = PBRTextureMapUtils.locateCompanionTexture(resourceManager, iresource, + "_n"); + abufferedimageMaterial[0] = PBRTextureMapUtils.locateCompanionTexture(resourceManager, iresource, + "_s"); + boolean dontAnimateNormals = false; + boolean dontAnimateMaterial = false; + if (abufferedimageNormal[0] == null) { + abufferedimageNormal[0] = PBRTextureMapUtils.defaultNormalsTexture; + dontAnimateNormals = true; + } + if (abufferedimageMaterial[0] == null) { + abufferedimageMaterial[0] = PBRTextureMapUtils.generateMaterialTextureFor( + ((EaglerTextureAtlasSprite) (entry.getValue())).getIconName()); + dontAnimateMaterial = true; + } + PBRTextureMapUtils.unifySizes(0, abufferedimageColor, abufferedimageNormal, abufferedimageMaterial); + + TextureMetadataSection texturemetadatasection = (TextureMetadataSection) iresource + .getMetadata("texture"); + if (texturemetadatasection != null) { + List list = texturemetadatasection.getListMipmaps(); + if (!list.isEmpty()) { + int l = abufferedimageColor[0].width; + int i1 = abufferedimageColor[0].height; + if (MathHelper.roundUpToPowerOfTwo(l) != l || MathHelper.roundUpToPowerOfTwo(i1) != i1) { + throw new RuntimeException( + "Unable to load extra miplevels, source-texture is not power of two"); + } + } + + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + int i2 = ((Integer) iterator.next()).intValue(); + if (i2 > 0 && i2 < abufferedimageColor.length - 1 && abufferedimageColor[i2] == null) { + ResourceLocation resourcelocation2 = this.completeResourceLocation(resourcelocation, + i2); + + try { + IResource mipLevelResource = resourceManager.getResource(resourcelocation2); + abufferedimageColor[i2] = TextureUtil + .readBufferedImage(mipLevelResource.getInputStream()); + abufferedimageNormal[i2] = PBRTextureMapUtils + .locateCompanionTexture(resourceManager, mipLevelResource, "_n"); + abufferedimageMaterial[i2] = PBRTextureMapUtils + .locateCompanionTexture(resourceManager, mipLevelResource, "_s"); + if (abufferedimageNormal[i2] == null) { + abufferedimageNormal[i2] = PBRTextureMapUtils.defaultNormalsTexture; + } + if (abufferedimageMaterial[i2] == null) { + abufferedimageMaterial[i2] = PBRTextureMapUtils.generateMaterialTextureFor( + ((EaglerTextureAtlasSprite) (entry.getValue())).getIconName()); + } + PBRTextureMapUtils.unifySizes(i2, abufferedimageColor, abufferedimageNormal, + abufferedimageMaterial); + if ((abufferedimageColor[0].width >> i2) != abufferedimageColor[i2].width) { + throw new IOException("Mipmap level " + i2 + " is the wrong size, should be " + + (abufferedimageColor[0].width >> i2) + " pixels"); + } + } catch (Throwable exc) { + logger.error("Unable to load miplevel {} from: {}", i2, resourcelocation2); + logger.error(exc); + } + } + } + } + + AnimationMetadataSection animationmetadatasection = (AnimationMetadataSection) iresource + .getMetadata("animation"); + textureatlassprite.loadSpritePBR( + new ImageData[][] { abufferedimageColor, abufferedimageNormal, abufferedimageMaterial }, + animationmetadatasection, dontAnimateNormals, dontAnimateMaterial); + } catch (RuntimeException runtimeexception) { + logger.error("Unable to parse metadata from " + resourcelocation1); + logger.error(runtimeexception); + continue; + } catch (IOException ioexception1) { + logger.error("Using missing texture, unable to load " + resourcelocation1); + logger.error(ioexception1); + continue; + } + + j = Math.min(j, Math.min(textureatlassprite.getIconWidth(), textureatlassprite.getIconHeight())); + int l1 = Math.min(Integer.lowestOneBit(textureatlassprite.getIconWidth()), + Integer.lowestOneBit(textureatlassprite.getIconHeight())); + if (l1 < k) { + logger.warn("Texture {} with size {}x{} limits mip level from {} to {}", + new Object[] { resourcelocation1, Integer.valueOf(textureatlassprite.getIconWidth()), + Integer.valueOf(textureatlassprite.getIconHeight()), + Integer.valueOf(MathHelper.calculateLogBaseTwo(k)), + Integer.valueOf(MathHelper.calculateLogBaseTwo(l1)) }); + k = l1; + } + + stitcher.addSprite(textureatlassprite); + continue; + } + + try { + IResource iresource = resourceManager.getResource(resourcelocation1); + ImageData[] abufferedimage = new ImageData[1 + this.mipmapLevels]; + abufferedimage[0] = TextureUtil.readBufferedImage(iresource.getInputStream()); + TextureMetadataSection texturemetadatasection = (TextureMetadataSection) iresource + .getMetadata("texture"); + if (texturemetadatasection != null) { + List list = texturemetadatasection.getListMipmaps(); + if (!list.isEmpty()) { + int l = abufferedimage[0].width; + int i1 = abufferedimage[0].height; + if (MathHelper.roundUpToPowerOfTwo(l) != l || MathHelper.roundUpToPowerOfTwo(i1) != i1) { + throw new RuntimeException( + "Unable to load extra miplevels, source-texture is not power of two"); + } + } + + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + int i2 = ((Integer) iterator.next()).intValue(); + if (i2 > 0 && i2 < abufferedimage.length - 1 && abufferedimage[i2] == null) { + ResourceLocation resourcelocation2 = this.completeResourceLocation(resourcelocation, i2); + + try { + abufferedimage[i2] = TextureUtil.readBufferedImage( + resourceManager.getResource(resourcelocation2).getInputStream()); + } catch (IOException ioexception) { + logger.error("Unable to load miplevel {} from: {}", + new Object[] { Integer.valueOf(i2), resourcelocation2 }); + logger.error(ioexception); + } + } + } + } + + AnimationMetadataSection animationmetadatasection = (AnimationMetadataSection) iresource + .getMetadata("animation"); + textureatlassprite.loadSprite(abufferedimage, animationmetadatasection); + } catch (RuntimeException runtimeexception) { + logger.error("Unable to parse metadata from " + resourcelocation1); + logger.error(runtimeexception); + continue; + } catch (IOException ioexception1) { + logger.error("Using missing texture, unable to load " + resourcelocation1); + logger.error(ioexception1); + continue; + } + + j = Math.min(j, Math.min(textureatlassprite.getIconWidth(), textureatlassprite.getIconHeight())); + int l1 = Math.min(Integer.lowestOneBit(textureatlassprite.getIconWidth()), + Integer.lowestOneBit(textureatlassprite.getIconHeight())); + if (l1 < k) { + logger.warn("Texture {} with size {}x{} limits mip level from {} to {}", + new Object[] { resourcelocation1, Integer.valueOf(textureatlassprite.getIconWidth()), + Integer.valueOf(textureatlassprite.getIconHeight()), + Integer.valueOf(MathHelper.calculateLogBaseTwo(k)), + Integer.valueOf(MathHelper.calculateLogBaseTwo(l1)) }); + k = l1; + } + + stitcher.addSprite(textureatlassprite); + } + + int j1 = Math.min(j, k); + int k1 = MathHelper.calculateLogBaseTwo(j1); + if (k1 < this.mipmapLevels) { + logger.warn("{}: dropping miplevel from {} to {}, because of minimum power of two: {}", new Object[] { + this.basePath, Integer.valueOf(this.mipmapLevels), Integer.valueOf(k1), Integer.valueOf(j1) }); + this.mipmapLevels = k1; + } + + for (final EaglerTextureAtlasSprite textureatlassprite1 : this.mapRegisteredSprites.values()) { + try { + textureatlassprite1.generateMipmaps(this.mipmapLevels); + } catch (Throwable throwable1) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable1, "Applying mipmap"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Sprite being mipmapped"); + crashreportcategory.addCrashSectionCallable("Sprite name", new Callable() { + public String call() throws Exception { + return textureatlassprite1.getIconName(); + } + }); + crashreportcategory.addCrashSectionCallable("Sprite size", new Callable() { + public String call() throws Exception { + return textureatlassprite1.getIconWidth() + " x " + textureatlassprite1.getIconHeight(); + } + }); + crashreportcategory.addCrashSectionCallable("Sprite frames", new Callable() { + public String call() throws Exception { + return textureatlassprite1.getFrameCount() + " frames"; + } + }); + crashreportcategory.addCrashSection("Mipmap levels", Integer.valueOf(this.mipmapLevels)); + throw new ReportedException(crashreport); + } + } + + if (isEaglerPBRMode) { + this.missingImagePBR.generateMipmaps(this.mipmapLevels); + stitcher.addSprite(this.missingImagePBR); + } else { + this.missingImage.generateMipmaps(this.mipmapLevels); + stitcher.addSprite(this.missingImage); + } + + try { + stitcher.doStitch(); + } catch (StitcherException stitcherexception) { + throw stitcherexception; + } + + logger.info("Created: {}x{} {}-atlas", new Object[] { Integer.valueOf(stitcher.getCurrentWidth()), + Integer.valueOf(stitcher.getCurrentHeight()), this.basePath }); + TextureUtil.allocateTextureImpl(this.getGlTextureId(), this.mipmapLevels, stitcher.getCurrentWidth(), + stitcher.getCurrentHeight()); + + if (isEaglerPBRMode) { + TextureUtil.allocateTextureImpl(eaglerPBRMaterialTexture, this.mipmapLevels, stitcher.getCurrentWidth(), + stitcher.getCurrentHeight() * 2); + } + + TextureUtil.bindTexture(this.glTextureId); + + copyColorFramebuffer = new IFramebufferGL[this.mipmapLevels + 1]; + for (int l = 0; l < copyColorFramebuffer.length; ++l) { + copyColorFramebuffer[l] = _wglCreateFramebuffer(); + _wglBindFramebuffer(_GL_FRAMEBUFFER, copyColorFramebuffer[l]); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + EaglercraftGPU.getNativeTexture(this.glTextureId), l); + } + + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + + HashMap hashmap = Maps.newHashMap(this.mapRegisteredSprites); + + width = stitcher.getCurrentWidth(); + height = stitcher.getCurrentHeight(); + + List spriteList = stitcher.getStichSlots(); + for (int l = 0, m = spriteList.size(); l < m; ++l) { + EaglerTextureAtlasSprite textureatlassprite2 = spriteList.get(l); + String s = textureatlassprite2.getIconName(); + hashmap.remove(s); + this.mapUploadedSprites.put(s, textureatlassprite2); + + try { + TextureUtil.bindTexture(this.glTextureId); + TextureUtil.uploadTextureMipmap(textureatlassprite2.getFrameTextureData(0), + textureatlassprite2.getIconWidth(), textureatlassprite2.getIconHeight(), + textureatlassprite2.getOriginX(), textureatlassprite2.getOriginY(), false, false); + if (isEaglerPBRMode) { + TextureUtil.bindTexture(eaglerPBRMaterialTexture); + int[][][] pixels = ((EaglerTextureAtlasSpritePBR) textureatlassprite2).getFramePBRTextureData(0); + TextureUtil.uploadTextureMipmap(pixels[1], textureatlassprite2.getIconWidth(), + textureatlassprite2.getIconHeight(), textureatlassprite2.getOriginX(), + textureatlassprite2.getOriginY(), false, false); + TextureUtil.uploadTextureMipmap(pixels[2], textureatlassprite2.getIconWidth(), + textureatlassprite2.getIconHeight(), textureatlassprite2.getOriginX(), + textureatlassprite2.getOriginY() + height, false, false); + } + } catch (Throwable throwable) { + CrashReport crashreport1 = CrashReport.makeCrashReport(throwable, "Stitching texture atlas"); + CrashReportCategory crashreportcategory1 = crashreport1.makeCategory("Texture being stitched together"); + crashreportcategory1.addCrashSection("Atlas path", this.basePath); + crashreportcategory1.addCrashSection("Sprite", textureatlassprite2); + throw new ReportedException(crashreport1); + } + + if (textureatlassprite2.hasAnimationMetadata()) { + this.listAnimatedSprites.add(textureatlassprite2); + } + } + + for (EaglerTextureAtlasSprite textureatlassprite3 : (Collection) hashmap.values()) { + textureatlassprite3.copyFrom(this.missingImage); + } + + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + } + + private ResourceLocation completeResourceLocation(ResourceLocation location, int parInt1) { + return parInt1 == 0 + ? new ResourceLocation(location.getResourceDomain(), + HString.format("%s/%s%s", new Object[] { this.basePath, location.getResourcePath(), ".png" })) + : new ResourceLocation(location.getResourceDomain(), HString.format("%s/mipmaps/%s.%d%s", + new Object[] { this.basePath, location.getResourcePath(), Integer.valueOf(parInt1), ".png" })); + } + + public EaglerTextureAtlasSprite getAtlasSprite(String iconName) { + EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapUploadedSprites.get(iconName); + if (textureatlassprite == null) { + textureatlassprite = isEaglerPBRMode ? missingImagePBR : missingImage; + } + + return textureatlassprite; + } + + public void updateAnimations() { + if (isEaglerPBRMode) { + for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) { + this.listAnimatedSprites.get(i).updateAnimationPBR(copyColorFramebuffer, copyMaterialFramebuffer, + height); + } + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + return; + } + + for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) { + this.listAnimatedSprites.get(i).updateAnimation(copyColorFramebuffer); + } + + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + } + + private void destroyAnimationCaches() { + for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) { + this.listAnimatedSprites.get(i).clearFramesTextureData(); + } + } + + public EaglerTextureAtlasSprite registerSprite(ResourceLocation location) { + if (location == null) { + throw new IllegalArgumentException("Location cannot be null!"); + } else { + EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapRegisteredSprites + .get(location); + if (textureatlassprite == null) { + if (isEaglerPBRMode) { + textureatlassprite = EaglerTextureAtlasSpritePBR.makeAtlasSprite(location); + } else { + textureatlassprite = EaglerTextureAtlasSprite.makeAtlasSprite(location); + } + this.mapRegisteredSprites.put(location.toString(), textureatlassprite); + } + + return textureatlassprite; + } + } + + public void tick() { + this.updateAnimations(); + } + + public void setMipmapLevels(int mipmapLevelsIn) { + if (!isGLES2) { + this.mipmapLevels = mipmapLevelsIn; + } else { + this.mipmapLevels = 0; // Due to limitations in OpenGL ES 2.0 texture completeness, its easier to just + // make this zero + } + } + + public EaglerTextureAtlasSprite getMissingSprite() { + return isEaglerPBRMode ? missingImagePBR : missingImage; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void setEnablePBREagler(boolean enable) { + isEaglerPBRMode = enable; + } + + public void setBlurMipmapDirect0(boolean parFlag, boolean parFlag2) { + if (isGLES2) { + super.setBlurMipmapDirect0(parFlag, false); + } else { + super.setBlurMipmapDirect0(parFlag, parFlag2); + if (isEaglerPBRMode && eaglerPBRMaterialTexture != -1) { + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(eaglerPBRMaterialTexture); + super.setBlurMipmapDirect0(parFlag, parFlag2); + GlStateManager.setActiveTexture(GL_TEXTURE0); + } + } + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/renderer/texture/TextureUtil.java b/src/game/java/net/minecraft/client/renderer/texture/TextureUtil.java similarity index 92% rename from src/main/java/net/minecraft/client/renderer/texture/TextureUtil.java rename to src/game/java/net/minecraft/client/renderer/texture/TextureUtil.java index 83dcfda..045bae6 100644 --- a/src/main/java/net/minecraft/client/renderer/texture/TextureUtil.java +++ b/src/game/java/net/minecraft/client/renderer/texture/TextureUtil.java @@ -174,6 +174,11 @@ public class TextureUtil { int parInt5, boolean parFlag, boolean parFlag2, boolean parFlag3) { int i = 4194304 / parInt2; setTextureBlurMipmap(parFlag, parFlag3); + if (!parFlag2 && !EaglercraftGPU.checkNPOTCapable() && ImageData.isNPOTStatic(parInt2, parInt3)) { + parFlag2 = true; + logger.warn( + "An NPOT (non-power-of-two) texture was allocated with GL_REPEAT wrapping in an OpenGL context where that isn't supported, changing to GL_CLAMP_TO_EDGE to avoid errors"); + } setTextureClamped(parFlag2); int l; @@ -202,10 +207,12 @@ public class TextureUtil { // deleteTexture(parInt1); //TODO: why bindTexture(parInt1); if (parInt2 >= 0) { - EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, '\u813d', parInt2); - EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u813a', 0.0F); - EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u813b', (float) parInt2); - // EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u8501', 0.0F); + if (EaglercraftGPU.checkOpenGLESVersion() >= 300) { + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, '\u813d', parInt2); + EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u813a', 0.0F); + EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u813b', (float) parInt2); + // EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u8501', 0.0F); + } } EaglercraftGPU.glTexStorage2D(GL_TEXTURE_2D, parInt2 + 1, GL_RGBA8, parInt3, parInt4); } @@ -224,6 +231,11 @@ public class TextureUtil { int k = 4194304 / i; int[] aint = new int[k * i]; setTextureBlurred(parFlag); + if (!parFlag2 && !EaglercraftGPU.checkNPOTCapable() && parBufferedImage.isNPOT()) { + parFlag2 = true; + logger.warn( + "An NPOT (non-power-of-two) texture was allocated with GL_REPEAT wrapping in an OpenGL context where that isn't supported, changing to GL_CLAMP_TO_EDGE to avoid errors"); + } setTextureClamped(parFlag2); for (int l = 0; l < i * j; l += i * k) { diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.java b/src/game/java/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.java rename to src/game/java/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/RenderItemFrame.java b/src/game/java/net/minecraft/client/renderer/tileentity/RenderItemFrame.java similarity index 99% rename from src/main/java/net/minecraft/client/renderer/tileentity/RenderItemFrame.java rename to src/game/java/net/minecraft/client/renderer/tileentity/RenderItemFrame.java index 3f21f53..dc3bd8a 100644 --- a/src/main/java/net/minecraft/client/renderer/tileentity/RenderItemFrame.java +++ b/src/game/java/net/minecraft/client/renderer/tileentity/RenderItemFrame.java @@ -188,7 +188,7 @@ public class RenderItemFrame extends Render { double d3 = entityitemframe.getDistanceSqToEntity(this.renderManager.livingPlayer); float f2 = entityitemframe.isSneaking() ? 32.0F : 64.0F; if (d3 < (double) (f2 * f2)) { - String s = entityitemframe.getDisplayedItem().getDisplayName(); + String s = entityitemframe.getDisplayedItem().getDisplayNameProfanityFilter(); if (entityitemframe.isSneaking()) { if (DeferredStateManager.isInDeferredPass()) { NameTagRenderer.renderNameTag(entityitemframe, null, d0, d1, d2, -69); diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/RenderWitherSkull.java b/src/game/java/net/minecraft/client/renderer/tileentity/RenderWitherSkull.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/RenderWitherSkull.java rename to src/game/java/net/minecraft/client/renderer/tileentity/RenderWitherSkull.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.java similarity index 96% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.java index a28b447..8597058 100644 --- a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.java +++ b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.java @@ -8,6 +8,7 @@ import java.util.Map; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.client.Minecraft; import net.minecraft.client.model.ModelBanner; @@ -113,7 +114,7 @@ public class TileEntityBannerRenderer extends TileEntitySpecialRenderer= 256) { - long i = System.currentTimeMillis(); + long i = EagRuntime.steadyTimeMillis(); Iterator iterator = DESIGNS.keySet().iterator(); while (iterator.hasNext()) { @@ -149,7 +150,7 @@ public class TileEntityBannerRenderer extends TileEntitySpecialRenderer= 24 && calendar.get(5) <= 26) { this.isChristams = true; } diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.java similarity index 92% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.java index 8e1f36b..e4b7dd2 100644 --- a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.java +++ b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.java @@ -57,6 +57,8 @@ public class TileEntitySignRenderer extends TileEntitySpecialRenderer 0 ? ((IChatComponent) list.get(0)).getFormattedText() : ""; if (j == tileentitysign.lineBeingEdited) { s = "> " + s + " <"; - fontrenderer.drawString(s, -fontrenderer.getStringWidth(s) / 2, - j * 10 - tileentitysign.signText.length * 5, b0); + fontrenderer.drawString(s, -fontrenderer.getStringWidth(s) / 2, j * 10 - signText.length * 5, + b0); } else { - fontrenderer.drawString(s, -fontrenderer.getStringWidth(s) / 2, - j * 10 - tileentitysign.signText.length * 5, b0); + fontrenderer.drawString(s, -fontrenderer.getStringWidth(s) / 2, j * 10 - signText.length * 5, + b0); } } } diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.java rename to src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.java diff --git a/src/main/java/net/minecraft/client/renderer/vertex/DefaultVertexFormats.java b/src/game/java/net/minecraft/client/renderer/vertex/DefaultVertexFormats.java similarity index 100% rename from src/main/java/net/minecraft/client/renderer/vertex/DefaultVertexFormats.java rename to src/game/java/net/minecraft/client/renderer/vertex/DefaultVertexFormats.java diff --git a/src/main/java/net/minecraft/client/resources/AbstractResourcePack.java b/src/game/java/net/minecraft/client/resources/AbstractResourcePack.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/AbstractResourcePack.java rename to src/game/java/net/minecraft/client/resources/AbstractResourcePack.java diff --git a/src/main/java/net/minecraft/client/resources/DefaultPlayerSkin.java b/src/game/java/net/minecraft/client/resources/DefaultPlayerSkin.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/DefaultPlayerSkin.java rename to src/game/java/net/minecraft/client/resources/DefaultPlayerSkin.java diff --git a/src/main/java/net/minecraft/client/resources/DefaultResourcePack.java b/src/game/java/net/minecraft/client/resources/DefaultResourcePack.java similarity index 90% rename from src/main/java/net/minecraft/client/resources/DefaultResourcePack.java rename to src/game/java/net/minecraft/client/resources/DefaultResourcePack.java index 6b01186..4dd696c 100644 --- a/src/main/java/net/minecraft/client/resources/DefaultResourcePack.java +++ b/src/game/java/net/minecraft/client/resources/DefaultResourcePack.java @@ -69,8 +69,9 @@ public class DefaultResourcePack implements IResourcePack { .getResourceStream("/assets/" + location.getResourceDomain() + "/" + location.getResourcePath()); } - public boolean resourceExists(ResourceLocation resourcelocation) { - return this.getResourceStream(resourcelocation) != null; + public boolean resourceExists(ResourceLocation location) { + return EagRuntime + .getResourceExists("/assets/" + location.getResourceDomain() + "/" + location.getResourcePath()); } public Set getResourceDomains() { @@ -81,14 +82,14 @@ public class DefaultResourcePack implements IResourcePack { throws IOException { try { return AbstractResourcePack.readMetadata(parIMetadataSerializer, - EagRuntime.getResourceStream("pack.mcmeta"), parString1); + EagRuntime.getRequiredResourceStream("pack.mcmeta"), parString1); } catch (RuntimeException var4) { return (T) null; } } public ImageData getPackImage() throws IOException { - return TextureUtil.readBufferedImage(EagRuntime.getResourceStream("pack.png")); + return TextureUtil.readBufferedImage(EagRuntime.getRequiredResourceStream("pack.png")); } public String getPackName() { diff --git a/src/main/java/net/minecraft/client/resources/FallbackResourceManager.java b/src/game/java/net/minecraft/client/resources/FallbackResourceManager.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/FallbackResourceManager.java rename to src/game/java/net/minecraft/client/resources/FallbackResourceManager.java diff --git a/src/main/java/net/minecraft/client/resources/FoliageColorReloadListener.java b/src/game/java/net/minecraft/client/resources/FoliageColorReloadListener.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/FoliageColorReloadListener.java rename to src/game/java/net/minecraft/client/resources/FoliageColorReloadListener.java diff --git a/src/main/java/net/minecraft/client/resources/GrassColorReloadListener.java b/src/game/java/net/minecraft/client/resources/GrassColorReloadListener.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/GrassColorReloadListener.java rename to src/game/java/net/minecraft/client/resources/GrassColorReloadListener.java diff --git a/src/main/java/net/minecraft/client/resources/I18n.java b/src/game/java/net/minecraft/client/resources/I18n.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/I18n.java rename to src/game/java/net/minecraft/client/resources/I18n.java diff --git a/src/main/java/net/minecraft/client/resources/IReloadableResourceManager.java b/src/game/java/net/minecraft/client/resources/IReloadableResourceManager.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/IReloadableResourceManager.java rename to src/game/java/net/minecraft/client/resources/IReloadableResourceManager.java diff --git a/src/main/java/net/minecraft/client/resources/IResource.java b/src/game/java/net/minecraft/client/resources/IResource.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/IResource.java rename to src/game/java/net/minecraft/client/resources/IResource.java diff --git a/src/main/java/net/minecraft/client/resources/IResourceManager.java b/src/game/java/net/minecraft/client/resources/IResourceManager.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/IResourceManager.java rename to src/game/java/net/minecraft/client/resources/IResourceManager.java diff --git a/src/main/java/net/minecraft/client/resources/IResourceManagerReloadListener.java b/src/game/java/net/minecraft/client/resources/IResourceManagerReloadListener.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/IResourceManagerReloadListener.java rename to src/game/java/net/minecraft/client/resources/IResourceManagerReloadListener.java diff --git a/src/main/java/net/minecraft/client/resources/IResourcePack.java b/src/game/java/net/minecraft/client/resources/IResourcePack.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/IResourcePack.java rename to src/game/java/net/minecraft/client/resources/IResourcePack.java diff --git a/src/main/java/net/minecraft/client/resources/Language.java b/src/game/java/net/minecraft/client/resources/Language.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/Language.java rename to src/game/java/net/minecraft/client/resources/Language.java diff --git a/src/main/java/net/minecraft/client/resources/LanguageManager.java b/src/game/java/net/minecraft/client/resources/LanguageManager.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/LanguageManager.java rename to src/game/java/net/minecraft/client/resources/LanguageManager.java diff --git a/src/main/java/net/minecraft/client/resources/Locale.java b/src/game/java/net/minecraft/client/resources/Locale.java similarity index 98% rename from src/main/java/net/minecraft/client/resources/Locale.java rename to src/game/java/net/minecraft/client/resources/Locale.java index 7f1dc2a..5986d50 100644 --- a/src/main/java/net/minecraft/client/resources/Locale.java +++ b/src/game/java/net/minecraft/client/resources/Locale.java @@ -58,7 +58,7 @@ public class Locale { Map properties = Maps.newHashMap(); private boolean unicode; - private static final Set hasShownMissing = new HashSet(); + private static final Set hasShownMissing = new HashSet<>(); /** * + diff --git a/src/main/java/net/minecraft/client/resources/ResourcePackFileNotFoundException.java b/src/game/java/net/minecraft/client/resources/ResourcePackFileNotFoundException.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/ResourcePackFileNotFoundException.java rename to src/game/java/net/minecraft/client/resources/ResourcePackFileNotFoundException.java diff --git a/src/main/java/net/minecraft/client/resources/ResourcePackListEntry.java b/src/game/java/net/minecraft/client/resources/ResourcePackListEntry.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/ResourcePackListEntry.java rename to src/game/java/net/minecraft/client/resources/ResourcePackListEntry.java diff --git a/src/main/java/net/minecraft/client/resources/ResourcePackListEntryDefault.java b/src/game/java/net/minecraft/client/resources/ResourcePackListEntryDefault.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/ResourcePackListEntryDefault.java rename to src/game/java/net/minecraft/client/resources/ResourcePackListEntryDefault.java diff --git a/src/main/java/net/minecraft/client/resources/ResourcePackListEntryFound.java b/src/game/java/net/minecraft/client/resources/ResourcePackListEntryFound.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/ResourcePackListEntryFound.java rename to src/game/java/net/minecraft/client/resources/ResourcePackListEntryFound.java diff --git a/src/main/java/net/minecraft/client/resources/ResourcePackRepository.java b/src/game/java/net/minecraft/client/resources/ResourcePackRepository.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/ResourcePackRepository.java rename to src/game/java/net/minecraft/client/resources/ResourcePackRepository.java diff --git a/src/main/java/net/minecraft/client/resources/SimpleReloadableResourceManager.java b/src/game/java/net/minecraft/client/resources/SimpleReloadableResourceManager.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/SimpleReloadableResourceManager.java rename to src/game/java/net/minecraft/client/resources/SimpleReloadableResourceManager.java diff --git a/src/main/java/net/minecraft/client/resources/SimpleResource.java b/src/game/java/net/minecraft/client/resources/SimpleResource.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/SimpleResource.java rename to src/game/java/net/minecraft/client/resources/SimpleResource.java diff --git a/src/main/java/net/minecraft/client/resources/data/AnimationFrame.java b/src/game/java/net/minecraft/client/resources/data/AnimationFrame.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/AnimationFrame.java rename to src/game/java/net/minecraft/client/resources/data/AnimationFrame.java diff --git a/src/main/java/net/minecraft/client/resources/data/AnimationMetadataSection.java b/src/game/java/net/minecraft/client/resources/data/AnimationMetadataSection.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/AnimationMetadataSection.java rename to src/game/java/net/minecraft/client/resources/data/AnimationMetadataSection.java diff --git a/src/main/java/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/FontMetadataSection.java b/src/game/java/net/minecraft/client/resources/data/FontMetadataSection.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/FontMetadataSection.java rename to src/game/java/net/minecraft/client/resources/data/FontMetadataSection.java diff --git a/src/main/java/net/minecraft/client/resources/data/FontMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/FontMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/FontMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/FontMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/IMetadataSection.java b/src/game/java/net/minecraft/client/resources/data/IMetadataSection.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/IMetadataSection.java rename to src/game/java/net/minecraft/client/resources/data/IMetadataSection.java diff --git a/src/main/java/net/minecraft/client/resources/data/IMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/IMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/IMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/IMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/IMetadataSerializer.java b/src/game/java/net/minecraft/client/resources/data/IMetadataSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/IMetadataSerializer.java rename to src/game/java/net/minecraft/client/resources/data/IMetadataSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/LanguageMetadataSection.java b/src/game/java/net/minecraft/client/resources/data/LanguageMetadataSection.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/LanguageMetadataSection.java rename to src/game/java/net/minecraft/client/resources/data/LanguageMetadataSection.java diff --git a/src/main/java/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/PackMetadataSection.java b/src/game/java/net/minecraft/client/resources/data/PackMetadataSection.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/PackMetadataSection.java rename to src/game/java/net/minecraft/client/resources/data/PackMetadataSection.java diff --git a/src/main/java/net/minecraft/client/resources/data/PackMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/PackMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/PackMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/PackMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/data/TextureMetadataSection.java b/src/game/java/net/minecraft/client/resources/data/TextureMetadataSection.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/TextureMetadataSection.java rename to src/game/java/net/minecraft/client/resources/data/TextureMetadataSection.java diff --git a/src/main/java/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.java b/src/game/java/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.java rename to src/game/java/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.java diff --git a/src/main/java/net/minecraft/client/resources/model/BuiltInModel.java b/src/game/java/net/minecraft/client/resources/model/BuiltInModel.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/BuiltInModel.java rename to src/game/java/net/minecraft/client/resources/model/BuiltInModel.java diff --git a/src/main/java/net/minecraft/client/resources/model/IBakedModel.java b/src/game/java/net/minecraft/client/resources/model/IBakedModel.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/IBakedModel.java rename to src/game/java/net/minecraft/client/resources/model/IBakedModel.java diff --git a/src/main/java/net/minecraft/client/resources/model/ModelBakery.java b/src/game/java/net/minecraft/client/resources/model/ModelBakery.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/ModelBakery.java rename to src/game/java/net/minecraft/client/resources/model/ModelBakery.java diff --git a/src/main/java/net/minecraft/client/resources/model/ModelManager.java b/src/game/java/net/minecraft/client/resources/model/ModelManager.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/ModelManager.java rename to src/game/java/net/minecraft/client/resources/model/ModelManager.java diff --git a/src/main/java/net/minecraft/client/resources/model/ModelResourceLocation.java b/src/game/java/net/minecraft/client/resources/model/ModelResourceLocation.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/ModelResourceLocation.java rename to src/game/java/net/minecraft/client/resources/model/ModelResourceLocation.java diff --git a/src/main/java/net/minecraft/client/resources/model/ModelRotation.java b/src/game/java/net/minecraft/client/resources/model/ModelRotation.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/ModelRotation.java rename to src/game/java/net/minecraft/client/resources/model/ModelRotation.java diff --git a/src/main/java/net/minecraft/client/resources/model/SimpleBakedModel.java b/src/game/java/net/minecraft/client/resources/model/SimpleBakedModel.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/SimpleBakedModel.java rename to src/game/java/net/minecraft/client/resources/model/SimpleBakedModel.java diff --git a/src/main/java/net/minecraft/client/resources/model/WeightedBakedModel.java b/src/game/java/net/minecraft/client/resources/model/WeightedBakedModel.java similarity index 100% rename from src/main/java/net/minecraft/client/resources/model/WeightedBakedModel.java rename to src/game/java/net/minecraft/client/resources/model/WeightedBakedModel.java diff --git a/src/main/java/net/minecraft/client/settings/GameSettings.java b/src/game/java/net/minecraft/client/settings/GameSettings.java similarity index 87% rename from src/main/java/net/minecraft/client/settings/GameSettings.java rename to src/game/java/net/minecraft/client/settings/GameSettings.java index 8429fb4..e5a5755 100644 --- a/src/main/java/net/minecraft/client/settings/GameSettings.java +++ b/src/game/java/net/minecraft/client/settings/GameSettings.java @@ -25,12 +25,16 @@ import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.EaglerDeferredConfig; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.EaglerDeferredPipeline; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.dynamiclights.DynamicLightsStateManager; +import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec; +import net.lax1dude.eaglercraft.v1_8.recording.ScreenRecordingController; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.SoundCategory; import net.minecraft.client.gui.GuiNewChat; @@ -218,12 +222,24 @@ public class GameSettings { public boolean enableUpdateSvc = true; public boolean enableFNAWSkins = false; public boolean enableDynamicLights = false; + public boolean hasHiddenPhishWarning = false; + public boolean enableProfanityFilter = false; + public boolean hasShownProfanityFilter = false; + public float touchControlOpacity = 1.0f; public int voiceListenRadius = 16; public float voiceListenVolume = 0.5f; public float voiceSpeakVolume = 0.5f; public int voicePTTKey = 47; // V + public EnumScreenRecordingCodec screenRecordCodec; + public int screenRecordFPS = ScreenRecordingController.DEFAULT_FPS; + public int screenRecordResolution = ScreenRecordingController.DEFAULT_RESOLUTION; + public int screenRecordAudioBitrate = ScreenRecordingController.DEFAULT_AUDIO_BITRATE; + public int screenRecordVideoBitrate = ScreenRecordingController.DEFAULT_VIDEO_BITRATE; + public float screenRecordGameVolume = ScreenRecordingController.DEFAULT_GAME_VOLUME; + public float screenRecordMicVolume = ScreenRecordingController.DEFAULT_MIC_VOLUME; + public boolean hidePassword = true; private boolean shouldReloadPage = false; @@ -252,9 +268,8 @@ public class GameSettings { this.language = EagRuntime.getConfiguration().getDefaultLocale(); this.forceUnicodeFont = false; this.mc = mcIn; - GameSettings.Options.RENDER_DISTANCE.setValueMax(18.0F); - this.renderDistanceChunks = 4; + this.screenRecordCodec = ScreenRecordingController.getDefaultCodec(); this.loadOptions(); } @@ -276,7 +291,8 @@ public class GameSettings { */ public static boolean isKeyDown(KeyBinding parKeyBinding) { return parKeyBinding.getKeyCode() == 0 ? false - : (parKeyBinding.getKeyCode() < 0 ? Mouse.isButtonDown(parKeyBinding.getKeyCode() + 100) + : (parKeyBinding.getKeyCode() < 0 + ? PointerInputAbstraction.getVCursorButtonDown(parKeyBinding.getKeyCode() + 100) : Keyboard.isKeyDown(parKeyBinding.getKeyCode())); } @@ -361,6 +377,10 @@ public class GameSettings { this.renderDistanceChunks = (int) parFloat1; this.mc.renderGlobal.setDisplayListEntitiesDirty(); } + + if (parOptions == GameSettings.Options.EAGLER_TOUCH_CONTROL_OPACITY) { + this.touchControlOpacity = parFloat1; + } } /** @@ -449,10 +469,6 @@ public class GameSettings { this.snooperEnabled = !this.snooperEnabled; } - if (parOptions == GameSettings.Options.TOUCHSCREEN) { - this.touchscreen = !this.touchscreen; - } - if (parOptions == GameSettings.Options.BLOCK_ALTERNATIVES) { this.allowBlockAlternatives = !this.allowBlockAlternatives; this.mc.renderGlobal.loadRenderers(); @@ -520,6 +536,10 @@ public class GameSettings { this.mc.toggleFullscreen(); } + if (parOptions == GameSettings.Options.EAGLER_PROFANITY_FILTER) { + this.enableProfanityFilter = !this.enableProfanityFilter; + } + this.saveOptions(); } @@ -544,69 +564,71 @@ public class GameSettings { ? (float) this.mipmapLevels : (parOptions == GameSettings.Options.RENDER_DISTANCE ? (float) this.renderDistanceChunks - : 0.0F))))))))))); + : (parOptions == GameSettings.Options.EAGLER_TOUCH_CONTROL_OPACITY + ? this.touchControlOpacity + : 0.0F)))))))))))); } public boolean getOptionOrdinalValue(GameSettings.Options parOptions) { switch (parOptions) { - case DISABLE_ALPHA: - return this.disableAlpha; - case ENABLE_SOUND: - return this.enableSound; - case HIDE_PASSWORD: - return this.hidePassword; - case FNAW_SKINS: - return this.enableFNAWSkins; - case EAGLER_VSYNC: - return this.enableVsync; - case EAGLER_DYNAMIC_LIGHTS: - return this.enableDynamicLights; - case INVERT_MOUSE: - return this.invertMouse; - case VIEW_BOBBING: - return this.viewBobbing; - case ANAGLYPH: - return this.anaglyph; - case FBO_ENABLE: - return this.fboEnable; - case CHAT_COLOR: - return this.chatColours; - case CHAT_LINKS: - return this.chatLinks; - case CHAT_LINKS_PROMPT: - return this.chatLinksPrompt; - case SNOOPER_ENABLED: - return this.snooperEnabled; - case TOUCHSCREEN: - return this.touchscreen; - case FORCE_UNICODE_FONT: - return this.forceUnicodeFont; - case BLOCK_ALTERNATIVES: - return this.allowBlockAlternatives; - case REDUCED_DEBUG_INFO: - return this.reducedDebugInfo; - case ENTITY_SHADOWS: - return this.field_181151_V; - case HUD_COORDS: - return this.hudCoords; - case HUD_FPS: - return this.hudFps; - case HUD_PLAYER: - return this.hudPlayer; - case HUD_STATS: - return this.hudStats; - case HUD_WORLD: - return this.hudWorld; - case HUD_24H: - return this.hud24h; - case CHUNK_FIX: - return this.chunkFix; - case FOG: - return this.fog; - case FULLSCREEN: - return this.mc.isFullScreen(); - default: - return false; + case INVERT_MOUSE: + return this.invertMouse; + case VIEW_BOBBING: + return this.viewBobbing; + case ANAGLYPH: + return this.anaglyph; + case FBO_ENABLE: + return this.fboEnable; + case CHAT_COLOR: + return this.chatColours; + case CHAT_LINKS: + return this.chatLinks; + case CHAT_LINKS_PROMPT: + return this.chatLinksPrompt; + case SNOOPER_ENABLED: + return this.snooperEnabled; + case FORCE_UNICODE_FONT: + return this.forceUnicodeFont; + case BLOCK_ALTERNATIVES: + return this.allowBlockAlternatives; + case REDUCED_DEBUG_INFO: + return this.reducedDebugInfo; + case ENTITY_SHADOWS: + return this.field_181151_V; + case HUD_COORDS: + return this.hudCoords; + case HUD_FPS: + return this.hudFps; + case HUD_PLAYER: + return this.hudPlayer; + case HUD_STATS: + return this.hudStats; + case HUD_WORLD: + return this.hudWorld; + case HUD_24H: + return this.hud24h; + case CHUNK_FIX: + return this.chunkFix; + case FOG: + return this.fog; + case FULLSCREEN: + return this.mc.isFullScreen(); + case FNAW_SKINS: + return this.enableFNAWSkins; + case EAGLER_VSYNC: + return this.enableVsync; + case EAGLER_DYNAMIC_LIGHTS: + return this.enableDynamicLights; + case EAGLER_PROFANITY_FILTER: + return this.enableProfanityFilter; + case DISABLE_ALPHA: + return this.disableAlpha; + case ENABLE_SOUND: + return this.enableSound; + case HIDE_PASSWORD: + return this.hidePassword; + default: + return false; } } @@ -696,7 +718,11 @@ public class GameSettings { : s + (int) (f * 100.0F) + "%") - : "yee")))))))))))); + : (parOptions == GameSettings.Options.EAGLER_TOUCH_CONTROL_OPACITY + ? (s + (int) (f + * 100.0F) + + "%") + : "yee"))))))))))))); } else if (parOptions.getEnumBoolean()) { boolean flag = this.getOptionOrdinalValue(parOptions); if (parOptions == GameSettings.Options.ENABLE_SOUND) { @@ -947,10 +973,6 @@ public class GameSettings { this.pauseOnLostFocus = astring[1].equals("true"); } - if (astring[0].equals("touchscreen")) { - this.touchscreen = astring[1].equals("true"); - } - if (astring[0].equals("overrideHeight")) { this.overrideHeight = Integer.parseInt(astring[1]); } @@ -1094,6 +1116,58 @@ public class GameSettings { this.enableDynamicLights = astring[1].equals("true"); } + if (astring[0].equals("hasHiddenPhishWarning")) { + this.hasHiddenPhishWarning = astring[1].equals("true"); + } + + if (astring[0].equals("enableProfanityFilter")) { + this.enableProfanityFilter = astring[1].equals("true"); + } + + if (astring[0].equals("hasShownProfanityFilter")) { + this.hasShownProfanityFilter = astring[1].equals("true"); + } + + if (astring[0].equals("screenRecordCodec")) { + EnumScreenRecordingCodec codec = EnumScreenRecordingCodec.valueOf(astring[1]); + if (!ScreenRecordingController.codecs.contains(codec)) { + throw new IllegalStateException("Selected codec is not supported: " + codec.name); + } + screenRecordCodec = codec; + } + + if (astring[0].equals("screenRecordFPS")) { + screenRecordFPS = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("screenRecordFPS")) { + screenRecordFPS = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("screenRecordResolution")) { + screenRecordResolution = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("screenRecordAudioBitrate")) { + screenRecordAudioBitrate = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("screenRecordVideoBitrate")) { + screenRecordVideoBitrate = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("screenRecordGameVolume")) { + screenRecordGameVolume = parseFloat(astring[1]); + } + + if (astring[0].equals("screenRecordMicVolume")) { + screenRecordMicVolume = parseFloat(astring[1]); + } + + if (astring[0].equals("touchControlOpacity")) { + touchControlOpacity = parseFloat(astring[1]); + } + deferredShaderConf.readOption(astring[0], astring[1]); } catch (Exception var8) { logger.warn("Skipping bad option: " + s); @@ -1106,8 +1180,18 @@ public class GameSettings { VoiceClientController.setVoiceListenVolume(voiceListenVolume); VoiceClientController.setVoiceSpeakVolume(voiceSpeakVolume); VoiceClientController.setVoiceProximity(voiceListenRadius); + ScreenRecordingController.setGameVolume(screenRecordGameVolume); + ScreenRecordingController.setMicrophoneVolume(screenRecordMicVolume); if (this.mc.getRenderManager() != null) this.mc.getRenderManager().setEnableFNAWSkins(this.enableFNAWSkins); + if (this.shaders && !EaglerDeferredPipeline.isSupported()) { + logger.error("Setting shaders to false because they are not supported"); + this.shaders = false; + } + if (this.enableDynamicLights && !DynamicLightsStateManager.isSupported()) { + logger.error("Setting dynamic lights to false because they are not supported"); + this.enableDynamicLights = false; + } } catch (Exception exception) { logger.error("Failed to load options"); logger.error(exception); @@ -1187,7 +1271,6 @@ public class GameSettings { printwriter.println("hideServerAddress:" + this.hideServerAddress); printwriter.println("advancedItemTooltips:" + this.advancedItemTooltips); printwriter.println("pauseOnLostFocus:" + this.pauseOnLostFocus); - printwriter.println("touchscreen:" + this.touchscreen); printwriter.println("overrideWidth:" + this.overrideWidth); printwriter.println("overrideHeight:" + this.overrideHeight); printwriter.println("heldItemTooltips:" + this.heldItemTooltips); @@ -1219,6 +1302,19 @@ public class GameSettings { printwriter.println("voicePTTKey:" + this.voicePTTKey); printwriter.println("enableFNAWSkins:" + this.enableFNAWSkins); printwriter.println("enableDynamicLights:" + this.enableDynamicLights); + printwriter.println("hasHiddenPhishWarning:" + this.hasHiddenPhishWarning); + printwriter.println("enableProfanityFilter:" + this.enableProfanityFilter); + printwriter.println("hasShownProfanityFilter:" + this.hasShownProfanityFilter); + if (screenRecordCodec != null) { + printwriter.println("screenRecordCodec:" + this.screenRecordCodec); + } + printwriter.println("screenRecordFPS:" + this.screenRecordFPS); + printwriter.println("screenRecordResolution:" + this.screenRecordResolution); + printwriter.println("screenRecordAudioBitrate:" + this.screenRecordAudioBitrate); + printwriter.println("screenRecordVideoBitrate:" + this.screenRecordVideoBitrate); + printwriter.println("screenRecordGameVolume:" + this.screenRecordGameVolume); + printwriter.println("screenRecordMicVolume:" + this.screenRecordMicVolume); + printwriter.println("touchControlOpacity:" + this.touchControlOpacity); for (KeyBinding keybinding : this.keyBindings) { printwriter.println("key_" + keybinding.getKeyDescription() + ":" + keybinding.getKeyCode()); @@ -1255,7 +1351,7 @@ public class GameSettings { public float getSoundLevel(SoundCategory parSoundCategory) { return this.mapSoundLevels.containsKey(parSoundCategory) ? ((Float) this.mapSoundLevels.get(parSoundCategory)).floatValue() - : (parSoundCategory == SoundCategory.VOICE ? 0.0F : 1.0F); + : 1.0F; } public void setSoundLevel(SoundCategory parSoundCategory, float parFloat1) { @@ -1339,8 +1435,8 @@ public class GameSettings { CHAT_VISIBILITY("options.chat.visibility", false, false), CHAT_COLOR("options.chat.color", false, true), CHAT_LINKS("options.chat.links", false, true), CHAT_OPACITY("options.chat.opacity", true, false), CHAT_LINKS_PROMPT("options.chat.links.prompt", false, true), SNOOPER_ENABLED("options.snooper", false, true), - TOUCHSCREEN("options.touchscreen", false, true), CHAT_SCALE("options.chat.scale", true, false), - CHAT_WIDTH("options.chat.width", true, false), CHAT_HEIGHT_FOCUSED("options.chat.height.focused", true, false), + CHAT_SCALE("options.chat.scale", true, false), CHAT_WIDTH("options.chat.width", true, false), + CHAT_HEIGHT_FOCUSED("options.chat.height.focused", true, false), CHAT_HEIGHT_UNFOCUSED("options.chat.height.unfocused", true, false), MIPMAP_LEVELS("options.mipmapLevels", true, false, 0.0F, 4.0F, 1.0F), FORCE_UNICODE_FONT("options.forceUnicodeFont", false, true), @@ -1362,7 +1458,9 @@ public class GameSettings { FOG("options.fog", false, true), FXAA("options.fxaa", false, false), FULLSCREEN("options.fullscreen", false, true), FNAW_SKINS("options.skinCustomisation.enableFNAWSkins", false, true), - EAGLER_VSYNC("options.vsync", false, true), EAGLER_DYNAMIC_LIGHTS("options.dynamicLights", false, true); + EAGLER_VSYNC("options.vsync", false, true), EAGLER_DYNAMIC_LIGHTS("options.dynamicLights", false, true), + EAGLER_PROFANITY_FILTER("options.profanityFilterButton", false, true), + EAGLER_TOUCH_CONTROL_OPACITY("options.touchControlOpacity", true, false); private final boolean enumFloat; private final boolean enumBoolean; diff --git a/src/main/java/net/minecraft/client/settings/KeyBinding.java b/src/game/java/net/minecraft/client/settings/KeyBinding.java similarity index 100% rename from src/main/java/net/minecraft/client/settings/KeyBinding.java rename to src/game/java/net/minecraft/client/settings/KeyBinding.java diff --git a/src/main/java/net/minecraft/client/stream/IStream.java b/src/game/java/net/minecraft/client/stream/IStream.java similarity index 100% rename from src/main/java/net/minecraft/client/stream/IStream.java rename to src/game/java/net/minecraft/client/stream/IStream.java diff --git a/src/main/java/net/minecraft/client/util/JsonBlendingMode.java b/src/game/java/net/minecraft/client/util/JsonBlendingMode.java similarity index 100% rename from src/main/java/net/minecraft/client/util/JsonBlendingMode.java rename to src/game/java/net/minecraft/client/util/JsonBlendingMode.java diff --git a/src/main/java/net/minecraft/client/util/JsonException.java b/src/game/java/net/minecraft/client/util/JsonException.java similarity index 100% rename from src/main/java/net/minecraft/client/util/JsonException.java rename to src/game/java/net/minecraft/client/util/JsonException.java diff --git a/src/main/java/net/minecraft/command/CommandBase.java b/src/game/java/net/minecraft/command/CommandBase.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandBase.java rename to src/game/java/net/minecraft/command/CommandBase.java diff --git a/src/main/java/net/minecraft/command/CommandBlockData.java b/src/game/java/net/minecraft/command/CommandBlockData.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandBlockData.java rename to src/game/java/net/minecraft/command/CommandBlockData.java diff --git a/src/main/java/net/minecraft/command/CommandClearInventory.java b/src/game/java/net/minecraft/command/CommandClearInventory.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandClearInventory.java rename to src/game/java/net/minecraft/command/CommandClearInventory.java diff --git a/src/main/java/net/minecraft/command/CommandClone.java b/src/game/java/net/minecraft/command/CommandClone.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandClone.java rename to src/game/java/net/minecraft/command/CommandClone.java diff --git a/src/main/java/net/minecraft/command/CommandCompare.java b/src/game/java/net/minecraft/command/CommandCompare.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandCompare.java rename to src/game/java/net/minecraft/command/CommandCompare.java diff --git a/src/main/java/net/minecraft/command/CommandDefaultGameMode.java b/src/game/java/net/minecraft/command/CommandDefaultGameMode.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandDefaultGameMode.java rename to src/game/java/net/minecraft/command/CommandDefaultGameMode.java diff --git a/src/main/java/net/minecraft/command/CommandDifficulty.java b/src/game/java/net/minecraft/command/CommandDifficulty.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandDifficulty.java rename to src/game/java/net/minecraft/command/CommandDifficulty.java diff --git a/src/main/java/net/minecraft/command/CommandEffect.java b/src/game/java/net/minecraft/command/CommandEffect.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandEffect.java rename to src/game/java/net/minecraft/command/CommandEffect.java diff --git a/src/main/java/net/minecraft/command/CommandEnchant.java b/src/game/java/net/minecraft/command/CommandEnchant.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandEnchant.java rename to src/game/java/net/minecraft/command/CommandEnchant.java diff --git a/src/main/java/net/minecraft/command/CommandEntityData.java b/src/game/java/net/minecraft/command/CommandEntityData.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandEntityData.java rename to src/game/java/net/minecraft/command/CommandEntityData.java diff --git a/src/main/java/net/minecraft/command/CommandException.java b/src/game/java/net/minecraft/command/CommandException.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandException.java rename to src/game/java/net/minecraft/command/CommandException.java diff --git a/src/main/java/net/minecraft/command/CommandExecuteAt.java b/src/game/java/net/minecraft/command/CommandExecuteAt.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandExecuteAt.java rename to src/game/java/net/minecraft/command/CommandExecuteAt.java diff --git a/src/main/java/net/minecraft/command/CommandFill.java b/src/game/java/net/minecraft/command/CommandFill.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandFill.java rename to src/game/java/net/minecraft/command/CommandFill.java diff --git a/src/main/java/net/minecraft/command/CommandGameMode.java b/src/game/java/net/minecraft/command/CommandGameMode.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandGameMode.java rename to src/game/java/net/minecraft/command/CommandGameMode.java diff --git a/src/main/java/net/minecraft/command/CommandGameRule.java b/src/game/java/net/minecraft/command/CommandGameRule.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandGameRule.java rename to src/game/java/net/minecraft/command/CommandGameRule.java diff --git a/src/main/java/net/minecraft/command/CommandGive.java b/src/game/java/net/minecraft/command/CommandGive.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandGive.java rename to src/game/java/net/minecraft/command/CommandGive.java diff --git a/src/main/java/net/minecraft/command/CommandHandler.java b/src/game/java/net/minecraft/command/CommandHandler.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandHandler.java rename to src/game/java/net/minecraft/command/CommandHandler.java diff --git a/src/main/java/net/minecraft/command/CommandHelp.java b/src/game/java/net/minecraft/command/CommandHelp.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandHelp.java rename to src/game/java/net/minecraft/command/CommandHelp.java diff --git a/src/main/java/net/minecraft/command/CommandKill.java b/src/game/java/net/minecraft/command/CommandKill.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandKill.java rename to src/game/java/net/minecraft/command/CommandKill.java diff --git a/src/main/java/net/minecraft/command/CommandNotFoundException.java b/src/game/java/net/minecraft/command/CommandNotFoundException.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandNotFoundException.java rename to src/game/java/net/minecraft/command/CommandNotFoundException.java diff --git a/src/main/java/net/minecraft/command/CommandParticle.java b/src/game/java/net/minecraft/command/CommandParticle.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandParticle.java rename to src/game/java/net/minecraft/command/CommandParticle.java diff --git a/src/main/java/net/minecraft/command/CommandPlaySound.java b/src/game/java/net/minecraft/command/CommandPlaySound.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandPlaySound.java rename to src/game/java/net/minecraft/command/CommandPlaySound.java diff --git a/src/main/java/net/minecraft/command/CommandReplaceItem.java b/src/game/java/net/minecraft/command/CommandReplaceItem.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandReplaceItem.java rename to src/game/java/net/minecraft/command/CommandReplaceItem.java diff --git a/src/main/java/net/minecraft/command/CommandResultStats.java b/src/game/java/net/minecraft/command/CommandResultStats.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandResultStats.java rename to src/game/java/net/minecraft/command/CommandResultStats.java diff --git a/src/main/java/net/minecraft/command/CommandServerKick.java b/src/game/java/net/minecraft/command/CommandServerKick.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandServerKick.java rename to src/game/java/net/minecraft/command/CommandServerKick.java diff --git a/src/main/java/net/minecraft/command/CommandSetPlayerTimeout.java b/src/game/java/net/minecraft/command/CommandSetPlayerTimeout.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandSetPlayerTimeout.java rename to src/game/java/net/minecraft/command/CommandSetPlayerTimeout.java diff --git a/src/main/java/net/minecraft/command/CommandSetSpawnpoint.java b/src/game/java/net/minecraft/command/CommandSetSpawnpoint.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandSetSpawnpoint.java rename to src/game/java/net/minecraft/command/CommandSetSpawnpoint.java diff --git a/src/main/java/net/minecraft/command/CommandShowSeed.java b/src/game/java/net/minecraft/command/CommandShowSeed.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandShowSeed.java rename to src/game/java/net/minecraft/command/CommandShowSeed.java diff --git a/src/main/java/net/minecraft/command/CommandSpreadPlayers.java b/src/game/java/net/minecraft/command/CommandSpreadPlayers.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandSpreadPlayers.java rename to src/game/java/net/minecraft/command/CommandSpreadPlayers.java diff --git a/src/main/java/net/minecraft/command/CommandStats.java b/src/game/java/net/minecraft/command/CommandStats.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandStats.java rename to src/game/java/net/minecraft/command/CommandStats.java diff --git a/src/main/java/net/minecraft/command/CommandTime.java b/src/game/java/net/minecraft/command/CommandTime.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandTime.java rename to src/game/java/net/minecraft/command/CommandTime.java diff --git a/src/main/java/net/minecraft/command/CommandTitle.java b/src/game/java/net/minecraft/command/CommandTitle.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandTitle.java rename to src/game/java/net/minecraft/command/CommandTitle.java diff --git a/src/main/java/net/minecraft/command/CommandToggleDownfall.java b/src/game/java/net/minecraft/command/CommandToggleDownfall.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandToggleDownfall.java rename to src/game/java/net/minecraft/command/CommandToggleDownfall.java diff --git a/src/main/java/net/minecraft/command/CommandTrigger.java b/src/game/java/net/minecraft/command/CommandTrigger.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandTrigger.java rename to src/game/java/net/minecraft/command/CommandTrigger.java diff --git a/src/main/java/net/minecraft/command/CommandWeather.java b/src/game/java/net/minecraft/command/CommandWeather.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandWeather.java rename to src/game/java/net/minecraft/command/CommandWeather.java diff --git a/src/main/java/net/minecraft/command/CommandWorldBorder.java b/src/game/java/net/minecraft/command/CommandWorldBorder.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandWorldBorder.java rename to src/game/java/net/minecraft/command/CommandWorldBorder.java diff --git a/src/main/java/net/minecraft/command/CommandXP.java b/src/game/java/net/minecraft/command/CommandXP.java similarity index 100% rename from src/main/java/net/minecraft/command/CommandXP.java rename to src/game/java/net/minecraft/command/CommandXP.java diff --git a/src/main/java/net/minecraft/command/EntityNotFoundException.java b/src/game/java/net/minecraft/command/EntityNotFoundException.java similarity index 100% rename from src/main/java/net/minecraft/command/EntityNotFoundException.java rename to src/game/java/net/minecraft/command/EntityNotFoundException.java diff --git a/src/main/java/net/minecraft/command/IAdminCommand.java b/src/game/java/net/minecraft/command/IAdminCommand.java similarity index 100% rename from src/main/java/net/minecraft/command/IAdminCommand.java rename to src/game/java/net/minecraft/command/IAdminCommand.java diff --git a/src/main/java/net/minecraft/command/ICommand.java b/src/game/java/net/minecraft/command/ICommand.java similarity index 100% rename from src/main/java/net/minecraft/command/ICommand.java rename to src/game/java/net/minecraft/command/ICommand.java diff --git a/src/main/java/net/minecraft/command/ICommandManager.java b/src/game/java/net/minecraft/command/ICommandManager.java similarity index 100% rename from src/main/java/net/minecraft/command/ICommandManager.java rename to src/game/java/net/minecraft/command/ICommandManager.java diff --git a/src/main/java/net/minecraft/command/ICommandSender.java b/src/game/java/net/minecraft/command/ICommandSender.java similarity index 100% rename from src/main/java/net/minecraft/command/ICommandSender.java rename to src/game/java/net/minecraft/command/ICommandSender.java diff --git a/src/main/java/net/minecraft/command/NumberInvalidException.java b/src/game/java/net/minecraft/command/NumberInvalidException.java similarity index 100% rename from src/main/java/net/minecraft/command/NumberInvalidException.java rename to src/game/java/net/minecraft/command/NumberInvalidException.java diff --git a/src/main/java/net/minecraft/command/PlayerNotFoundException.java b/src/game/java/net/minecraft/command/PlayerNotFoundException.java similarity index 100% rename from src/main/java/net/minecraft/command/PlayerNotFoundException.java rename to src/game/java/net/minecraft/command/PlayerNotFoundException.java diff --git a/src/main/java/net/minecraft/command/PlayerSelector.java b/src/game/java/net/minecraft/command/PlayerSelector.java similarity index 100% rename from src/main/java/net/minecraft/command/PlayerSelector.java rename to src/game/java/net/minecraft/command/PlayerSelector.java diff --git a/src/main/java/net/minecraft/command/ServerCommandManager.java b/src/game/java/net/minecraft/command/ServerCommandManager.java similarity index 100% rename from src/main/java/net/minecraft/command/ServerCommandManager.java rename to src/game/java/net/minecraft/command/ServerCommandManager.java diff --git a/src/main/java/net/minecraft/command/SyntaxErrorException.java b/src/game/java/net/minecraft/command/SyntaxErrorException.java similarity index 100% rename from src/main/java/net/minecraft/command/SyntaxErrorException.java rename to src/game/java/net/minecraft/command/SyntaxErrorException.java diff --git a/src/main/java/net/minecraft/command/WrongUsageException.java b/src/game/java/net/minecraft/command/WrongUsageException.java similarity index 100% rename from src/main/java/net/minecraft/command/WrongUsageException.java rename to src/game/java/net/minecraft/command/WrongUsageException.java diff --git a/src/main/java/net/minecraft/command/server/CommandAchievement.java b/src/game/java/net/minecraft/command/server/CommandAchievement.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandAchievement.java rename to src/game/java/net/minecraft/command/server/CommandAchievement.java diff --git a/src/main/java/net/minecraft/command/server/CommandBlockLogic.java b/src/game/java/net/minecraft/command/server/CommandBlockLogic.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandBlockLogic.java rename to src/game/java/net/minecraft/command/server/CommandBlockLogic.java diff --git a/src/main/java/net/minecraft/command/server/CommandBroadcast.java b/src/game/java/net/minecraft/command/server/CommandBroadcast.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandBroadcast.java rename to src/game/java/net/minecraft/command/server/CommandBroadcast.java diff --git a/src/main/java/net/minecraft/command/server/CommandEmote.java b/src/game/java/net/minecraft/command/server/CommandEmote.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandEmote.java rename to src/game/java/net/minecraft/command/server/CommandEmote.java diff --git a/src/main/java/net/minecraft/command/server/CommandListPlayers.java b/src/game/java/net/minecraft/command/server/CommandListPlayers.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandListPlayers.java rename to src/game/java/net/minecraft/command/server/CommandListPlayers.java diff --git a/src/main/java/net/minecraft/command/server/CommandMessage.java b/src/game/java/net/minecraft/command/server/CommandMessage.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandMessage.java rename to src/game/java/net/minecraft/command/server/CommandMessage.java diff --git a/src/main/java/net/minecraft/command/server/CommandMessageRaw.java b/src/game/java/net/minecraft/command/server/CommandMessageRaw.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandMessageRaw.java rename to src/game/java/net/minecraft/command/server/CommandMessageRaw.java diff --git a/src/main/java/net/minecraft/command/server/CommandScoreboard.java b/src/game/java/net/minecraft/command/server/CommandScoreboard.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandScoreboard.java rename to src/game/java/net/minecraft/command/server/CommandScoreboard.java diff --git a/src/main/java/net/minecraft/command/server/CommandSetBlock.java b/src/game/java/net/minecraft/command/server/CommandSetBlock.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandSetBlock.java rename to src/game/java/net/minecraft/command/server/CommandSetBlock.java diff --git a/src/main/java/net/minecraft/command/server/CommandSetDefaultSpawnpoint.java b/src/game/java/net/minecraft/command/server/CommandSetDefaultSpawnpoint.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandSetDefaultSpawnpoint.java rename to src/game/java/net/minecraft/command/server/CommandSetDefaultSpawnpoint.java diff --git a/src/main/java/net/minecraft/command/server/CommandSummon.java b/src/game/java/net/minecraft/command/server/CommandSummon.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandSummon.java rename to src/game/java/net/minecraft/command/server/CommandSummon.java diff --git a/src/main/java/net/minecraft/command/server/CommandTeleport.java b/src/game/java/net/minecraft/command/server/CommandTeleport.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandTeleport.java rename to src/game/java/net/minecraft/command/server/CommandTeleport.java diff --git a/src/main/java/net/minecraft/command/server/CommandTestFor.java b/src/game/java/net/minecraft/command/server/CommandTestFor.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandTestFor.java rename to src/game/java/net/minecraft/command/server/CommandTestFor.java diff --git a/src/main/java/net/minecraft/command/server/CommandTestForBlock.java b/src/game/java/net/minecraft/command/server/CommandTestForBlock.java similarity index 100% rename from src/main/java/net/minecraft/command/server/CommandTestForBlock.java rename to src/game/java/net/minecraft/command/server/CommandTestForBlock.java diff --git a/src/main/java/net/minecraft/crash/CrashReport.java b/src/game/java/net/minecraft/crash/CrashReport.java similarity index 100% rename from src/main/java/net/minecraft/crash/CrashReport.java rename to src/game/java/net/minecraft/crash/CrashReport.java diff --git a/src/main/java/net/minecraft/crash/CrashReportCategory.java b/src/game/java/net/minecraft/crash/CrashReportCategory.java similarity index 100% rename from src/main/java/net/minecraft/crash/CrashReportCategory.java rename to src/game/java/net/minecraft/crash/CrashReportCategory.java diff --git a/src/main/java/net/minecraft/creativetab/CreativeTabs.java b/src/game/java/net/minecraft/creativetab/CreativeTabs.java similarity index 100% rename from src/main/java/net/minecraft/creativetab/CreativeTabs.java rename to src/game/java/net/minecraft/creativetab/CreativeTabs.java diff --git a/src/main/java/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java b/src/game/java/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java rename to src/game/java/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java diff --git a/src/main/java/net/minecraft/dispenser/BehaviorProjectileDispense.java b/src/game/java/net/minecraft/dispenser/BehaviorProjectileDispense.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/BehaviorProjectileDispense.java rename to src/game/java/net/minecraft/dispenser/BehaviorProjectileDispense.java diff --git a/src/main/java/net/minecraft/dispenser/IBehaviorDispenseItem.java b/src/game/java/net/minecraft/dispenser/IBehaviorDispenseItem.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/IBehaviorDispenseItem.java rename to src/game/java/net/minecraft/dispenser/IBehaviorDispenseItem.java diff --git a/src/main/java/net/minecraft/dispenser/IBlockSource.java b/src/game/java/net/minecraft/dispenser/IBlockSource.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/IBlockSource.java rename to src/game/java/net/minecraft/dispenser/IBlockSource.java diff --git a/src/main/java/net/minecraft/dispenser/ILocatableSource.java b/src/game/java/net/minecraft/dispenser/ILocatableSource.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/ILocatableSource.java rename to src/game/java/net/minecraft/dispenser/ILocatableSource.java diff --git a/src/main/java/net/minecraft/dispenser/ILocation.java b/src/game/java/net/minecraft/dispenser/ILocation.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/ILocation.java rename to src/game/java/net/minecraft/dispenser/ILocation.java diff --git a/src/main/java/net/minecraft/dispenser/IPosition.java b/src/game/java/net/minecraft/dispenser/IPosition.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/IPosition.java rename to src/game/java/net/minecraft/dispenser/IPosition.java diff --git a/src/main/java/net/minecraft/dispenser/PositionImpl.java b/src/game/java/net/minecraft/dispenser/PositionImpl.java similarity index 100% rename from src/main/java/net/minecraft/dispenser/PositionImpl.java rename to src/game/java/net/minecraft/dispenser/PositionImpl.java diff --git a/src/main/java/net/minecraft/enchantment/Enchantment.java b/src/game/java/net/minecraft/enchantment/Enchantment.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/Enchantment.java rename to src/game/java/net/minecraft/enchantment/Enchantment.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentArrowDamage.java b/src/game/java/net/minecraft/enchantment/EnchantmentArrowDamage.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentArrowDamage.java rename to src/game/java/net/minecraft/enchantment/EnchantmentArrowDamage.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentArrowFire.java b/src/game/java/net/minecraft/enchantment/EnchantmentArrowFire.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentArrowFire.java rename to src/game/java/net/minecraft/enchantment/EnchantmentArrowFire.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentArrowInfinite.java b/src/game/java/net/minecraft/enchantment/EnchantmentArrowInfinite.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentArrowInfinite.java rename to src/game/java/net/minecraft/enchantment/EnchantmentArrowInfinite.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentArrowKnockback.java b/src/game/java/net/minecraft/enchantment/EnchantmentArrowKnockback.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentArrowKnockback.java rename to src/game/java/net/minecraft/enchantment/EnchantmentArrowKnockback.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentDamage.java b/src/game/java/net/minecraft/enchantment/EnchantmentDamage.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentDamage.java rename to src/game/java/net/minecraft/enchantment/EnchantmentDamage.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentData.java b/src/game/java/net/minecraft/enchantment/EnchantmentData.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentData.java rename to src/game/java/net/minecraft/enchantment/EnchantmentData.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentDigging.java b/src/game/java/net/minecraft/enchantment/EnchantmentDigging.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentDigging.java rename to src/game/java/net/minecraft/enchantment/EnchantmentDigging.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentDurability.java b/src/game/java/net/minecraft/enchantment/EnchantmentDurability.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentDurability.java rename to src/game/java/net/minecraft/enchantment/EnchantmentDurability.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentFireAspect.java b/src/game/java/net/minecraft/enchantment/EnchantmentFireAspect.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentFireAspect.java rename to src/game/java/net/minecraft/enchantment/EnchantmentFireAspect.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentFishingSpeed.java b/src/game/java/net/minecraft/enchantment/EnchantmentFishingSpeed.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentFishingSpeed.java rename to src/game/java/net/minecraft/enchantment/EnchantmentFishingSpeed.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentFrostWalker.java b/src/game/java/net/minecraft/enchantment/EnchantmentFrostWalker.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentFrostWalker.java rename to src/game/java/net/minecraft/enchantment/EnchantmentFrostWalker.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentHelper.java b/src/game/java/net/minecraft/enchantment/EnchantmentHelper.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentHelper.java rename to src/game/java/net/minecraft/enchantment/EnchantmentHelper.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentKnockback.java b/src/game/java/net/minecraft/enchantment/EnchantmentKnockback.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentKnockback.java rename to src/game/java/net/minecraft/enchantment/EnchantmentKnockback.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentLootBonus.java b/src/game/java/net/minecraft/enchantment/EnchantmentLootBonus.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentLootBonus.java rename to src/game/java/net/minecraft/enchantment/EnchantmentLootBonus.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentMending.java b/src/game/java/net/minecraft/enchantment/EnchantmentMending.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentMending.java rename to src/game/java/net/minecraft/enchantment/EnchantmentMending.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentOxygen.java b/src/game/java/net/minecraft/enchantment/EnchantmentOxygen.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentOxygen.java rename to src/game/java/net/minecraft/enchantment/EnchantmentOxygen.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentProtection.java b/src/game/java/net/minecraft/enchantment/EnchantmentProtection.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentProtection.java rename to src/game/java/net/minecraft/enchantment/EnchantmentProtection.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentThorns.java b/src/game/java/net/minecraft/enchantment/EnchantmentThorns.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentThorns.java rename to src/game/java/net/minecraft/enchantment/EnchantmentThorns.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentUntouching.java b/src/game/java/net/minecraft/enchantment/EnchantmentUntouching.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentUntouching.java rename to src/game/java/net/minecraft/enchantment/EnchantmentUntouching.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentWaterWalker.java b/src/game/java/net/minecraft/enchantment/EnchantmentWaterWalker.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentWaterWalker.java rename to src/game/java/net/minecraft/enchantment/EnchantmentWaterWalker.java diff --git a/src/main/java/net/minecraft/enchantment/EnchantmentWaterWorker.java b/src/game/java/net/minecraft/enchantment/EnchantmentWaterWorker.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnchantmentWaterWorker.java rename to src/game/java/net/minecraft/enchantment/EnchantmentWaterWorker.java diff --git a/src/main/java/net/minecraft/enchantment/EnumEnchantmentType.java b/src/game/java/net/minecraft/enchantment/EnumEnchantmentType.java similarity index 100% rename from src/main/java/net/minecraft/enchantment/EnumEnchantmentType.java rename to src/game/java/net/minecraft/enchantment/EnumEnchantmentType.java diff --git a/src/main/java/net/minecraft/entity/DataWatcher.java b/src/game/java/net/minecraft/entity/DataWatcher.java similarity index 100% rename from src/main/java/net/minecraft/entity/DataWatcher.java rename to src/game/java/net/minecraft/entity/DataWatcher.java diff --git a/src/main/java/net/minecraft/entity/Entity.java b/src/game/java/net/minecraft/entity/Entity.java similarity index 50% rename from src/main/java/net/minecraft/entity/Entity.java rename to src/game/java/net/minecraft/entity/Entity.java index 5fa17a6..7096d2c 100644 --- a/src/main/java/net/minecraft/entity/Entity.java +++ b/src/game/java/net/minecraft/entity/Entity.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD:src/main/java/net/minecraft/entity/Entity.java package net.minecraft.entity; import java.util.List; @@ -2627,4 +2628,2459 @@ public abstract class Entity implements ICommandSender { } return 0.0f; } +======= +package net.minecraft.entity; + +import java.util.List; +import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DynamicLightManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.dynamiclights.DynamicLightsStateManager; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; + +import java.util.concurrent.Callable; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockFence; +import net.minecraft.block.BlockFenceGate; +import net.minecraft.block.BlockLiquid; +import net.minecraft.block.BlockWall; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.block.state.pattern.BlockPattern; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.command.CommandResultStats; +import net.minecraft.command.ICommandSender; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.EnchantmentProtection; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.event.HoverEvent; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagDouble; +import net.minecraft.nbt.NBTTagFloat; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.ReportedException; +import net.minecraft.util.StatCollector; +import net.minecraft.util.Vec3; +import net.minecraft.world.Explosion; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; + +/**+ + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class Entity implements ICommandSender { + private static final AxisAlignedBB ZERO_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); + private static int nextEntityID; + private int entityId; + public double renderDistanceWeight; + public boolean preventEntitySpawning; + public Entity riddenByEntity; + public Entity ridingEntity; + public boolean forceSpawn; + public World worldObj; + public double prevPosX; + public double prevPosY; + public double prevPosZ; + public double posX; + public double posY; + public double posZ; + public double motionX; + public double motionY; + public double motionZ; + public float rotationYaw; + public float rotationPitch; + public float prevRotationYaw; + public float prevRotationPitch; + private AxisAlignedBB boundingBox; + public boolean onGround; + public boolean isCollidedHorizontally; + public boolean isCollidedVertically; + public boolean isCollided; + public boolean velocityChanged; + protected boolean isInWeb; + private boolean isOutsideBorder; + public boolean isDead; + public float width; + public float height; + public float prevDistanceWalkedModified; + public float distanceWalkedModified; + public float distanceWalkedOnStepModified; + public float fallDistance; + private int nextStepDistance; + public double lastTickPosX; + public double lastTickPosY; + public double lastTickPosZ; + public float stepHeight; + public boolean noClip; + public float entityCollisionReduction; + protected EaglercraftRandom rand; + public int ticksExisted; + public int fireResistance; + private int fire; + protected boolean inWater; + public int hurtResistantTime; + protected boolean firstUpdate; + protected boolean isImmuneToFire; + protected DataWatcher dataWatcher; + private double entityRiderPitchDelta; + private double entityRiderYawDelta; + public boolean addedToChunk; + public int chunkCoordX; + public int chunkCoordY; + public int chunkCoordZ; + public int serverPosX; + public int serverPosY; + public int serverPosZ; + public boolean ignoreFrustumCheck; + public boolean isAirBorne; + public int timeUntilPortal; + protected boolean inPortal; + protected int portalCounter; + public int dimension; + protected BlockPos field_181016_an; + protected Vec3 field_181017_ao; + protected EnumFacing field_181018_ap; + private boolean invulnerable; + protected EaglercraftUUID entityUniqueID; + private final CommandResultStats cmdResultStats; + + public int getEntityId() { + return this.entityId; + } + + public void setEntityId(int id) { + this.entityId = id; + } + + /**+ + * Called by the /kill command. + */ + public void onKillCommand() { + this.setDead(); + } + + public Entity(World worldIn) { + this.entityId = nextEntityID++; + this.renderDistanceWeight = 1.0D; + this.boundingBox = ZERO_AABB; + this.width = 0.6F; + this.height = 1.8F; + this.nextStepDistance = 1; + this.rand = new EaglercraftRandom(); + this.fireResistance = 1; + this.firstUpdate = true; + this.entityUniqueID = MathHelper.getRandomUuid(this.rand); + this.cmdResultStats = new CommandResultStats(); + this.worldObj = worldIn; + this.setPosition(0.0D, 0.0D, 0.0D); + if (worldIn != null) { + this.dimension = worldIn.provider.getDimensionId(); + } + + this.dataWatcher = new DataWatcher(this); + this.dataWatcher.addObject(0, Byte.valueOf((byte) 0)); + this.dataWatcher.addObject(1, Short.valueOf((short) 300)); + this.dataWatcher.addObject(3, Byte.valueOf((byte) 0)); + this.dataWatcher.addObject(2, ""); + this.dataWatcher.addObject(4, Byte.valueOf((byte) 0)); + this.entityInit(); + } + + protected abstract void entityInit(); + + public DataWatcher getDataWatcher() { + return this.dataWatcher; + } + + public boolean equals(Object object) { + return object instanceof Entity ? ((Entity) object).entityId == this.entityId : false; + } + + public int hashCode() { + return this.entityId; + } + + /**+ + * Keeps moving the entity up so it isn't colliding with blocks + * and other requirements for this entity to be spawned (only + * actually used on players though its also on Entity) + */ + protected void preparePlayerToSpawn() { + if (this.worldObj != null) { + while (this.posY > 0.0D && this.posY < 256.0D) { + this.setPosition(this.posX, this.posY, this.posZ); + if (this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox()).isEmpty()) { + break; + } + + ++this.posY; + } + + this.motionX = this.motionY = this.motionZ = 0.0D; + this.rotationPitch = 0.0F; + } + } + + /**+ + * Will get destroyed next tick. + */ + public void setDead() { + this.isDead = true; + } + + /**+ + * Sets the width and height of the entity. Args: width, height + */ + protected void setSize(float f, float f1) { + if (f != this.width || f1 != this.height) { + float f2 = this.width; + this.width = f; + this.height = f1; + this.setEntityBoundingBox( + new AxisAlignedBB(this.getEntityBoundingBox().minX, this.getEntityBoundingBox().minY, + this.getEntityBoundingBox().minZ, this.getEntityBoundingBox().minX + (double) this.width, + this.getEntityBoundingBox().minY + (double) this.height, + this.getEntityBoundingBox().minZ + (double) this.width)); + if (this.width > f2 && !this.firstUpdate && !this.worldObj.isRemote) { + this.moveEntity((double) (f2 - this.width), 0.0D, (double) (f2 - this.width)); + } + } + + } + + /**+ + * Sets the rotation of the entity. Args: yaw, pitch (both in + * degrees) + */ + protected void setRotation(float yaw, float pitch) { + this.rotationYaw = yaw % 360.0F; + this.rotationPitch = pitch % 360.0F; + } + + /**+ + * Sets the x,y,z of the entity from the given parameters. Also + * seems to set up a bounding box. + */ + public void setPosition(double x, double y, double z) { + this.posX = x; + this.posY = y; + this.posZ = z; + float f = this.width / 2.0F; + float f1 = this.height; + this.setEntityBoundingBox( + new AxisAlignedBB(x - (double) f, y, z - (double) f, x + (double) f, y + (double) f1, z + (double) f)); + } + + /**+ + * Adds 15% to the entity's yaw and subtracts 15% from the + * pitch. Clamps pitch from -90 to 90. Both arguments in + * degrees. + */ + public void setAngles(float yaw, float pitch) { + float f = this.rotationPitch; + float f1 = this.rotationYaw; + this.rotationYaw = (float) ((double) this.rotationYaw + (double) yaw * 0.15D); + this.rotationPitch = (float) ((double) this.rotationPitch - (double) pitch * 0.15D); + this.rotationPitch = MathHelper.clamp_float(this.rotationPitch, -90.0F, 90.0F); + this.prevRotationPitch += this.rotationPitch - f; + this.prevRotationYaw += this.rotationYaw - f1; + } + + /**+ + * Called to update the entity's position/logic. + */ + public void onUpdate() { + this.onEntityUpdate(); + } + + /**+ + * Gets called every tick from main Entity class + */ + public void onEntityUpdate() { + if (this.ridingEntity != null && this.ridingEntity.isDead) { + this.ridingEntity = null; + } + + this.prevDistanceWalkedModified = this.distanceWalkedModified; + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + this.prevRotationPitch = this.rotationPitch; + this.prevRotationYaw = this.rotationYaw; + if (!this.worldObj.isRemote && this.worldObj instanceof WorldServer) { + MinecraftServer minecraftserver = ((WorldServer) this.worldObj).getMinecraftServer(); + int i = this.getMaxInPortalTime(); + if (this.inPortal) { + if (minecraftserver.getAllowNether()) { + if (this.ridingEntity == null && this.portalCounter++ >= i) { + this.portalCounter = i; + this.timeUntilPortal = this.getPortalCooldown(); + byte b0; + if (this.worldObj.provider.getDimensionId() == -1) { + b0 = 0; + } else { + b0 = -1; + } + + this.travelToDimension(b0); + } + + this.inPortal = false; + } + } else { + if (this.portalCounter > 0) { + this.portalCounter -= 4; + } + + if (this.portalCounter < 0) { + this.portalCounter = 0; + } + } + + if (this.timeUntilPortal > 0) { + --this.timeUntilPortal; + } + } + + this.spawnRunningParticles(); + this.handleWaterMovement(); + if (this.worldObj.isRemote) { + this.fire = 0; + } else if (this.fire > 0) { + if (this.isImmuneToFire) { + this.fire -= 4; + if (this.fire < 0) { + this.fire = 0; + } + } else { + if (this.fire % 20 == 0) { + this.attackEntityFrom(DamageSource.onFire, 1.0F); + } + + --this.fire; + } + } + + if (this.isInLava()) { + this.setOnFireFromLava(); + this.fallDistance *= 0.5F; + } + + if (this.posY < -64.0D) { + this.kill(); + } + + if (!this.worldObj.isRemote) { + this.setFlag(0, this.fire > 0); + } + + this.firstUpdate = false; + } + + /**+ + * Return the amount of time this entity should stay in a portal + * before being transported. + */ + public int getMaxInPortalTime() { + return 0; + } + + /**+ + * Called whenever the entity is walking inside of lava. + */ + protected void setOnFireFromLava() { + if (!this.isImmuneToFire) { + this.attackEntityFrom(DamageSource.lava, 4.0F); + this.setFire(15); + } + } + + /**+ + * Sets entity to burn for x amount of seconds, cannot lower + * amount of existing fire. + */ + public void setFire(int seconds) { + int i = seconds * 20; + i = EnchantmentProtection.getFireTimeForEntity(this, i); + if (this.fire < i) { + this.fire = i; + } + + } + + /**+ + * Removes fire from entity. + */ + public void extinguish() { + this.fire = 0; + } + + /**+ + * sets the dead flag. Used when you fall off the bottom of the + * world. + */ + protected void kill() { + this.setDead(); + } + + /**+ + * Checks if the offset position from the entity's current + * position is inside of liquid. Args: x, y, z + */ + public boolean isOffsetPositionInLiquid(double x, double y, double z) { + AxisAlignedBB axisalignedbb = this.getEntityBoundingBox().offset(x, y, z); + return this.isLiquidPresentInAABB(axisalignedbb); + } + + /**+ + * Determines if a liquid is present within the specified + * AxisAlignedBB. + */ + private boolean isLiquidPresentInAABB(AxisAlignedBB bb) { + return this.worldObj.getCollidingBoundingBoxes(this, bb).isEmpty() && !this.worldObj.isAnyLiquid(bb); + } + + /**+ + * Tries to moves the entity by the passed in displacement. + * Args: x, y, z + */ + public void moveEntity(double x, double y, double z) { + if (this.noClip) { + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, y, z)); + this.resetPositionToBB(); + } else { + double d0 = this.posX; + double d1 = this.posY; + double d2 = this.posZ; + if (this.isInWeb) { + this.isInWeb = false; + x *= 0.25D; + y *= 0.05000000074505806D; + z *= 0.25D; + this.motionX = 0.0D; + this.motionY = 0.0D; + this.motionZ = 0.0D; + } + + double d3 = x; + double d4 = y; + double d5 = z; + boolean flag = this.onGround && this.isSneaking() && this instanceof EntityPlayer; + if (flag) { + double d6; + for (d6 = 0.05D; x != 0.0D && this.worldObj + .getCollidingBoundingBoxes(this, this.getEntityBoundingBox().offset(x, -1.0D, 0.0D)) + .isEmpty(); d3 = x) { + if (x < d6 && x >= -d6) { + x = 0.0D; + } else if (x > 0.0D) { + x -= d6; + } else { + x += d6; + } + } + + for (; z != 0.0D && this.worldObj + .getCollidingBoundingBoxes(this, this.getEntityBoundingBox().offset(0.0D, -1.0D, z)) + .isEmpty(); d5 = z) { + if (z < d6 && z >= -d6) { + z = 0.0D; + } else if (z > 0.0D) { + z -= d6; + } else { + z += d6; + } + } + + for (; x != 0.0D && z != 0.0D + && this.worldObj + .getCollidingBoundingBoxes(this, this.getEntityBoundingBox().offset(x, -1.0D, z)) + .isEmpty(); d5 = z) { + if (x < d6 && x >= -d6) { + x = 0.0D; + } else if (x > 0.0D) { + x -= d6; + } else { + x += d6; + } + + d3 = x; + if (z < d6 && z >= -d6) { + z = 0.0D; + } else if (z > 0.0D) { + z -= d6; + } else { + z += d6; + } + } + } + + List list1 = this.worldObj.getCollidingBoundingBoxes(this, + this.getEntityBoundingBox().addCoord(x, y, z)); + AxisAlignedBB axisalignedbb = this.getEntityBoundingBox(); + + for (int i = 0, l = list1.size(); i < l; ++i) { + y = list1.get(i).calculateYOffset(this.getEntityBoundingBox(), y); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D)); + boolean flag1 = this.onGround || d4 != y && d4 < 0.0D; + + for (int i = 0, l = list1.size(); i < l; ++i) { + x = list1.get(i).calculateXOffset(this.getEntityBoundingBox(), x); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, 0.0D, 0.0D)); + + for (int i = 0, l = list1.size(); i < l; ++i) { + z = list1.get(i).calculateZOffset(this.getEntityBoundingBox(), z); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, 0.0D, z)); + if (this.stepHeight > 0.0F && flag1 && (d3 != x || d5 != z)) { + double d11 = x; + double d7 = y; + double d8 = z; + AxisAlignedBB axisalignedbb3 = this.getEntityBoundingBox(); + this.setEntityBoundingBox(axisalignedbb); + y = (double) this.stepHeight; + List list = this.worldObj.getCollidingBoundingBoxes(this, + this.getEntityBoundingBox().addCoord(d3, y, d5)); + AxisAlignedBB axisalignedbb4 = this.getEntityBoundingBox(); + AxisAlignedBB axisalignedbb5 = axisalignedbb4.addCoord(d3, 0.0D, d5); + double d9 = y; + + for (int i = 0, l = list.size(); i < l; ++i) { + d9 = list.get(i).calculateYOffset(axisalignedbb5, d9); + } + + axisalignedbb4 = axisalignedbb4.offset(0.0D, d9, 0.0D); + double d15 = d3; + + for (int i = 0, l = list.size(); i < l; ++i) { + d15 = list.get(i).calculateXOffset(axisalignedbb4, d15); + } + + axisalignedbb4 = axisalignedbb4.offset(d15, 0.0D, 0.0D); + double d16 = d5; + + for (int i = 0, l = list.size(); i < l; ++i) { + d16 = list.get(i).calculateZOffset(axisalignedbb4, d16); + } + + axisalignedbb4 = axisalignedbb4.offset(0.0D, 0.0D, d16); + AxisAlignedBB axisalignedbb14 = this.getEntityBoundingBox(); + double d17 = y; + + for (int i = 0, l = list.size(); i < l; ++i) { + d17 = list.get(i).calculateYOffset(axisalignedbb14, d17); + } + + axisalignedbb14 = axisalignedbb14.offset(0.0D, d17, 0.0D); + double d18 = d3; + + for (int i = 0, l = list.size(); i < l; ++i) { + d18 = list.get(i).calculateXOffset(axisalignedbb14, d18); + } + + axisalignedbb14 = axisalignedbb14.offset(d18, 0.0D, 0.0D); + double d19 = d5; + + for (int i = 0, l = list.size(); i < l; ++i) { + d19 = list.get(i).calculateZOffset(axisalignedbb14, d19); + } + + axisalignedbb14 = axisalignedbb14.offset(0.0D, 0.0D, d19); + double d20 = d15 * d15 + d16 * d16; + double d10 = d18 * d18 + d19 * d19; + if (d20 > d10) { + x = d15; + z = d16; + y = -d9; + this.setEntityBoundingBox(axisalignedbb4); + } else { + x = d18; + z = d19; + y = -d17; + this.setEntityBoundingBox(axisalignedbb14); + } + + for (int i = 0, l = list.size(); i < l; ++i) { + y = list.get(i).calculateYOffset(this.getEntityBoundingBox(), y); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D)); + if (d11 * d11 + d8 * d8 >= x * x + z * z) { + x = d11; + y = d7; + z = d8; + this.setEntityBoundingBox(axisalignedbb3); + } + } + + this.resetPositionToBB(); + this.isCollidedHorizontally = d3 != x || d5 != z; + this.isCollidedVertically = d4 != y; + this.onGround = this.isCollidedVertically && d4 < 0.0D; + this.isCollided = this.isCollidedHorizontally || this.isCollidedVertically; + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.posY - 0.20000000298023224D); + int k = MathHelper.floor_double(this.posZ); + BlockPos blockpos = new BlockPos(i, j, k); + Block block1 = this.worldObj.getBlockState(blockpos).getBlock(); + if (block1.getMaterial() == Material.air) { + Block block = this.worldObj.getBlockState(blockpos.down()).getBlock(); + if (block instanceof BlockFence || block instanceof BlockWall || block instanceof BlockFenceGate) { + block1 = block; + blockpos = blockpos.down(); + } + } + + this.updateFallState(y, this.onGround, block1, blockpos); + if (d3 != x) { + this.motionX = 0.0D; + } + + if (d5 != z) { + this.motionZ = 0.0D; + } + + if (d4 != y) { + block1.onLanded(this.worldObj, this); + } + + if (this.canTriggerWalking() && !flag && this.ridingEntity == null) { + double d12 = this.posX - d0; + double d13 = this.posY - d1; + double d14 = this.posZ - d2; + if (block1 != Blocks.ladder) { + d13 = 0.0D; + } + + if (block1 != null && this.onGround) { + block1.onEntityCollidedWithBlock(this.worldObj, blockpos, this); + } + + this.distanceWalkedModified = (float) ((double) this.distanceWalkedModified + + (double) MathHelper.sqrt_double(d12 * d12 + d14 * d14) * 0.6D); + this.distanceWalkedOnStepModified = (float) ((double) this.distanceWalkedOnStepModified + + (double) MathHelper.sqrt_double(d12 * d12 + d13 * d13 + d14 * d14) * 0.6D); + if (this.distanceWalkedOnStepModified > (float) this.nextStepDistance + && block1.getMaterial() != Material.air) { + this.nextStepDistance = (int) this.distanceWalkedOnStepModified + 1; + if (this.isInWater()) { + float f = MathHelper.sqrt_double(this.motionX * this.motionX * 0.20000000298023224D + + this.motionY * this.motionY + this.motionZ * this.motionZ * 0.20000000298023224D) + * 0.35F; + if (f > 1.0F) { + f = 1.0F; + } + + this.playSound(this.getSwimSound(), f, + 1.0F + (this.rand.nextFloat() - this.rand.nextFloat()) * 0.4F); + } + + this.playStepSound(blockpos, block1); + } + } + + try { + this.doBlockCollisions(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Checking entity block collision"); + CrashReportCategory crashreportcategory = crashreport + .makeCategory("Entity being checked for collision"); + this.addEntityCrashInfo(crashreportcategory); + throw new ReportedException(crashreport); + } + + boolean flag2 = this.isWet(); + if (this.worldObj.isFlammableWithin(this.getEntityBoundingBox().contract(0.001D, 0.001D, 0.001D))) { + this.dealFireDamage(1); + if (!flag2) { + ++this.fire; + if (this.fire == 0) { + this.setFire(8); + } + } + } else if (this.fire <= 0) { + this.fire = -this.fireResistance; + } + + if (flag2 && this.fire > 0) { + this.playSound("random.fizz", 0.7F, 1.6F + (this.rand.nextFloat() - this.rand.nextFloat()) * 0.4F); + this.fire = -this.fireResistance; + } + } + } + + /**+ + * Resets the entity's position to the center (planar) and + * bottom (vertical) points of its bounding box. + */ + private void resetPositionToBB() { + this.posX = (this.getEntityBoundingBox().minX + this.getEntityBoundingBox().maxX) / 2.0D; + this.posY = this.getEntityBoundingBox().minY; + this.posZ = (this.getEntityBoundingBox().minZ + this.getEntityBoundingBox().maxZ) / 2.0D; + } + + protected String getSwimSound() { + return "game.neutral.swim"; + } + + protected void doBlockCollisions() { + BlockPos blockpos = new BlockPos(this.getEntityBoundingBox().minX + 0.001D, + this.getEntityBoundingBox().minY + 0.001D, this.getEntityBoundingBox().minZ + 0.001D); + BlockPos blockpos1 = new BlockPos(this.getEntityBoundingBox().maxX - 0.001D, + this.getEntityBoundingBox().maxY - 0.001D, this.getEntityBoundingBox().maxZ - 0.001D); + if (this.worldObj.isAreaLoaded(blockpos, blockpos1)) { + for (int i = blockpos.getX(); i <= blockpos1.getX(); ++i) { + for (int j = blockpos.getY(); j <= blockpos1.getY(); ++j) { + for (int k = blockpos.getZ(); k <= blockpos1.getZ(); ++k) { + BlockPos blockpos2 = new BlockPos(i, j, k); + IBlockState iblockstate = this.worldObj.getBlockState(blockpos2); + + try { + iblockstate.getBlock().onEntityCollidedWithBlock(this.worldObj, blockpos2, iblockstate, + this); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, + "Colliding entity with block"); + CrashReportCategory crashreportcategory = crashreport + .makeCategory("Block being collided with"); + CrashReportCategory.addBlockInfo(crashreportcategory, blockpos2, iblockstate); + throw new ReportedException(crashreport); + } + } + } + } + } + + } + + protected void playStepSound(BlockPos pos, Block blockIn) { + Block.SoundType block$soundtype = blockIn.stepSound; + if (this.worldObj.getBlockState(pos.up()).getBlock() == Blocks.snow_layer) { + block$soundtype = Blocks.snow_layer.stepSound; + this.playSound(block$soundtype.getStepSound(), block$soundtype.getVolume() * 0.15F, + block$soundtype.getFrequency()); + } else if (!blockIn.getMaterial().isLiquid()) { + this.playSound(block$soundtype.getStepSound(), block$soundtype.getVolume() * 0.15F, + block$soundtype.getFrequency()); + } + + } + + public void playSound(String s, float f, float f1) { + if (!this.isSilent()) { + this.worldObj.playSoundAtEntity(this, s, f, f1); + } + + } + + /**+ + * @return True if this entity will not play sounds + */ + public boolean isSilent() { + return this.dataWatcher.getWatchableObjectByte(4) == 1; + } + + /**+ + * When set to true the entity will not play sounds. + */ + public void setSilent(boolean isSilent) { + this.dataWatcher.updateObject(4, Byte.valueOf((byte) (isSilent ? 1 : 0))); + } + + /**+ + * returns if this entity triggers Block.onEntityWalking on the + * blocks they walk on. used for spiders and wolves to prevent + * them from trampling crops + */ + protected boolean canTriggerWalking() { + return true; + } + + protected void updateFallState(double d0, boolean flag, Block block, BlockPos blockpos) { + if (flag) { + if (this.fallDistance > 0.0F) { + if (block != null) { + block.onFallenUpon(this.worldObj, blockpos, this, this.fallDistance); + } else { + this.fall(this.fallDistance, 1.0F); + } + + this.fallDistance = 0.0F; + } + } else if (d0 < 0.0D) { + this.fallDistance = (float) ((double) this.fallDistance - d0); + } + + } + + /**+ + * Returns the collision bounding box for this entity + */ + public AxisAlignedBB getCollisionBoundingBox() { + return null; + } + + /**+ + * Will deal the specified amount of damage to the entity if the + * entity isn't immune to fire damage. Args: amountDamage + */ + protected void dealFireDamage(int amount) { + if (!this.isImmuneToFire) { + this.attackEntityFrom(DamageSource.inFire, (float) amount); + } + + } + + public final boolean isImmuneToFire() { + return this.isImmuneToFire; + } + + public void fall(float distance, float damageMultiplier) { + if (this.riddenByEntity != null) { + this.riddenByEntity.fall(distance, damageMultiplier); + } + + } + + /**+ + * Checks if this entity is either in water or on an open air + * block in rain (used in wolves). + */ + public boolean isWet() { + return this.inWater || this.worldObj.canLightningStrike(new BlockPos(this.posX, this.posY, this.posZ)) + || this.worldObj + .canLightningStrike(new BlockPos(this.posX, this.posY + (double) this.height, this.posZ)); + } + + /**+ + * Checks if this entity is inside water (if inWater field is + * true as a result of handleWaterMovement() returning true) + */ + public boolean isInWater() { + return this.inWater; + } + + /**+ + * Returns if this entity is in water and will end up adding the + * waters velocity to the entity + */ + public boolean handleWaterMovement() { + if (this.worldObj.handleMaterialAcceleration( + this.getEntityBoundingBox().expand(0.0D, -0.4000000059604645D, 0.0D).contract(0.001D, 0.001D, 0.001D), + Material.water, this)) { + if (!this.inWater && !this.firstUpdate) { + this.resetHeight(); + } + + this.fallDistance = 0.0F; + this.inWater = true; + this.fire = 0; + } else { + this.inWater = false; + } + + return this.inWater; + } + + /**+ + * sets the players height back to normal after doing things + * like sleeping and dieing + */ + protected void resetHeight() { + float f = MathHelper.sqrt_double(this.motionX * this.motionX * 0.20000000298023224D + + this.motionY * this.motionY + this.motionZ * this.motionZ * 0.20000000298023224D) * 0.2F; + if (f > 1.0F) { + f = 1.0F; + } + + this.playSound(this.getSplashSound(), f, 1.0F + (this.rand.nextFloat() - this.rand.nextFloat()) * 0.4F); + float f1 = (float) MathHelper.floor_double(this.getEntityBoundingBox().minY); + + for (int i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + float f2 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width; + float f3 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width; + this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX + (double) f2, (double) (f1 + 1.0F), + this.posZ + (double) f3, this.motionX, this.motionY - (double) (this.rand.nextFloat() * 0.2F), + this.motionZ, new int[0]); + } + + for (int j = 0; (float) j < 1.0F + this.width * 20.0F; ++j) { + float f4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width; + float f5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width; + this.worldObj.spawnParticle(EnumParticleTypes.WATER_SPLASH, this.posX + (double) f4, (double) (f1 + 1.0F), + this.posZ + (double) f5, this.motionX, this.motionY, this.motionZ, new int[0]); + } + + } + + /**+ + * Attempts to create sprinting particles if the entity is + * sprinting and not in water. + */ + public void spawnRunningParticles() { + if (this.isSprinting() && !this.isInWater()) { + this.createRunningParticles(); + } + + } + + protected void createRunningParticles() { + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.posY - 0.20000000298023224D); + int k = MathHelper.floor_double(this.posZ); + BlockPos blockpos = new BlockPos(i, j, k); + IBlockState iblockstate = this.worldObj.getBlockState(blockpos); + Block block = iblockstate.getBlock(); + if (block.getRenderType() != -1) { + this.worldObj.spawnParticle(EnumParticleTypes.BLOCK_CRACK, + this.posX + ((double) this.rand.nextFloat() - 0.5D) * (double) this.width, + this.getEntityBoundingBox().minY + 0.1D, + this.posZ + ((double) this.rand.nextFloat() - 0.5D) * (double) this.width, -this.motionX * 4.0D, + 1.5D, -this.motionZ * 4.0D, new int[] { Block.getStateId(iblockstate) }); + } + + } + + protected String getSplashSound() { + return "game.neutral.swim.splash"; + } + + /**+ + * Checks if the current block the entity is within of the + * specified material type + */ + public boolean isInsideOfMaterial(Material materialIn) { + double d0 = this.posY + (double) this.getEyeHeight(); + BlockPos blockpos = new BlockPos(this.posX, d0, this.posZ); + IBlockState iblockstate = this.worldObj.getBlockState(blockpos); + Block block = iblockstate.getBlock(); + if (block.getMaterial() == materialIn) { + float f = BlockLiquid.getLiquidHeightPercent(iblockstate.getBlock().getMetaFromState(iblockstate)) + - 0.11111111F; + float f1 = (float) (blockpos.getY() + 1) - f; + boolean flag = d0 < (double) f1; + return !flag && this instanceof EntityPlayer ? false : flag; + } else { + return false; + } + } + + public boolean isInLava() { + return this.worldObj.isMaterialInBB( + this.getEntityBoundingBox().expand(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), + Material.lava); + } + + /**+ + * Used in both water and by flying objects + */ + public void moveFlying(float strafe, float forward, float friction) { + float f = strafe * strafe + forward * forward; + if (f >= 1.0E-4F) { + f = MathHelper.sqrt_float(f); + if (f < 1.0F) { + f = 1.0F; + } + + f = friction / f; + strafe = strafe * f; + forward = forward * f; + float f1 = MathHelper.sin(this.rotationYaw * 3.1415927F / 180.0F); + float f2 = MathHelper.cos(this.rotationYaw * 3.1415927F / 180.0F); + this.motionX += (double) (strafe * f2 - forward * f1); + this.motionZ += (double) (forward * f2 + strafe * f1); + } + } + + public int getBrightnessForRender(float var1) { + BlockPos blockpos = new BlockPos(this.posX, this.posY + (double) this.getEyeHeight(), this.posZ); + int i = 0; + if (DynamicLightsStateManager.isDynamicLightsRender()) { + i += (int) (getEaglerDynamicLightsValueSimple(var1) * 15.0f); + } + return this.worldObj.isBlockLoaded(blockpos) ? this.worldObj.getCombinedLight(blockpos, -i) + : (i > 15 ? 240 : (i << 4)); + } + + /**+ + * Gets how bright this entity is. + */ + public float getBrightness(float var1) { + BlockPos blockpos = new BlockPos(this.posX, this.posY + (double) this.getEyeHeight(), this.posZ); + float f = this.worldObj.isBlockLoaded(blockpos) ? this.worldObj.getLightBrightness(blockpos) : 0.0F; + if (DynamicLightsStateManager.isDynamicLightsRender()) { + f = Math.min(f + getEaglerDynamicLightsValueSimple(var1), 1.0f); + } + return f; + } + + /**+ + * Sets the reference to the World object. + */ + public void setWorld(World worldIn) { + this.worldObj = worldIn; + } + + /**+ + * Sets the entity's position and rotation. + */ + public void setPositionAndRotation(double x, double y, double z, float yaw, float pitch) { + this.prevPosX = this.posX = x; + this.prevPosY = this.posY = y; + this.prevPosZ = this.posZ = z; + this.prevRotationYaw = this.rotationYaw = yaw; + this.prevRotationPitch = this.rotationPitch = pitch; + double d0 = (double) (this.prevRotationYaw - yaw); + if (d0 < -180.0D) { + this.prevRotationYaw += 360.0F; + } + + if (d0 >= 180.0D) { + this.prevRotationYaw -= 360.0F; + } + + this.setPosition(this.posX, this.posY, this.posZ); + this.setRotation(yaw, pitch); + } + + public void moveToBlockPosAndAngles(BlockPos pos, float rotationYawIn, float rotationPitchIn) { + this.setLocationAndAngles((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D, + rotationYawIn, rotationPitchIn); + } + + /**+ + * Sets the location and Yaw/Pitch of an entity in the world + */ + public void setLocationAndAngles(double x, double y, double z, float yaw, float pitch) { + this.lastTickPosX = this.prevPosX = this.posX = x; + this.lastTickPosY = this.prevPosY = this.posY = y; + this.lastTickPosZ = this.prevPosZ = this.posZ = z; + this.rotationYaw = yaw; + this.rotationPitch = pitch; + this.setPosition(this.posX, this.posY, this.posZ); + } + + /**+ + * Returns the distance to the entity. Args: entity + */ + public float getDistanceToEntity(Entity entityIn) { + float f = (float) (this.posX - entityIn.posX); + float f1 = (float) (this.posY - entityIn.posY); + float f2 = (float) (this.posZ - entityIn.posZ); + return MathHelper.sqrt_float(f * f + f1 * f1 + f2 * f2); + } + + /**+ + * Gets the squared distance to the position. Args: x, y, z + */ + public double getDistanceSq(double x, double y, double z) { + double d0 = this.posX - x; + double d1 = this.posY - y; + double d2 = this.posZ - z; + return d0 * d0 + d1 * d1 + d2 * d2; + } + + /**+ + * Gets the squared distance to the position. Args: x, y, z + */ + public double getDistanceSq(BlockPos pos) { + return pos.distanceSq(this.posX, this.posY, this.posZ); + } + + public double getDistanceSqToCenter(BlockPos pos) { + return pos.distanceSqToCenter(this.posX, this.posY, this.posZ); + } + + /**+ + * Gets the distance to the position. Args: x, y, z + */ + public double getDistance(double x, double y, double z) { + double d0 = this.posX - x; + double d1 = this.posY - y; + double d2 = this.posZ - z; + return (double) MathHelper.sqrt_double(d0 * d0 + d1 * d1 + d2 * d2); + } + + /**+ + * Returns the squared distance to the entity. Args: entity + */ + public double getDistanceSqToEntity(Entity entityIn) { + double d0 = this.posX - entityIn.posX; + double d1 = this.posY - entityIn.posY; + double d2 = this.posZ - entityIn.posZ; + return d0 * d0 + d1 * d1 + d2 * d2; + } + + /**+ + * Called by a player entity when they collide with an entity + */ + public void onCollideWithPlayer(EntityPlayer parEntityPlayer) { + } + + /**+ + * Applies a velocity to each of the entities pushing them away + * from each other. Args: entity + */ + public void applyEntityCollision(Entity entityIn) { + if (entityIn.riddenByEntity != this && entityIn.ridingEntity != this) { + if (!entityIn.noClip && !this.noClip) { + double d0 = entityIn.posX - this.posX; + double d1 = entityIn.posZ - this.posZ; + double d2 = MathHelper.abs_max(d0, d1); + if (d2 >= 0.009999999776482582D) { + d2 = (double) MathHelper.sqrt_double(d2); + d0 = d0 / d2; + d1 = d1 / d2; + double d3 = 1.0D / d2; + if (d3 > 1.0D) { + d3 = 1.0D; + } + + d0 = d0 * d3; + d1 = d1 * d3; + d0 = d0 * 0.05000000074505806D; + d1 = d1 * 0.05000000074505806D; + d0 = d0 * (double) (1.0F - this.entityCollisionReduction); + d1 = d1 * (double) (1.0F - this.entityCollisionReduction); + if (this.riddenByEntity == null) { + this.addVelocity(-d0, 0.0D, -d1); + } + + if (entityIn.riddenByEntity == null) { + entityIn.addVelocity(d0, 0.0D, d1); + } + } + + } + } + } + + /**+ + * Adds to the current velocity of the entity. Args: x, y, z + */ + public void addVelocity(double x, double y, double z) { + this.motionX += x; + this.motionY += y; + this.motionZ += z; + this.isAirBorne = true; + } + + /**+ + * Sets that this entity has been attacked. + */ + protected void setBeenAttacked() { + this.velocityChanged = true; + } + + /**+ + * Called when the entity is attacked. + */ + public boolean attackEntityFrom(DamageSource damagesource, float var2) { + if (this.isEntityInvulnerable(damagesource)) { + return false; + } else { + this.setBeenAttacked(); + return false; + } + } + + /**+ + * interpolated look vector + */ + public Vec3 getLook(float partialTicks) { + if (partialTicks == 1.0F) { + return this.getVectorForRotation(this.rotationPitch, this.rotationYaw); + } else { + float f = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * partialTicks; + float f1 = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * partialTicks; + return this.getVectorForRotation(f, f1); + } + } + + /**+ + * Creates a Vec3 using the pitch and yaw of the entities + * rotation. + */ + protected final Vec3 getVectorForRotation(float pitch, float yaw) { + float f = MathHelper.cos(-yaw * 0.017453292F - 3.1415927F); + float f1 = MathHelper.sin(-yaw * 0.017453292F - 3.1415927F); + float f2 = -MathHelper.cos(-pitch * 0.017453292F); + float f3 = MathHelper.sin(-pitch * 0.017453292F); + return new Vec3((double) (f1 * f2), (double) f3, (double) (f * f2)); + } + + public Vec3 getPositionEyes(float partialTicks) { + if (partialTicks == 1.0F) { + return new Vec3(this.posX, this.posY + (double) this.getEyeHeight(), this.posZ); + } else { + double d0 = this.prevPosX + (this.posX - this.prevPosX) * (double) partialTicks; + double d1 = this.prevPosY + (this.posY - this.prevPosY) * (double) partialTicks + + (double) this.getEyeHeight(); + double d2 = this.prevPosZ + (this.posZ - this.prevPosZ) * (double) partialTicks; + return new Vec3(d0, d1, d2); + } + } + + public MovingObjectPosition rayTrace(double blockReachDistance, float partialTicks) { + Vec3 vec3 = this.getPositionEyes(partialTicks); + Vec3 vec31 = this.getLook(partialTicks); + Vec3 vec32 = vec3.addVector(vec31.xCoord * blockReachDistance, vec31.yCoord * blockReachDistance, + vec31.zCoord * blockReachDistance); + return this.worldObj.rayTraceBlocks(vec3, vec32, false, false, true); + } + + /**+ + * Returns true if other Entities should be prevented from + * moving through this Entity. + */ + public boolean canBeCollidedWith() { + return false; + } + + /**+ + * Returns true if this entity should push and be pushed by + * other entities when colliding. + */ + public boolean canBePushed() { + return false; + } + + /**+ + * Adds a value to the player score. Currently not actually used + * and the entity passed in does nothing. Args: entity, + * scoreToAdd + */ + public void addToPlayerScore(Entity entityIn, int amount) { + } + + public boolean isInRangeToRender3d(double x, double y, double z) { + double d0 = this.posX - x; + double d1 = this.posY - y; + double d2 = this.posZ - z; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + return this.isInRangeToRenderDist(d3); + } + + /**+ + * Checks if the entity is in range to render by using the past + * in distance and comparing it to its average edge length * 64 + * * renderDistanceWeight Args: distance + */ + public boolean isInRangeToRenderDist(double distance) { + double d0 = this.getEntityBoundingBox().getAverageEdgeLength(); + if (Double.isNaN(d0)) { + d0 = 1.0D; + } + + d0 = d0 * 64.0D * this.renderDistanceWeight; + return distance < d0 * d0; + } + + /**+ + * Like writeToNBTOptional but does not check if the entity is + * ridden. Used for saving ridden entities with their riders. + */ + public boolean writeMountToNBT(NBTTagCompound tagCompund) { + String s = this.getEntityString(); + if (!this.isDead && s != null) { + tagCompund.setString("id", s); + this.writeToNBT(tagCompund); + return true; + } else { + return false; + } + } + + /**+ + * Either write this entity to the NBT tag given and return + * true, or return false without doing anything. If this returns + * false the entity is not saved on disk. Ridden entities return + * false here as they are saved with their rider. + */ + public boolean writeToNBTOptional(NBTTagCompound tagCompund) { + String s = this.getEntityString(); + if (!this.isDead && s != null && this.riddenByEntity == null) { + tagCompund.setString("id", s); + this.writeToNBT(tagCompund); + return true; + } else { + return false; + } + } + + /**+ + * Save the entity to NBT (calls an abstract helper method to + * write extra data) + */ + public void writeToNBT(NBTTagCompound tagCompund) { + try { + tagCompund.setTag("Pos", this.newDoubleNBTList(new double[] { this.posX, this.posY, this.posZ })); + tagCompund.setTag("Motion", + this.newDoubleNBTList(new double[] { this.motionX, this.motionY, this.motionZ })); + tagCompund.setTag("Rotation", this.newFloatNBTList(new float[] { this.rotationYaw, this.rotationPitch })); + tagCompund.setFloat("FallDistance", this.fallDistance); + tagCompund.setShort("Fire", (short) this.fire); + tagCompund.setShort("Air", (short) this.getAir()); + tagCompund.setBoolean("OnGround", this.onGround); + tagCompund.setInteger("Dimension", this.dimension); + tagCompund.setBoolean("Invulnerable", this.invulnerable); + tagCompund.setInteger("PortalCooldown", this.timeUntilPortal); + tagCompund.setLong("UUIDMost", this.getUniqueID().getMostSignificantBits()); + tagCompund.setLong("UUIDLeast", this.getUniqueID().getLeastSignificantBits()); + if (this.getCustomNameTag() != null && this.getCustomNameTag().length() > 0) { + tagCompund.setString("CustomName", this.getCustomNameTag()); + tagCompund.setBoolean("CustomNameVisible", this.getAlwaysRenderNameTag()); + } + + this.cmdResultStats.writeStatsToNBT(tagCompund); + if (this.isSilent()) { + tagCompund.setBoolean("Silent", this.isSilent()); + } + + this.writeEntityToNBT(tagCompund); + if (this.ridingEntity != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + if (this.ridingEntity.writeMountToNBT(nbttagcompound)) { + tagCompund.setTag("Riding", nbttagcompound); + } + } + + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Saving entity NBT"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being saved"); + this.addEntityCrashInfo(crashreportcategory); + throw new ReportedException(crashreport); + } + } + + /**+ + * Reads the entity from NBT (calls an abstract helper method to + * read specialized data) + */ + public void readFromNBT(NBTTagCompound tagCompund) { + try { + NBTTagList nbttaglist = tagCompund.getTagList("Pos", 6); + NBTTagList nbttaglist1 = tagCompund.getTagList("Motion", 6); + NBTTagList nbttaglist2 = tagCompund.getTagList("Rotation", 5); + this.motionX = nbttaglist1.getDoubleAt(0); + this.motionY = nbttaglist1.getDoubleAt(1); + this.motionZ = nbttaglist1.getDoubleAt(2); + if (Math.abs(this.motionX) > 10.0D) { + this.motionX = 0.0D; + } + + if (Math.abs(this.motionY) > 10.0D) { + this.motionY = 0.0D; + } + + if (Math.abs(this.motionZ) > 10.0D) { + this.motionZ = 0.0D; + } + + this.prevPosX = this.lastTickPosX = this.posX = nbttaglist.getDoubleAt(0); + this.prevPosY = this.lastTickPosY = this.posY = nbttaglist.getDoubleAt(1); + this.prevPosZ = this.lastTickPosZ = this.posZ = nbttaglist.getDoubleAt(2); + this.prevRotationYaw = this.rotationYaw = nbttaglist2.getFloatAt(0); + this.prevRotationPitch = this.rotationPitch = nbttaglist2.getFloatAt(1); + this.setRotationYawHead(this.rotationYaw); + this.func_181013_g(this.rotationYaw); + this.fallDistance = tagCompund.getFloat("FallDistance"); + this.fire = tagCompund.getShort("Fire"); + this.setAir(tagCompund.getShort("Air")); + this.onGround = tagCompund.getBoolean("OnGround"); + this.dimension = tagCompund.getInteger("Dimension"); + this.invulnerable = tagCompund.getBoolean("Invulnerable"); + this.timeUntilPortal = tagCompund.getInteger("PortalCooldown"); + if (tagCompund.hasKey("UUIDMost", 4) && tagCompund.hasKey("UUIDLeast", 4)) { + this.entityUniqueID = new EaglercraftUUID(tagCompund.getLong("UUIDMost"), + tagCompund.getLong("UUIDLeast")); + } else if (tagCompund.hasKey("UUID", 8)) { + this.entityUniqueID = EaglercraftUUID.fromString(tagCompund.getString("UUID")); + } + + this.setPosition(this.posX, this.posY, this.posZ); + this.setRotation(this.rotationYaw, this.rotationPitch); + if (tagCompund.hasKey("CustomName", 8) && tagCompund.getString("CustomName").length() > 0) { + this.setCustomNameTag(tagCompund.getString("CustomName")); + } + + this.setAlwaysRenderNameTag(tagCompund.getBoolean("CustomNameVisible")); + this.cmdResultStats.readStatsFromNBT(tagCompund); + this.setSilent(tagCompund.getBoolean("Silent")); + this.readEntityFromNBT(tagCompund); + if (this.shouldSetPosAfterLoading()) { + this.setPosition(this.posX, this.posY, this.posZ); + } + + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Loading entity NBT"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being loaded"); + this.addEntityCrashInfo(crashreportcategory); + throw new ReportedException(crashreport); + } + } + + protected boolean shouldSetPosAfterLoading() { + return true; + } + + /**+ + * Returns the string that identifies this Entity's class + */ + protected final String getEntityString() { + return EntityList.getEntityString(this); + } + + protected abstract void readEntityFromNBT(NBTTagCompound var1); + + protected abstract void writeEntityToNBT(NBTTagCompound var1); + + public void onChunkLoad() { + } + + /**+ + * creates a NBT list from the array of doubles passed to this + * function + */ + protected NBTTagList newDoubleNBTList(double... numbers) { + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < numbers.length; ++i) { + nbttaglist.appendTag(new NBTTagDouble(numbers[i])); + } + + return nbttaglist; + } + + /**+ + * Returns a new NBTTagList filled with the specified floats + */ + protected NBTTagList newFloatNBTList(float... numbers) { + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < numbers.length; ++i) { + nbttaglist.appendTag(new NBTTagFloat(numbers[i])); + } + + return nbttaglist; + } + + public EntityItem dropItem(Item itemIn, int size) { + return this.dropItemWithOffset(itemIn, size, 0.0F); + } + + public EntityItem dropItemWithOffset(Item itemIn, int size, float offsetY) { + return this.entityDropItem(new ItemStack(itemIn, size, 0), offsetY); + } + + /**+ + * Drops an item at the position of the entity. + */ + public EntityItem entityDropItem(ItemStack itemStackIn, float offsetY) { + if (itemStackIn.stackSize != 0 && itemStackIn.getItem() != null) { + EntityItem entityitem = new EntityItem(this.worldObj, this.posX, this.posY + (double) offsetY, this.posZ, + itemStackIn); + entityitem.setDefaultPickupDelay(); + this.worldObj.spawnEntityInWorld(entityitem); + return entityitem; + } else { + return null; + } + } + + /**+ + * Checks whether target entity is alive. + */ + public boolean isEntityAlive() { + return !this.isDead; + } + + /**+ + * Checks if this entity is inside of an opaque block + */ + public boolean isEntityInsideOpaqueBlock() { + if (this.noClip) { + return false; + } else { + BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE); + + for (int i = 0; i < 8; ++i) { + int j = MathHelper.floor_double( + this.posY + (double) (((float) ((i >> 0) % 2) - 0.5F) * 0.1F) + (double) this.getEyeHeight()); + int k = MathHelper + .floor_double(this.posX + (double) (((float) ((i >> 1) % 2) - 0.5F) * this.width * 0.8F)); + int l = MathHelper + .floor_double(this.posZ + (double) (((float) ((i >> 2) % 2) - 0.5F) * this.width * 0.8F)); + if (blockpos$mutableblockpos.getX() != k || blockpos$mutableblockpos.getY() != j + || blockpos$mutableblockpos.getZ() != l) { + blockpos$mutableblockpos.func_181079_c(k, j, l); + if (this.worldObj.getBlockState(blockpos$mutableblockpos).getBlock().isVisuallyOpaque()) { + return true; + } + } + } + + return false; + } + } + + /**+ + * First layer of player interaction + */ + public boolean interactFirst(EntityPlayer playerIn) { + return false; + } + + /**+ + * Returns a boundingBox used to collide the entity with other + * entities and blocks. This enables the entity to be pushable + * on contact, like boats or minecarts. + */ + public AxisAlignedBB getCollisionBox(Entity entityIn) { + return null; + } + + /**+ + * Handles updating while being ridden by an entity + */ + public void updateRidden() { + if (this.ridingEntity.isDead) { + this.ridingEntity = null; + } else { + this.motionX = 0.0D; + this.motionY = 0.0D; + this.motionZ = 0.0D; + this.onUpdate(); + if (this.ridingEntity != null) { + this.ridingEntity.updateRiderPosition(); + this.entityRiderYawDelta += (double) (this.ridingEntity.rotationYaw + - this.ridingEntity.prevRotationYaw); + + for (this.entityRiderPitchDelta += (double) (this.ridingEntity.rotationPitch + - this.ridingEntity.prevRotationPitch); this.entityRiderYawDelta >= 180.0D; this.entityRiderYawDelta -= 360.0D) { + ; + } + + while (this.entityRiderYawDelta < -180.0D) { + this.entityRiderYawDelta += 360.0D; + } + + while (this.entityRiderPitchDelta >= 180.0D) { + this.entityRiderPitchDelta -= 360.0D; + } + + while (this.entityRiderPitchDelta < -180.0D) { + this.entityRiderPitchDelta += 360.0D; + } + + double d0 = this.entityRiderYawDelta * 0.5D; + double d1 = this.entityRiderPitchDelta * 0.5D; + float f = 10.0F; + if (d0 > (double) f) { + d0 = (double) f; + } + + if (d0 < (double) (-f)) { + d0 = (double) (-f); + } + + if (d1 > (double) f) { + d1 = (double) f; + } + + if (d1 < (double) (-f)) { + d1 = (double) (-f); + } + + this.entityRiderYawDelta -= d0; + this.entityRiderPitchDelta -= d1; + } + } + } + + public void updateRiderPosition() { + if (this.riddenByEntity != null) { + this.riddenByEntity.setPosition(this.posX, + this.posY + this.getMountedYOffset() + this.riddenByEntity.getYOffset(), this.posZ); + } + } + + /**+ + * Returns the Y Offset of this entity. + */ + public double getYOffset() { + return 0.0D; + } + + /**+ + * Returns the Y offset from the entity's position for any + * entity riding this one. + */ + public double getMountedYOffset() { + return (double) this.height * 0.75D; + } + + /**+ + * Called when a player mounts an entity. e.g. mounts a pig, + * mounts a boat. + */ + public void mountEntity(Entity entity) { + this.entityRiderPitchDelta = 0.0D; + this.entityRiderYawDelta = 0.0D; + if (entity == null) { + if (this.ridingEntity != null) { + this.setLocationAndAngles(this.ridingEntity.posX, + this.ridingEntity.getEntityBoundingBox().minY + (double) this.ridingEntity.height, + this.ridingEntity.posZ, this.rotationYaw, this.rotationPitch); + this.ridingEntity.riddenByEntity = null; + } + + this.ridingEntity = null; + } else { + if (this.ridingEntity != null) { + this.ridingEntity.riddenByEntity = null; + } + + if (entity != null) { + for (Entity entity1 = entity.ridingEntity; entity1 != null; entity1 = entity1.ridingEntity) { + if (entity1 == this) { + return; + } + } + } + + this.ridingEntity = entity; + entity.riddenByEntity = this; + } + } + + public void setPositionAndRotation2(double d0, double d1, double d2, float f, float f1, int var9, boolean var10) { + this.setPosition(d0, d1, d2); + this.setRotation(f, f1); + List list = this.worldObj.getCollidingBoundingBoxes(this, + this.getEntityBoundingBox().contract(0.03125D, 0.0D, 0.03125D)); + if (!list.isEmpty()) { + double d3 = 0.0D; + + for (AxisAlignedBB axisalignedbb : (List) list) { + if (axisalignedbb.maxY > d3) { + d3 = axisalignedbb.maxY; + } + } + + d1 = d1 + (d3 - this.getEntityBoundingBox().minY); + this.setPosition(d0, d1, d2); + } + + } + + public float getCollisionBorderSize() { + return 0.1F; + } + + /**+ + * returns a (normalized) vector of where this entity is looking + */ + public Vec3 getLookVec() { + return null; + } + + public void func_181015_d(BlockPos parBlockPos) { + if (this.timeUntilPortal > 0) { + this.timeUntilPortal = this.getPortalCooldown(); + } else { + if (!this.worldObj.isRemote && !parBlockPos.equals(this.field_181016_an)) { + this.field_181016_an = parBlockPos; + BlockPattern.PatternHelper blockpattern$patternhelper = Blocks.portal.func_181089_f(this.worldObj, + parBlockPos); + double d0 = blockpattern$patternhelper.getFinger().getAxis() == EnumFacing.Axis.X + ? (double) blockpattern$patternhelper.func_181117_a().getZ() + : (double) blockpattern$patternhelper.func_181117_a().getX(); + double d1 = blockpattern$patternhelper.getFinger().getAxis() == EnumFacing.Axis.X ? this.posZ + : this.posX; + d1 = Math.abs(MathHelper.func_181160_c( + d1 - (double) (blockpattern$patternhelper.getFinger().rotateY() + .getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? 1 : 0), + d0, d0 - (double) blockpattern$patternhelper.func_181118_d())); + double d2 = MathHelper.func_181160_c(this.posY - 1.0D, + (double) blockpattern$patternhelper.func_181117_a().getY(), + (double) (blockpattern$patternhelper.func_181117_a().getY() + - blockpattern$patternhelper.func_181119_e())); + this.field_181017_ao = new Vec3(d1, d2, 0.0D); + this.field_181018_ap = blockpattern$patternhelper.getFinger(); + } + + this.inPortal = true; + } + } + + /**+ + * Return the amount of cooldown before this entity can use a + * portal again. + */ + public int getPortalCooldown() { + return 300; + } + + /**+ + * Sets the velocity to the args. Args: x, y, z + */ + public void setVelocity(double x, double y, double z) { + this.motionX = x; + this.motionY = y; + this.motionZ = z; + } + + public void handleStatusUpdate(byte id) { + } + + /**+ + * Setups the entity to do the hurt animation. Only used by + * packets in multiplayer. + */ + public void performHurtAnimation() { + } + + /**+ + * returns the inventory of this entity (only used in + * EntityPlayerMP it seems) + */ + public ItemStack[] getInventory() { + return null; + } + + /**+ + * Sets the held item, or an armor slot. Slot 0 is held item. + * Slot 1-4 is armor. Params: Item, slot + */ + public void setCurrentItemOrArmor(int var1, ItemStack var2) { + } + + /**+ + * Returns true if the entity is on fire. Used by render to add + * the fire effect on rendering. + */ + public boolean isBurning() { + boolean flag = this.worldObj != null && this.worldObj.isRemote; + return !this.isImmuneToFire && (this.fire > 0 || flag && this.getFlag(0)); + } + + /**+ + * Returns true if the entity is riding another entity, used by + * render to rotate the legs to be in 'sit' position for + * players. + */ + public boolean isRiding() { + return this.ridingEntity != null; + } + + /**+ + * Returns if this entity is sneaking. + */ + public boolean isSneaking() { + return this.getFlag(1); + } + + /**+ + * Sets the sneaking flag. + */ + public void setSneaking(boolean sneaking) { + this.setFlag(1, sneaking); + } + + /**+ + * Get if the Entity is sprinting. + */ + public boolean isSprinting() { + return this.getFlag(3); + } + + /**+ + * Set sprinting switch for Entity. + */ + public void setSprinting(boolean flag) { + this.setFlag(3, flag); + } + + public boolean isInvisible() { + return this.getFlag(5); + } + + /**+ + * Only used by renderer in EntityLivingBase + * subclasses.\nDetermines if an entity is visible or not to a + * specfic player, if the entity is normally invisible.\nFor + * EntityLivingBase subclasses, returning false when invisible + * will render the entity semitransparent. + */ + public boolean isInvisibleToPlayer(EntityPlayer player) { + return player.isSpectator() ? false : this.isInvisible(); + } + + public void setInvisible(boolean invisible) { + this.setFlag(5, invisible); + } + + public boolean isEating() { + return this.getFlag(4); + } + + public void setEating(boolean eating) { + this.setFlag(4, eating); + } + + /**+ + * Returns true if the flag is active for the entity. Known + * flags: 0) is burning; 1) is sneaking; 2) is riding something; + * 3) is sprinting; 4) is eating + */ + protected boolean getFlag(int flag) { + return (this.dataWatcher.getWatchableObjectByte(0) & 1 << flag) != 0; + } + + /**+ + * Enable or disable a entity flag, see getEntityFlag to read + * the know flags. + */ + protected void setFlag(int flag, boolean set) { + byte b0 = this.dataWatcher.getWatchableObjectByte(0); + if (set) { + this.dataWatcher.updateObject(0, Byte.valueOf((byte) (b0 | 1 << flag))); + } else { + this.dataWatcher.updateObject(0, Byte.valueOf((byte) (b0 & ~(1 << flag)))); + } + + } + + public int getAir() { + return this.dataWatcher.getWatchableObjectShort(1); + } + + public void setAir(int air) { + this.dataWatcher.updateObject(1, Short.valueOf((short) air)); + } + + /**+ + * Called when a lightning bolt hits the entity. + */ + public void onStruckByLightning(EntityLightningBolt lightningBolt) { + this.attackEntityFrom(DamageSource.lightningBolt, 5.0F); + ++this.fire; + if (this.fire == 0) { + this.setFire(8); + } + + } + + /**+ + * This method gets called when the entity kills another one. + */ + public void onKillEntity(EntityLivingBase entityLivingIn) { + } + + protected boolean pushOutOfBlocks(double d0, double d1, double d2) { + BlockPos blockpos = new BlockPos(d0, d1, d2); + double d3 = d0 - (double) blockpos.getX(); + double d4 = d1 - (double) blockpos.getY(); + double d5 = d2 - (double) blockpos.getZ(); + List list = this.worldObj.func_147461_a(this.getEntityBoundingBox()); + if (list.isEmpty() && !this.worldObj.isBlockFullCube(blockpos)) { + return false; + } else { + byte b0 = 3; + double d6 = 9999.0D; + if (!this.worldObj.isBlockFullCube(blockpos.west()) && d3 < d6) { + d6 = d3; + b0 = 0; + } + + if (!this.worldObj.isBlockFullCube(blockpos.east()) && 1.0D - d3 < d6) { + d6 = 1.0D - d3; + b0 = 1; + } + + if (!this.worldObj.isBlockFullCube(blockpos.up()) && 1.0D - d4 < d6) { + d6 = 1.0D - d4; + b0 = 3; + } + + if (!this.worldObj.isBlockFullCube(blockpos.north()) && d5 < d6) { + d6 = d5; + b0 = 4; + } + + if (!this.worldObj.isBlockFullCube(blockpos.south()) && 1.0D - d5 < d6) { + d6 = 1.0D - d5; + b0 = 5; + } + + float f = this.rand.nextFloat() * 0.2F + 0.1F; + if (b0 == 0) { + this.motionX = (double) (-f); + } + + if (b0 == 1) { + this.motionX = (double) f; + } + + if (b0 == 3) { + this.motionY = (double) f; + } + + if (b0 == 4) { + this.motionZ = (double) (-f); + } + + if (b0 == 5) { + this.motionZ = (double) f; + } + + return true; + } + } + + /**+ + * Sets the Entity inside a web block. + */ + public void setInWeb() { + this.isInWeb = true; + this.fallDistance = 0.0F; + } + + /**+ + * Gets the name of this command sender (usually username, but + * possibly "Rcon") + */ + public String getName() { + if (this.hasCustomName()) { + return this.getCustomNameTag(); + } else { + String s = EntityList.getEntityString(this); + if (s == null) { + s = "generic"; + } + + return StatCollector.translateToLocal("entity." + s + ".name"); + } + } + + /** + * + + * Return the Entity parts making up this Entity (currently only + * for dragons) + */ + public Entity[] getParts() { + return null; + } + + /**+ + * Returns true if Entity argument is equal to this Entity + */ + public boolean isEntityEqual(Entity entityIn) { + return this == entityIn; + } + + public float getRotationYawHead() { + return 0.0F; + } + + /**+ + * Sets the head's yaw rotation of the entity. + */ + public void setRotationYawHead(float rotation) { + } + + public void func_181013_g(float parFloat1) { + } + + /**+ + * If returns false, the item will not inflict any damage + * against entities. + */ + public boolean canAttackWithItem() { + return true; + } + + /**+ + * Called when a player attacks an entity. If this returns true + * the attack will not happen. + */ + public boolean hitByEntity(Entity entityIn) { + return false; + } + + public String toString() { + return HString.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", + new Object[] { this.getClass().getSimpleName(), this.getName(), Integer.valueOf(this.entityId), + this.worldObj == null ? "~NULL~" : this.worldObj.getWorldInfo().getWorldName(), + Double.valueOf(this.posX), Double.valueOf(this.posY), Double.valueOf(this.posZ) }); + } + + public boolean isEntityInvulnerable(DamageSource source) { + return this.invulnerable && source != DamageSource.outOfWorld && !source.isCreativePlayer(); + } + + /**+ + * Sets this entity's location and angles to the location and + * angles of the passed in entity. + */ + public void copyLocationAndAnglesFrom(Entity entityIn) { + this.setLocationAndAngles(entityIn.posX, entityIn.posY, entityIn.posZ, entityIn.rotationYaw, + entityIn.rotationPitch); + } + + /**+ + * Prepares this entity in new dimension by copying NBT data + * from entity in old dimension + */ + public void copyDataFromOld(Entity entityIn) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + entityIn.writeToNBT(nbttagcompound); + this.readFromNBT(nbttagcompound); + this.timeUntilPortal = entityIn.timeUntilPortal; + this.field_181016_an = entityIn.field_181016_an; + this.field_181017_ao = entityIn.field_181017_ao; + this.field_181018_ap = entityIn.field_181018_ap; + } + + /**+ + * Teleports the entity to another dimension. Params: Dimension + * number to teleport to + */ + public void travelToDimension(int i) { + if (!this.worldObj.isRemote && !this.isDead) { + MinecraftServer minecraftserver = MinecraftServer.getServer(); + int j = this.dimension; + WorldServer worldserver = minecraftserver.worldServerForDimension(j); + WorldServer worldserver1 = minecraftserver.worldServerForDimension(i); + this.dimension = i; + if (j == 1 && i == 1) { + worldserver1 = minecraftserver.worldServerForDimension(0); + this.dimension = 0; + } + + this.worldObj.removeEntity(this); + this.isDead = false; + minecraftserver.getConfigurationManager().transferEntityToWorld(this, j, worldserver, worldserver1); + Entity entity = EntityList.createEntityByName(EntityList.getEntityString(this), worldserver1); + if (entity != null) { + entity.copyDataFromOld(this); + if (j == 1 && i == 1) { + BlockPos blockpos = this.worldObj.getTopSolidOrLiquidBlock(worldserver1.getSpawnPoint()); + entity.moveToBlockPosAndAngles(blockpos, entity.rotationYaw, entity.rotationPitch); + } + + worldserver1.spawnEntityInWorld(entity); + } + + this.isDead = true; + worldserver.resetUpdateEntityTick(); + worldserver1.resetUpdateEntityTick(); + } + } + + /**+ + * Explosion resistance of a block relative to this entity + */ + public float getExplosionResistance(Explosion explosionIn, World worldIn, BlockPos pos, IBlockState blockStateIn) { + return blockStateIn.getBlock().getExplosionResistance(this); + } + + public boolean verifyExplosion(Explosion explosionIn, World worldIn, BlockPos pos, IBlockState blockStateIn, + float parFloat1) { + return true; + } + + /**+ + * The maximum height from where the entity is alowed to jump + * (used in pathfinder) + */ + public int getMaxFallHeight() { + return 3; + } + + public Vec3 func_181014_aG() { + return this.field_181017_ao; + } + + public EnumFacing func_181012_aH() { + return this.field_181018_ap; + } + + /**+ + * Return whether this entity should NOT trigger a pressure + * plate or a tripwire. + */ + public boolean doesEntityNotTriggerPressurePlate() { + return false; + } + + public void addEntityCrashInfo(CrashReportCategory category) { + category.addCrashSectionCallable("Entity Type", new Callable() { + public String call() throws Exception { + return EntityList.getEntityString(Entity.this) + " (" + Entity.this.getClass().getCanonicalName() + ")"; + } + }); + category.addCrashSection("Entity ID", Integer.valueOf(this.entityId)); + category.addCrashSectionCallable("Entity Name", new Callable() { + public String call() throws Exception { + return Entity.this.getName(); + } + }); + category.addCrashSection("Entity\'s Exact location", String.format("%.2f, %.2f, %.2f", + new Object[] { Double.valueOf(this.posX), Double.valueOf(this.posY), Double.valueOf(this.posZ) })); + category.addCrashSection("Entity\'s Block location", + CrashReportCategory.getCoordinateInfo((double) MathHelper.floor_double(this.posX), + (double) MathHelper.floor_double(this.posY), (double) MathHelper.floor_double(this.posZ))); + category.addCrashSection("Entity\'s Momentum", String.format("%.2f, %.2f, %.2f", new Object[] { + Double.valueOf(this.motionX), Double.valueOf(this.motionY), Double.valueOf(this.motionZ) })); + category.addCrashSectionCallable("Entity\'s Rider", new Callable() { + public String call() throws Exception { + return Entity.this.riddenByEntity.toString(); + } + }); + category.addCrashSectionCallable("Entity\'s Vehicle", new Callable() { + public String call() throws Exception { + return Entity.this.ridingEntity.toString(); + } + }); + } + + /**+ + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() { + return this.isBurning(); + } + + public EaglercraftUUID getUniqueID() { + return this.entityUniqueID; + } + + public boolean isPushedByWater() { + return true; + } + + /**+ + * Get the formatted ChatComponent that will be used for the + * sender's username in chat + */ + public IChatComponent getDisplayName() { + ChatComponentText chatcomponenttext = new ChatComponentText(this.getName()); + chatcomponenttext.getChatStyle().setChatHoverEvent(this.getHoverEvent()); + chatcomponenttext.getChatStyle().setInsertion(this.getUniqueID().toString()); + return chatcomponenttext; + } + + public IChatComponent getDisplayNameProfanityFilter() { + ChatComponentText chatcomponenttext = new ChatComponentText(this.getNameProfanityFilter()); + chatcomponenttext.getChatStyle().setChatHoverEvent(this.getHoverEvent()); + chatcomponenttext.getChatStyle().setInsertion(this.getUniqueID().toString()); + return chatcomponenttext; + } + + /**+ + * Sets the custom name tag for this entity + */ + public void setCustomNameTag(String name) { + this.dataWatcher.updateObject(2, name); + } + + public String getCustomNameTag() { + return this.dataWatcher.getWatchableObjectString(2); + } + + private String lastNameTagForFilter = null; + private String lastFilteredNameTagForFilter = null; + + public String getCustomNameTagProfanityFilter() { + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + String str = getCustomNameTag(); + if (str != null) { + if (!str.equals(lastNameTagForFilter)) { + lastNameTagForFilter = str; + lastFilteredNameTagForFilter = ProfanityFilter.getInstance().profanityFilterString(str); + } + return lastFilteredNameTagForFilter; + } else { + return null; + } + } else { + return getCustomNameTag(); + } + } + + /**+ + * Returns true if this thing is named + */ + public boolean hasCustomName() { + return this.dataWatcher.getWatchableObjectString(2).length() > 0; + } + + public void setAlwaysRenderNameTag(boolean alwaysRenderNameTag) { + this.dataWatcher.updateObject(3, Byte.valueOf((byte) (alwaysRenderNameTag ? 1 : 0))); + } + + public boolean getAlwaysRenderNameTag() { + return this.dataWatcher.getWatchableObjectByte(3) == 1; + } + + /**+ + * Sets the position of the entity and updates the 'last' + * variables + */ + public void setPositionAndUpdate(double d0, double d1, double d2) { + this.setLocationAndAngles(d0, d1, d2, this.rotationYaw, this.rotationPitch); + } + + public boolean getAlwaysRenderNameTagForRender() { + return this.getAlwaysRenderNameTag(); + } + + public void onDataWatcherUpdate(int dataID) { + } + + public EnumFacing getHorizontalFacing() { + return EnumFacing + .getHorizontal(MathHelper.floor_double((double) (this.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3); + } + + protected HoverEvent getHoverEvent() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + String s = EntityList.getEntityString(this); + nbttagcompound.setString("id", this.getUniqueID().toString()); + if (s != null) { + nbttagcompound.setString("type", s); + } + + nbttagcompound.setString("name", this.getName()); + return new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new ChatComponentText(nbttagcompound.toString())); + } + + public boolean isSpectatedByPlayer(EntityPlayerMP var1) { + return true; + } + + public AxisAlignedBB getEntityBoundingBox() { + return this.boundingBox; + } + + public void setEntityBoundingBox(AxisAlignedBB bb) { + this.boundingBox = bb; + } + + public float getEyeHeight() { + return this.height * 0.85F; + } + + public boolean isOutsideBorder() { + return this.isOutsideBorder; + } + + public void setOutsideBorder(boolean outsideBorder) { + this.isOutsideBorder = outsideBorder; + } + + public boolean replaceItemInInventory(int inventorySlot, ItemStack itemStackIn) { + return false; + } + + /**+ + * Send a chat message to the CommandSender + */ + public void addChatMessage(IChatComponent var1) { + } + + /**+ + * Returns {@code true} if the CommandSender is allowed to + * execute the command, {@code false} if not + */ + public boolean canCommandSenderUseCommand(int var1, String var2) { + return true; + } + + /**+ + * Get the position in the world. {@code null} is not + * allowed! If you are not an entity in the world, return + * the coordinates 0, 0, 0 + */ + public BlockPos getPosition() { + return new BlockPos(this.posX, this.posY + 0.5D, this.posZ); + } + + /**+ + * Get the position vector. {@code null} is not allowed! + * If you are not an entity in the world, return 0.0D, 0.0D, + * 0.0D + */ + public Vec3 getPositionVector() { + return new Vec3(this.posX, this.posY, this.posZ); + } + + /**+ + * Get the world, if available. {@code null} is not + * allowed! If you are not an entity in the world, return + * the overworld + */ + public World getEntityWorld() { + return this.worldObj; + } + + /**+ + * Returns the entity associated with the command sender. MAY BE + * NULL! + */ + public Entity getCommandSenderEntity() { + return this; + } + + /**+ + * Returns true if the command sender should be sent feedback + * about executed commands + */ + public boolean sendCommandFeedback() { + return false; + } + + public void setCommandStat(CommandResultStats.Type commandresultstats$type, int i) { + this.cmdResultStats.func_179672_a(this, commandresultstats$type, i); + } + + public CommandResultStats getCommandStats() { + return this.cmdResultStats; + } + + public void func_174817_o(Entity entityIn) { + this.cmdResultStats.func_179671_a(entityIn.getCommandStats()); + } + + public NBTTagCompound getNBTTagCompound() { + return null; + } + + /**+ + * Called when client receives entity's NBTTagCompound from + * server. + */ + public void clientUpdateEntityNBT(NBTTagCompound compound) { + } + + /**+ + * New version of interactWith that includes vector information + * on where precisely the player targeted. + */ + public boolean interactAt(EntityPlayer player, Vec3 targetVec3) { + return false; + } + + public boolean isImmuneToExplosions() { + return false; + } + + protected void applyEnchantments(EntityLivingBase entityLivingBaseIn, Entity entityIn) { + if (entityIn instanceof EntityLivingBase) { + EnchantmentHelper.applyThornEnchantments((EntityLivingBase) entityIn, entityLivingBaseIn); + } + + EnchantmentHelper.applyArthropodEnchantments(entityLivingBaseIn, entityIn); + } + + public void renderDynamicLightsEagler(float partialTicks, boolean isInFrustum) { + double entityX = prevPosX + (posX - prevPosX) * (double) partialTicks; + double entityY = prevPosY + (posY - prevPosY) * (double) partialTicks; + double entityZ = prevPosZ + (posZ - prevPosZ) * (double) partialTicks; + double entityX2 = entityX - TileEntityRendererDispatcher.staticPlayerX; + double entityY2 = entityY - TileEntityRendererDispatcher.staticPlayerY; + double entityZ2 = entityZ - TileEntityRendererDispatcher.staticPlayerZ; + if (entityX2 * entityX2 + entityY2 * entityY2 + + entityZ2 * entityZ2 < (isInFrustum ? (64.0 * 64.0) : (24.0 * 24.0))) { + renderDynamicLightsEaglerAt(entityX, entityY, entityZ, entityX2, entityY2, entityZ2, partialTicks, + isInFrustum); + } + } + + protected void renderDynamicLightsEaglerAt(double entityX, double entityY, double entityZ, double renderX, + double renderY, double renderZ, float partialTicks, boolean isInFrustum) { + float size = Math.max(width, height); + if (size < 1.0f && !isInFrustum) { + return; + } + if (this.isBurning()) { + float mag = 5.0f * size; + DynamicLightManager.renderDynamicLight("entity_" + entityId + "_fire", entityX, entityY + height * 0.75, + entityZ, mag, 0.487f * mag, 0.1411f * mag, false); + } + } + + public void renderDynamicLightsEaglerSimple(float partialTicks) { + double entityX = prevPosX + (posX - prevPosX) * (double) partialTicks; + double entityY = prevPosY + (posY - prevPosY) * (double) partialTicks; + double entityZ = prevPosZ + (posZ - prevPosZ) * (double) partialTicks; + renderDynamicLightsEaglerSimpleAt(entityX, entityY, entityZ, partialTicks); + } + + protected void renderDynamicLightsEaglerSimpleAt(double entityX, double entityY, double entityZ, + float partialTicks) { + float renderBrightness = this.getEaglerDynamicLightsValueSimple(partialTicks); + if (renderBrightness > 0.1f) { + DynamicLightsStateManager.renderDynamicLight("entity_" + entityId + "_lightmap", entityX, + entityY + height * 0.85, entityZ, renderBrightness * 13.0f); + } + } + + protected float getEaglerDynamicLightsValueSimple(float partialTicks) { + float size = Math.max(width, height); + if (size < 1.0f) { + return 0.0f; + } + if (this.isBurning()) { + return size / 2.0f; + } + return 0.0f; + } +>>>>>>> b302c97c (u37):src/game/java/net/minecraft/entity/Entity.java } \ No newline at end of file diff --git a/src/main/java/net/minecraft/entity/EntityAgeable.java b/src/game/java/net/minecraft/entity/EntityAgeable.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityAgeable.java rename to src/game/java/net/minecraft/entity/EntityAgeable.java diff --git a/src/main/java/net/minecraft/entity/EntityBodyHelper.java b/src/game/java/net/minecraft/entity/EntityBodyHelper.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityBodyHelper.java rename to src/game/java/net/minecraft/entity/EntityBodyHelper.java diff --git a/src/main/java/net/minecraft/entity/EntityConstructor.java b/src/game/java/net/minecraft/entity/EntityConstructor.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityConstructor.java rename to src/game/java/net/minecraft/entity/EntityConstructor.java diff --git a/src/main/java/net/minecraft/entity/EntityCreature.java b/src/game/java/net/minecraft/entity/EntityCreature.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityCreature.java rename to src/game/java/net/minecraft/entity/EntityCreature.java diff --git a/src/main/java/net/minecraft/entity/EntityFlying.java b/src/game/java/net/minecraft/entity/EntityFlying.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityFlying.java rename to src/game/java/net/minecraft/entity/EntityFlying.java diff --git a/src/main/java/net/minecraft/entity/EntityHanging.java b/src/game/java/net/minecraft/entity/EntityHanging.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityHanging.java rename to src/game/java/net/minecraft/entity/EntityHanging.java diff --git a/src/main/java/net/minecraft/entity/EntityLeashKnot.java b/src/game/java/net/minecraft/entity/EntityLeashKnot.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityLeashKnot.java rename to src/game/java/net/minecraft/entity/EntityLeashKnot.java diff --git a/src/main/java/net/minecraft/entity/EntityList.java b/src/game/java/net/minecraft/entity/EntityList.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityList.java rename to src/game/java/net/minecraft/entity/EntityList.java diff --git a/src/main/java/net/minecraft/entity/EntityLiving.java b/src/game/java/net/minecraft/entity/EntityLiving.java similarity index 96% rename from src/main/java/net/minecraft/entity/EntityLiving.java rename to src/game/java/net/minecraft/entity/EntityLiving.java index 9bb10ae..e83f508 100644 --- a/src/main/java/net/minecraft/entity/EntityLiving.java +++ b/src/game/java/net/minecraft/entity/EntityLiving.java @@ -101,9 +101,8 @@ public abstract class EntityLiving extends EntityLivingBase { public EntityLiving(World worldIn) { super(worldIn); - this.tasks = new EntityAITasks(worldIn != null && worldIn.theProfiler != null ? worldIn.theProfiler : null); - this.targetTasks = new EntityAITasks( - worldIn != null && worldIn.theProfiler != null ? worldIn.theProfiler : null); + this.tasks = new EntityAITasks(); + this.targetTasks = new EntityAITasks(); this.lookHelper = new EntityLookHelper(this); this.moveHelper = new EntityMoveHelper(this); this.jumpHelper = new EntityJumpHelper(this); @@ -220,13 +219,10 @@ public abstract class EntityLiving extends EntityLivingBase { */ public void onEntityUpdate() { super.onEntityUpdate(); - this.worldObj.theProfiler.startSection("mobBaseTick"); if (this.isEntityAlive() && this.rand.nextInt(1000) < this.livingSoundTime++) { this.livingSoundTime = -this.getTalkInterval(); this.playLivingSound(); } - - this.worldObj.theProfiler.endSection(); } /** @@ -479,7 +475,6 @@ public abstract class EntityLiving extends EntityLivingBase { */ public void onLivingUpdate() { super.onLivingUpdate(); - this.worldObj.theProfiler.startSection("looting"); if (!this.worldObj.isRemote && this.canPickUpLoot() && !this.dead && this.worldObj.getGameRules().getBoolean("mobGriefing")) { List lst = this.worldObj.getEntitiesWithinAABB(EntityItem.class, @@ -491,8 +486,6 @@ public abstract class EntityLiving extends EntityLivingBase { } } } - - this.worldObj.theProfiler.endSection(); } /** @@ -606,33 +599,15 @@ public abstract class EntityLiving extends EntityLivingBase { protected final void updateEntityActionState() { ++this.entityAge; - this.worldObj.theProfiler.startSection("checkDespawn"); this.despawnEntity(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("sensing"); this.senses.clearSensingCache(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("targetSelector"); this.targetTasks.onUpdateTasks(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("goalSelector"); this.tasks.onUpdateTasks(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("navigation"); this.navigator.onUpdateNavigation(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("mob tick"); this.updateAITasks(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("controls"); - this.worldObj.theProfiler.startSection("move"); this.moveHelper.onUpdateMoveHelper(); - this.worldObj.theProfiler.endStartSection("look"); this.lookHelper.onUpdateLook(); - this.worldObj.theProfiler.endStartSection("jump"); this.jumpHelper.doJump(); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.endSection(); } protected void updateAITasks() { diff --git a/src/main/java/net/minecraft/entity/EntityLivingBase.java b/src/game/java/net/minecraft/entity/EntityLivingBase.java similarity index 98% rename from src/main/java/net/minecraft/entity/EntityLivingBase.java rename to src/game/java/net/minecraft/entity/EntityLivingBase.java index d9dc25a..95cf942 100644 --- a/src/main/java/net/minecraft/entity/EntityLivingBase.java +++ b/src/game/java/net/minecraft/entity/EntityLivingBase.java @@ -251,7 +251,6 @@ public abstract class EntityLivingBase extends Entity { public void onEntityUpdate() { this.prevSwingProgress = this.swingProgress; super.onEntityUpdate(); - this.worldObj.theProfiler.startSection("livingEntityBaseTick"); boolean flag = this instanceof EntityPlayer; if (this.isEntityAlive()) { if (this.isEntityInsideOpaqueBlock()) { @@ -349,7 +348,6 @@ public abstract class EntityLivingBase extends Entity { this.prevRotationYawHead = this.rotationYawHead; this.prevRotationYaw = this.rotationYaw; this.prevRotationPitch = this.rotationPitch; - this.worldObj.theProfiler.endSection(); } protected void frostWalk(BlockPos pos) { @@ -360,8 +358,7 @@ public abstract class EntityLivingBase extends Entity { } } - /** - * + + /**+ * If Animal, checks if the age timer is negative */ public boolean isChild() { @@ -1724,10 +1721,7 @@ public abstract class EntityLivingBase extends Entity { } this.onGroundSpeedFactor += (f3 - this.onGroundSpeedFactor) * 0.3F; - this.worldObj.theProfiler.startSection("headTurn"); f2 = this.func_110146_f(f1, f2); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("rangeChecks"); while (this.rotationYaw - this.prevRotationYaw < -180.0F) { this.prevRotationYaw -= 360.0F; @@ -1767,7 +1761,6 @@ public abstract class EntityLivingBase extends Entity { this.ticksElytraFlying = 0; } - this.worldObj.theProfiler.endSection(); this.movedDistance += f2; } @@ -1836,20 +1829,15 @@ public abstract class EntityLivingBase extends Entity { this.motionZ = 0.0D; } - this.worldObj.theProfiler.startSection("ai"); if (this.isMovementBlocked()) { this.isJumping = false; this.moveStrafing = 0.0F; this.moveForward = 0.0F; this.randomYawVelocity = 0.0F; } else if (this.isServerWorld()) { - this.worldObj.theProfiler.startSection("newAi"); this.updateEntityActionState(); - this.worldObj.theProfiler.endSection(); } - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("jump"); if (this.isJumping) { if (this.isInWater()) { this.updateAITick(); @@ -1863,20 +1851,14 @@ public abstract class EntityLivingBase extends Entity { this.jumpTicks = 0; } - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("travel"); this.moveStrafing *= 0.98F; this.moveForward *= 0.98F; this.randomYawVelocity *= 0.9F; this.updateElytra(); this.moveEntityWithHeading(this.moveStrafing, this.moveForward); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("push"); if (!this.worldObj.isRemote) { this.collideWithNearbyEntities(); } - - this.worldObj.theProfiler.endSection(); } private void updateElytra() { @@ -2221,7 +2203,7 @@ public abstract class EntityLivingBase extends Entity { if (itm != null && itm.stackSize > 0) { Item item = itm.getItem(); if (item != null) { - float f2 = item.getHeldItemBrightnessEagler(); + float f2 = item.getHeldItemBrightnessEagler(itm); f = Math.min(f + f2 * 0.5f, 1.0f) + f2 * 0.5f; } } diff --git a/src/main/java/net/minecraft/entity/EntityMinecartCommandBlock.java b/src/game/java/net/minecraft/entity/EntityMinecartCommandBlock.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityMinecartCommandBlock.java rename to src/game/java/net/minecraft/entity/EntityMinecartCommandBlock.java diff --git a/src/main/java/net/minecraft/entity/EntitySpawnPlacementRegistry.java b/src/game/java/net/minecraft/entity/EntitySpawnPlacementRegistry.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntitySpawnPlacementRegistry.java rename to src/game/java/net/minecraft/entity/EntitySpawnPlacementRegistry.java diff --git a/src/main/java/net/minecraft/entity/EntityTracker.java b/src/game/java/net/minecraft/entity/EntityTracker.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityTracker.java rename to src/game/java/net/minecraft/entity/EntityTracker.java diff --git a/src/main/java/net/minecraft/entity/EntityTrackerEntry.java b/src/game/java/net/minecraft/entity/EntityTrackerEntry.java similarity index 100% rename from src/main/java/net/minecraft/entity/EntityTrackerEntry.java rename to src/game/java/net/minecraft/entity/EntityTrackerEntry.java diff --git a/src/main/java/net/minecraft/entity/EnumCreatureAttribute.java b/src/game/java/net/minecraft/entity/EnumCreatureAttribute.java similarity index 100% rename from src/main/java/net/minecraft/entity/EnumCreatureAttribute.java rename to src/game/java/net/minecraft/entity/EnumCreatureAttribute.java diff --git a/src/main/java/net/minecraft/entity/EnumCreatureType.java b/src/game/java/net/minecraft/entity/EnumCreatureType.java similarity index 100% rename from src/main/java/net/minecraft/entity/EnumCreatureType.java rename to src/game/java/net/minecraft/entity/EnumCreatureType.java diff --git a/src/main/java/net/minecraft/entity/IEntityLivingData.java b/src/game/java/net/minecraft/entity/IEntityLivingData.java similarity index 100% rename from src/main/java/net/minecraft/entity/IEntityLivingData.java rename to src/game/java/net/minecraft/entity/IEntityLivingData.java diff --git a/src/main/java/net/minecraft/entity/IEntityMultiPart.java b/src/game/java/net/minecraft/entity/IEntityMultiPart.java similarity index 100% rename from src/main/java/net/minecraft/entity/IEntityMultiPart.java rename to src/game/java/net/minecraft/entity/IEntityMultiPart.java diff --git a/src/main/java/net/minecraft/entity/IEntityOwnable.java b/src/game/java/net/minecraft/entity/IEntityOwnable.java similarity index 100% rename from src/main/java/net/minecraft/entity/IEntityOwnable.java rename to src/game/java/net/minecraft/entity/IEntityOwnable.java diff --git a/src/main/java/net/minecraft/entity/IMerchant.java b/src/game/java/net/minecraft/entity/IMerchant.java similarity index 100% rename from src/main/java/net/minecraft/entity/IMerchant.java rename to src/game/java/net/minecraft/entity/IMerchant.java diff --git a/src/main/java/net/minecraft/entity/INpc.java b/src/game/java/net/minecraft/entity/INpc.java similarity index 100% rename from src/main/java/net/minecraft/entity/INpc.java rename to src/game/java/net/minecraft/entity/INpc.java diff --git a/src/main/java/net/minecraft/entity/IProjectile.java b/src/game/java/net/minecraft/entity/IProjectile.java similarity index 100% rename from src/main/java/net/minecraft/entity/IProjectile.java rename to src/game/java/net/minecraft/entity/IProjectile.java diff --git a/src/main/java/net/minecraft/entity/IRangedAttackMob.java b/src/game/java/net/minecraft/entity/IRangedAttackMob.java similarity index 100% rename from src/main/java/net/minecraft/entity/IRangedAttackMob.java rename to src/game/java/net/minecraft/entity/IRangedAttackMob.java diff --git a/src/main/java/net/minecraft/entity/NpcMerchant.java b/src/game/java/net/minecraft/entity/NpcMerchant.java similarity index 100% rename from src/main/java/net/minecraft/entity/NpcMerchant.java rename to src/game/java/net/minecraft/entity/NpcMerchant.java diff --git a/src/main/java/net/minecraft/entity/SharedMonsterAttributes.java b/src/game/java/net/minecraft/entity/SharedMonsterAttributes.java similarity index 100% rename from src/main/java/net/minecraft/entity/SharedMonsterAttributes.java rename to src/game/java/net/minecraft/entity/SharedMonsterAttributes.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIArrowAttack.java b/src/game/java/net/minecraft/entity/ai/EntityAIArrowAttack.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIArrowAttack.java rename to src/game/java/net/minecraft/entity/ai/EntityAIArrowAttack.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIAttackOnCollide.java b/src/game/java/net/minecraft/entity/ai/EntityAIAttackOnCollide.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIAttackOnCollide.java rename to src/game/java/net/minecraft/entity/ai/EntityAIAttackOnCollide.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIAvoidEntity.java b/src/game/java/net/minecraft/entity/ai/EntityAIAvoidEntity.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIAvoidEntity.java rename to src/game/java/net/minecraft/entity/ai/EntityAIAvoidEntity.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIBase.java b/src/game/java/net/minecraft/entity/ai/EntityAIBase.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIBase.java rename to src/game/java/net/minecraft/entity/ai/EntityAIBase.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIBeg.java b/src/game/java/net/minecraft/entity/ai/EntityAIBeg.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIBeg.java rename to src/game/java/net/minecraft/entity/ai/EntityAIBeg.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIBreakDoor.java b/src/game/java/net/minecraft/entity/ai/EntityAIBreakDoor.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIBreakDoor.java rename to src/game/java/net/minecraft/entity/ai/EntityAIBreakDoor.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIControlledByPlayer.java b/src/game/java/net/minecraft/entity/ai/EntityAIControlledByPlayer.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIControlledByPlayer.java rename to src/game/java/net/minecraft/entity/ai/EntityAIControlledByPlayer.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAICreeperSwell.java b/src/game/java/net/minecraft/entity/ai/EntityAICreeperSwell.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAICreeperSwell.java rename to src/game/java/net/minecraft/entity/ai/EntityAICreeperSwell.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIDefendVillage.java b/src/game/java/net/minecraft/entity/ai/EntityAIDefendVillage.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIDefendVillage.java rename to src/game/java/net/minecraft/entity/ai/EntityAIDefendVillage.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIDoorInteract.java b/src/game/java/net/minecraft/entity/ai/EntityAIDoorInteract.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIDoorInteract.java rename to src/game/java/net/minecraft/entity/ai/EntityAIDoorInteract.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIEatGrass.java b/src/game/java/net/minecraft/entity/ai/EntityAIEatGrass.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIEatGrass.java rename to src/game/java/net/minecraft/entity/ai/EntityAIEatGrass.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIFindEntityNearest.java b/src/game/java/net/minecraft/entity/ai/EntityAIFindEntityNearest.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIFindEntityNearest.java rename to src/game/java/net/minecraft/entity/ai/EntityAIFindEntityNearest.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.java b/src/game/java/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.java rename to src/game/java/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIFleeSun.java b/src/game/java/net/minecraft/entity/ai/EntityAIFleeSun.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIFleeSun.java rename to src/game/java/net/minecraft/entity/ai/EntityAIFleeSun.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIFollowGolem.java b/src/game/java/net/minecraft/entity/ai/EntityAIFollowGolem.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIFollowGolem.java rename to src/game/java/net/minecraft/entity/ai/EntityAIFollowGolem.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIFollowOwner.java b/src/game/java/net/minecraft/entity/ai/EntityAIFollowOwner.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIFollowOwner.java rename to src/game/java/net/minecraft/entity/ai/EntityAIFollowOwner.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIFollowParent.java b/src/game/java/net/minecraft/entity/ai/EntityAIFollowParent.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIFollowParent.java rename to src/game/java/net/minecraft/entity/ai/EntityAIFollowParent.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIHarvestFarmland.java b/src/game/java/net/minecraft/entity/ai/EntityAIHarvestFarmland.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIHarvestFarmland.java rename to src/game/java/net/minecraft/entity/ai/EntityAIHarvestFarmland.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIHurtByTarget.java b/src/game/java/net/minecraft/entity/ai/EntityAIHurtByTarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIHurtByTarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAIHurtByTarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAILeapAtTarget.java b/src/game/java/net/minecraft/entity/ai/EntityAILeapAtTarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAILeapAtTarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAILeapAtTarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAILookAtTradePlayer.java b/src/game/java/net/minecraft/entity/ai/EntityAILookAtTradePlayer.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAILookAtTradePlayer.java rename to src/game/java/net/minecraft/entity/ai/EntityAILookAtTradePlayer.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAILookAtVillager.java b/src/game/java/net/minecraft/entity/ai/EntityAILookAtVillager.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAILookAtVillager.java rename to src/game/java/net/minecraft/entity/ai/EntityAILookAtVillager.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAILookIdle.java b/src/game/java/net/minecraft/entity/ai/EntityAILookIdle.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAILookIdle.java rename to src/game/java/net/minecraft/entity/ai/EntityAILookIdle.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIMate.java b/src/game/java/net/minecraft/entity/ai/EntityAIMate.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIMate.java rename to src/game/java/net/minecraft/entity/ai/EntityAIMate.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIMoveIndoors.java b/src/game/java/net/minecraft/entity/ai/EntityAIMoveIndoors.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIMoveIndoors.java rename to src/game/java/net/minecraft/entity/ai/EntityAIMoveIndoors.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIMoveThroughVillage.java b/src/game/java/net/minecraft/entity/ai/EntityAIMoveThroughVillage.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIMoveThroughVillage.java rename to src/game/java/net/minecraft/entity/ai/EntityAIMoveThroughVillage.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIMoveToBlock.java b/src/game/java/net/minecraft/entity/ai/EntityAIMoveToBlock.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIMoveToBlock.java rename to src/game/java/net/minecraft/entity/ai/EntityAIMoveToBlock.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.java b/src/game/java/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.java rename to src/game/java/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.java b/src/game/java/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAINearestAttackableTarget.java b/src/game/java/net/minecraft/entity/ai/EntityAINearestAttackableTarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAINearestAttackableTarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAINearestAttackableTarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIOcelotAttack.java b/src/game/java/net/minecraft/entity/ai/EntityAIOcelotAttack.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIOcelotAttack.java rename to src/game/java/net/minecraft/entity/ai/EntityAIOcelotAttack.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIOcelotSit.java b/src/game/java/net/minecraft/entity/ai/EntityAIOcelotSit.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIOcelotSit.java rename to src/game/java/net/minecraft/entity/ai/EntityAIOcelotSit.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIOpenDoor.java b/src/game/java/net/minecraft/entity/ai/EntityAIOpenDoor.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIOpenDoor.java rename to src/game/java/net/minecraft/entity/ai/EntityAIOpenDoor.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.java b/src/game/java/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.java b/src/game/java/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIPanic.java b/src/game/java/net/minecraft/entity/ai/EntityAIPanic.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIPanic.java rename to src/game/java/net/minecraft/entity/ai/EntityAIPanic.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIPlay.java b/src/game/java/net/minecraft/entity/ai/EntityAIPlay.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIPlay.java rename to src/game/java/net/minecraft/entity/ai/EntityAIPlay.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.java b/src/game/java/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.java rename to src/game/java/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIRestrictSun.java b/src/game/java/net/minecraft/entity/ai/EntityAIRestrictSun.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIRestrictSun.java rename to src/game/java/net/minecraft/entity/ai/EntityAIRestrictSun.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java b/src/game/java/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java rename to src/game/java/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAISit.java b/src/game/java/net/minecraft/entity/ai/EntityAISit.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAISit.java rename to src/game/java/net/minecraft/entity/ai/EntityAISit.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAISwimming.java b/src/game/java/net/minecraft/entity/ai/EntityAISwimming.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAISwimming.java rename to src/game/java/net/minecraft/entity/ai/EntityAISwimming.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAITarget.java b/src/game/java/net/minecraft/entity/ai/EntityAITarget.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAITarget.java rename to src/game/java/net/minecraft/entity/ai/EntityAITarget.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAITargetNonTamed.java b/src/game/java/net/minecraft/entity/ai/EntityAITargetNonTamed.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAITargetNonTamed.java rename to src/game/java/net/minecraft/entity/ai/EntityAITargetNonTamed.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAITasks.java b/src/game/java/net/minecraft/entity/ai/EntityAITasks.java similarity index 96% rename from src/main/java/net/minecraft/entity/ai/EntityAITasks.java rename to src/game/java/net/minecraft/entity/ai/EntityAITasks.java index 71a37e8..2242461 100644 --- a/src/main/java/net/minecraft/entity/ai/EntityAITasks.java +++ b/src/game/java/net/minecraft/entity/ai/EntityAITasks.java @@ -3,7 +3,6 @@ package net.minecraft.entity.ai; import com.google.common.collect.Lists; import java.util.Iterator; import java.util.List; -import net.minecraft.profiler.Profiler; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -49,7 +48,6 @@ public class EntityAITasks { * executed. */ private List executingTaskEntries = Lists.newArrayList(); - private final Profiler theProfiler; private int tickCount; private int tickRate = 3; @@ -57,8 +55,7 @@ public class EntityAITasks { this.theProfiler = profilerIn; } - /** - * + + /**+ * Add a now AITask. Args : priority, task */ public void addTask(int priority, EntityAIBase task) { @@ -89,7 +86,6 @@ public class EntityAITasks { } public void onUpdateTasks() { - this.theProfiler.startSection("goalSetup"); if (this.tickCount++ % this.tickRate == 0) { Iterator iterator = this.taskEntries.iterator(); @@ -133,14 +129,9 @@ public class EntityAITasks { } } - this.theProfiler.endSection(); - this.theProfiler.startSection("goalTick"); - for (int i = 0, l = this.executingTaskEntries.size(); i < l; ++i) { this.executingTaskEntries.get(i).action.updateTask(); } - - this.theProfiler.endSection(); } /** diff --git a/src/main/java/net/minecraft/entity/ai/EntityAITempt.java b/src/game/java/net/minecraft/entity/ai/EntityAITempt.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAITempt.java rename to src/game/java/net/minecraft/entity/ai/EntityAITempt.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAITradePlayer.java b/src/game/java/net/minecraft/entity/ai/EntityAITradePlayer.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAITradePlayer.java rename to src/game/java/net/minecraft/entity/ai/EntityAITradePlayer.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIVillagerInteract.java b/src/game/java/net/minecraft/entity/ai/EntityAIVillagerInteract.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIVillagerInteract.java rename to src/game/java/net/minecraft/entity/ai/EntityAIVillagerInteract.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIVillagerMate.java b/src/game/java/net/minecraft/entity/ai/EntityAIVillagerMate.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIVillagerMate.java rename to src/game/java/net/minecraft/entity/ai/EntityAIVillagerMate.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIWander.java b/src/game/java/net/minecraft/entity/ai/EntityAIWander.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIWander.java rename to src/game/java/net/minecraft/entity/ai/EntityAIWander.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIWatchClosest.java b/src/game/java/net/minecraft/entity/ai/EntityAIWatchClosest.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIWatchClosest.java rename to src/game/java/net/minecraft/entity/ai/EntityAIWatchClosest.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityAIWatchClosest2.java b/src/game/java/net/minecraft/entity/ai/EntityAIWatchClosest2.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityAIWatchClosest2.java rename to src/game/java/net/minecraft/entity/ai/EntityAIWatchClosest2.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityJumpHelper.java b/src/game/java/net/minecraft/entity/ai/EntityJumpHelper.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityJumpHelper.java rename to src/game/java/net/minecraft/entity/ai/EntityJumpHelper.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityLookHelper.java b/src/game/java/net/minecraft/entity/ai/EntityLookHelper.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityLookHelper.java rename to src/game/java/net/minecraft/entity/ai/EntityLookHelper.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityMinecartMobSpawner.java b/src/game/java/net/minecraft/entity/ai/EntityMinecartMobSpawner.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityMinecartMobSpawner.java rename to src/game/java/net/minecraft/entity/ai/EntityMinecartMobSpawner.java diff --git a/src/main/java/net/minecraft/entity/ai/EntityMoveHelper.java b/src/game/java/net/minecraft/entity/ai/EntityMoveHelper.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/EntityMoveHelper.java rename to src/game/java/net/minecraft/entity/ai/EntityMoveHelper.java diff --git a/src/main/java/net/minecraft/entity/ai/EntitySenses.java b/src/game/java/net/minecraft/entity/ai/EntitySenses.java similarity index 95% rename from src/main/java/net/minecraft/entity/ai/EntitySenses.java rename to src/game/java/net/minecraft/entity/ai/EntitySenses.java index f65ec2e..870f618 100644 --- a/src/main/java/net/minecraft/entity/ai/EntitySenses.java +++ b/src/game/java/net/minecraft/entity/ai/EntitySenses.java @@ -63,9 +63,7 @@ public class EntitySenses { } else if (this.unseenEntities.contains(entityIn)) { return false; } else { - this.entityObj.worldObj.theProfiler.startSection("canSee"); boolean flag = this.entityObj.canEntityBeSeen(entityIn); - this.entityObj.worldObj.theProfiler.endSection(); if (flag) { this.seenEntities.add(entityIn); } else { diff --git a/src/main/java/net/minecraft/entity/ai/RandomPositionGenerator.java b/src/game/java/net/minecraft/entity/ai/RandomPositionGenerator.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/RandomPositionGenerator.java rename to src/game/java/net/minecraft/entity/ai/RandomPositionGenerator.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/AttributeModifier.java b/src/game/java/net/minecraft/entity/ai/attributes/AttributeModifier.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/AttributeModifier.java rename to src/game/java/net/minecraft/entity/ai/attributes/AttributeModifier.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/BaseAttribute.java b/src/game/java/net/minecraft/entity/ai/attributes/BaseAttribute.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/BaseAttribute.java rename to src/game/java/net/minecraft/entity/ai/attributes/BaseAttribute.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/BaseAttributeMap.java b/src/game/java/net/minecraft/entity/ai/attributes/BaseAttributeMap.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/BaseAttributeMap.java rename to src/game/java/net/minecraft/entity/ai/attributes/BaseAttributeMap.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/IAttribute.java b/src/game/java/net/minecraft/entity/ai/attributes/IAttribute.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/IAttribute.java rename to src/game/java/net/minecraft/entity/ai/attributes/IAttribute.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/IAttributeInstance.java b/src/game/java/net/minecraft/entity/ai/attributes/IAttributeInstance.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/IAttributeInstance.java rename to src/game/java/net/minecraft/entity/ai/attributes/IAttributeInstance.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.java b/src/game/java/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.java rename to src/game/java/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/RangedAttribute.java b/src/game/java/net/minecraft/entity/ai/attributes/RangedAttribute.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/RangedAttribute.java rename to src/game/java/net/minecraft/entity/ai/attributes/RangedAttribute.java diff --git a/src/main/java/net/minecraft/entity/ai/attributes/ServersideAttributeMap.java b/src/game/java/net/minecraft/entity/ai/attributes/ServersideAttributeMap.java similarity index 100% rename from src/main/java/net/minecraft/entity/ai/attributes/ServersideAttributeMap.java rename to src/game/java/net/minecraft/entity/ai/attributes/ServersideAttributeMap.java diff --git a/src/main/java/net/minecraft/entity/boss/BossStatus.java b/src/game/java/net/minecraft/entity/boss/BossStatus.java similarity index 100% rename from src/main/java/net/minecraft/entity/boss/BossStatus.java rename to src/game/java/net/minecraft/entity/boss/BossStatus.java diff --git a/src/main/java/net/minecraft/entity/boss/EntityDragon.java b/src/game/java/net/minecraft/entity/boss/EntityDragon.java similarity index 100% rename from src/main/java/net/minecraft/entity/boss/EntityDragon.java rename to src/game/java/net/minecraft/entity/boss/EntityDragon.java diff --git a/src/main/java/net/minecraft/entity/boss/EntityDragonPart.java b/src/game/java/net/minecraft/entity/boss/EntityDragonPart.java similarity index 100% rename from src/main/java/net/minecraft/entity/boss/EntityDragonPart.java rename to src/game/java/net/minecraft/entity/boss/EntityDragonPart.java diff --git a/src/main/java/net/minecraft/entity/boss/EntityWither.java b/src/game/java/net/minecraft/entity/boss/EntityWither.java similarity index 100% rename from src/main/java/net/minecraft/entity/boss/EntityWither.java rename to src/game/java/net/minecraft/entity/boss/EntityWither.java diff --git a/src/main/java/net/minecraft/entity/boss/IBossDisplayData.java b/src/game/java/net/minecraft/entity/boss/IBossDisplayData.java similarity index 100% rename from src/main/java/net/minecraft/entity/boss/IBossDisplayData.java rename to src/game/java/net/minecraft/entity/boss/IBossDisplayData.java diff --git a/src/main/java/net/minecraft/entity/effect/EntityLightningBolt.java b/src/game/java/net/minecraft/entity/effect/EntityLightningBolt.java similarity index 100% rename from src/main/java/net/minecraft/entity/effect/EntityLightningBolt.java rename to src/game/java/net/minecraft/entity/effect/EntityLightningBolt.java diff --git a/src/main/java/net/minecraft/entity/effect/EntityWeatherEffect.java b/src/game/java/net/minecraft/entity/effect/EntityWeatherEffect.java similarity index 100% rename from src/main/java/net/minecraft/entity/effect/EntityWeatherEffect.java rename to src/game/java/net/minecraft/entity/effect/EntityWeatherEffect.java diff --git a/src/main/java/net/minecraft/entity/item/EntityArmorStand.java b/src/game/java/net/minecraft/entity/item/EntityArmorStand.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityArmorStand.java rename to src/game/java/net/minecraft/entity/item/EntityArmorStand.java diff --git a/src/main/java/net/minecraft/entity/item/EntityBoat.java b/src/game/java/net/minecraft/entity/item/EntityBoat.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityBoat.java rename to src/game/java/net/minecraft/entity/item/EntityBoat.java diff --git a/src/main/java/net/minecraft/entity/item/EntityEnderCrystal.java b/src/game/java/net/minecraft/entity/item/EntityEnderCrystal.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityEnderCrystal.java rename to src/game/java/net/minecraft/entity/item/EntityEnderCrystal.java diff --git a/src/main/java/net/minecraft/entity/item/EntityEnderEye.java b/src/game/java/net/minecraft/entity/item/EntityEnderEye.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityEnderEye.java rename to src/game/java/net/minecraft/entity/item/EntityEnderEye.java diff --git a/src/main/java/net/minecraft/entity/item/EntityEnderPearl.java b/src/game/java/net/minecraft/entity/item/EntityEnderPearl.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityEnderPearl.java rename to src/game/java/net/minecraft/entity/item/EntityEnderPearl.java diff --git a/src/main/java/net/minecraft/entity/item/EntityExpBottle.java b/src/game/java/net/minecraft/entity/item/EntityExpBottle.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityExpBottle.java rename to src/game/java/net/minecraft/entity/item/EntityExpBottle.java diff --git a/src/main/java/net/minecraft/entity/item/EntityFallingBlock.java b/src/game/java/net/minecraft/entity/item/EntityFallingBlock.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityFallingBlock.java rename to src/game/java/net/minecraft/entity/item/EntityFallingBlock.java diff --git a/src/main/java/net/minecraft/entity/item/EntityFireworkRocket.java b/src/game/java/net/minecraft/entity/item/EntityFireworkRocket.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityFireworkRocket.java rename to src/game/java/net/minecraft/entity/item/EntityFireworkRocket.java diff --git a/src/main/java/net/minecraft/entity/item/EntityItem.java b/src/game/java/net/minecraft/entity/item/EntityItem.java similarity index 99% rename from src/main/java/net/minecraft/entity/item/EntityItem.java rename to src/game/java/net/minecraft/entity/item/EntityItem.java index e89a828..f3c7977 100644 --- a/src/main/java/net/minecraft/entity/item/EntityItem.java +++ b/src/game/java/net/minecraft/entity/item/EntityItem.java @@ -507,7 +507,7 @@ public class EntityItem extends Entity { if (itm != null && itm.stackSize > 0) { Item item = itm.getItem(); if (item != null) { - float f2 = item.getHeldItemBrightnessEagler() * 0.75f; + float f2 = item.getHeldItemBrightnessEagler(itm) * 0.75f; f = Math.min(f + f2 * 0.5f, 1.0f) + f2 * 0.5f; } } diff --git a/src/main/java/net/minecraft/entity/item/EntityItemFrame.java b/src/game/java/net/minecraft/entity/item/EntityItemFrame.java similarity index 99% rename from src/main/java/net/minecraft/entity/item/EntityItemFrame.java rename to src/game/java/net/minecraft/entity/item/EntityItemFrame.java index 6b7b3e6..d1a6bbd 100644 --- a/src/main/java/net/minecraft/entity/item/EntityItemFrame.java +++ b/src/game/java/net/minecraft/entity/item/EntityItemFrame.java @@ -278,7 +278,7 @@ public class EntityItemFrame extends EntityHanging { if (itm != null && itm.stackSize > 0) { Item item = itm.getItem(); if (item != null) { - float f2 = item.getHeldItemBrightnessEagler() * 0.75f; + float f2 = item.getHeldItemBrightnessEagler(itm) * 0.75f; f = Math.min(f + f2 * 0.5f, 1.0f) + f2 * 0.5f; } } diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecart.java b/src/game/java/net/minecraft/entity/item/EntityMinecart.java similarity index 98% rename from src/main/java/net/minecraft/entity/item/EntityMinecart.java rename to src/game/java/net/minecraft/entity/item/EntityMinecart.java index 0de1e26..a451e32 100644 --- a/src/main/java/net/minecraft/entity/item/EntityMinecart.java +++ b/src/game/java/net/minecraft/entity/item/EntityMinecart.java @@ -268,7 +268,6 @@ public abstract class EntityMinecart extends Entity implements IWorldNameable { } if (!this.worldObj.isRemote && this.worldObj instanceof WorldServer) { - this.worldObj.theProfiler.startSection("portal"); MinecraftServer minecraftserver = ((WorldServer) this.worldObj).getMinecraftServer(); int i = this.getMaxInPortalTime(); if (this.inPortal) { @@ -301,8 +300,6 @@ public abstract class EntityMinecart extends Entity implements IWorldNameable { if (this.timeUntilPortal > 0) { --this.timeUntilPortal; } - - this.worldObj.theProfiler.endSection(); } if (this.worldObj.isRemote) { @@ -978,8 +975,15 @@ public abstract class EntityMinecart extends Entity implements IWorldNameable { return this.entityName != null ? this.entityName : super.getName(); } - /** - * + + public String getNameProfanityFilter() { + /**+ + * Gets the name of this command sender (usually username, but + * possibly "Rcon") + */ + return getName(); + } + + /**+ * Returns true if this thing is named */ public boolean hasCustomName() { @@ -1010,6 +1014,14 @@ public abstract class EntityMinecart extends Entity implements IWorldNameable { } } + public IChatComponent getDisplayNameProfanityFilter() { + /**+ + * Get the formatted ChatComponent that will be used for the + * sender's username in chat + */ + return getDisplayName(); + } + public static enum EnumMinecartType { RIDEABLE(0, "MinecartRideable"), CHEST(1, "MinecartChest"), FURNACE(2, "MinecartFurnace"), TNT(3, "MinecartTNT"), SPAWNER(4, "MinecartSpawner"), HOPPER(5, "MinecartHopper"), diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecartChest.java b/src/game/java/net/minecraft/entity/item/EntityMinecartChest.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityMinecartChest.java rename to src/game/java/net/minecraft/entity/item/EntityMinecartChest.java diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecartContainer.java b/src/game/java/net/minecraft/entity/item/EntityMinecartContainer.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityMinecartContainer.java rename to src/game/java/net/minecraft/entity/item/EntityMinecartContainer.java diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecartEmpty.java b/src/game/java/net/minecraft/entity/item/EntityMinecartEmpty.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityMinecartEmpty.java rename to src/game/java/net/minecraft/entity/item/EntityMinecartEmpty.java diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecartFurnace.java b/src/game/java/net/minecraft/entity/item/EntityMinecartFurnace.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityMinecartFurnace.java rename to src/game/java/net/minecraft/entity/item/EntityMinecartFurnace.java diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecartHopper.java b/src/game/java/net/minecraft/entity/item/EntityMinecartHopper.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityMinecartHopper.java rename to src/game/java/net/minecraft/entity/item/EntityMinecartHopper.java diff --git a/src/main/java/net/minecraft/entity/item/EntityMinecartTNT.java b/src/game/java/net/minecraft/entity/item/EntityMinecartTNT.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityMinecartTNT.java rename to src/game/java/net/minecraft/entity/item/EntityMinecartTNT.java diff --git a/src/main/java/net/minecraft/entity/item/EntityPainting.java b/src/game/java/net/minecraft/entity/item/EntityPainting.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityPainting.java rename to src/game/java/net/minecraft/entity/item/EntityPainting.java diff --git a/src/main/java/net/minecraft/entity/item/EntityTNTPrimed.java b/src/game/java/net/minecraft/entity/item/EntityTNTPrimed.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityTNTPrimed.java rename to src/game/java/net/minecraft/entity/item/EntityTNTPrimed.java diff --git a/src/main/java/net/minecraft/entity/item/EntityXPOrb.java b/src/game/java/net/minecraft/entity/item/EntityXPOrb.java similarity index 100% rename from src/main/java/net/minecraft/entity/item/EntityXPOrb.java rename to src/game/java/net/minecraft/entity/item/EntityXPOrb.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityBlaze.java b/src/game/java/net/minecraft/entity/monster/EntityBlaze.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityBlaze.java rename to src/game/java/net/minecraft/entity/monster/EntityBlaze.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityCaveSpider.java b/src/game/java/net/minecraft/entity/monster/EntityCaveSpider.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityCaveSpider.java rename to src/game/java/net/minecraft/entity/monster/EntityCaveSpider.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityCreeper.java b/src/game/java/net/minecraft/entity/monster/EntityCreeper.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityCreeper.java rename to src/game/java/net/minecraft/entity/monster/EntityCreeper.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityEnderman.java b/src/game/java/net/minecraft/entity/monster/EntityEnderman.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityEnderman.java rename to src/game/java/net/minecraft/entity/monster/EntityEnderman.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityEndermite.java b/src/game/java/net/minecraft/entity/monster/EntityEndermite.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityEndermite.java rename to src/game/java/net/minecraft/entity/monster/EntityEndermite.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityGhast.java b/src/game/java/net/minecraft/entity/monster/EntityGhast.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityGhast.java rename to src/game/java/net/minecraft/entity/monster/EntityGhast.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityGiantZombie.java b/src/game/java/net/minecraft/entity/monster/EntityGiantZombie.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityGiantZombie.java rename to src/game/java/net/minecraft/entity/monster/EntityGiantZombie.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityGolem.java b/src/game/java/net/minecraft/entity/monster/EntityGolem.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityGolem.java rename to src/game/java/net/minecraft/entity/monster/EntityGolem.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityGuardian.java b/src/game/java/net/minecraft/entity/monster/EntityGuardian.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityGuardian.java rename to src/game/java/net/minecraft/entity/monster/EntityGuardian.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityIronGolem.java b/src/game/java/net/minecraft/entity/monster/EntityIronGolem.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityIronGolem.java rename to src/game/java/net/minecraft/entity/monster/EntityIronGolem.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityMagmaCube.java b/src/game/java/net/minecraft/entity/monster/EntityMagmaCube.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityMagmaCube.java rename to src/game/java/net/minecraft/entity/monster/EntityMagmaCube.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityMob.java b/src/game/java/net/minecraft/entity/monster/EntityMob.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityMob.java rename to src/game/java/net/minecraft/entity/monster/EntityMob.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityPigZombie.java b/src/game/java/net/minecraft/entity/monster/EntityPigZombie.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityPigZombie.java rename to src/game/java/net/minecraft/entity/monster/EntityPigZombie.java diff --git a/src/main/java/net/minecraft/entity/monster/EntitySilverfish.java b/src/game/java/net/minecraft/entity/monster/EntitySilverfish.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntitySilverfish.java rename to src/game/java/net/minecraft/entity/monster/EntitySilverfish.java diff --git a/src/main/java/net/minecraft/entity/monster/EntitySkeleton.java b/src/game/java/net/minecraft/entity/monster/EntitySkeleton.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntitySkeleton.java rename to src/game/java/net/minecraft/entity/monster/EntitySkeleton.java diff --git a/src/main/java/net/minecraft/entity/monster/EntitySlime.java b/src/game/java/net/minecraft/entity/monster/EntitySlime.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntitySlime.java rename to src/game/java/net/minecraft/entity/monster/EntitySlime.java diff --git a/src/main/java/net/minecraft/entity/monster/EntitySnowman.java b/src/game/java/net/minecraft/entity/monster/EntitySnowman.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntitySnowman.java rename to src/game/java/net/minecraft/entity/monster/EntitySnowman.java diff --git a/src/main/java/net/minecraft/entity/monster/EntitySpider.java b/src/game/java/net/minecraft/entity/monster/EntitySpider.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntitySpider.java rename to src/game/java/net/minecraft/entity/monster/EntitySpider.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityWitch.java b/src/game/java/net/minecraft/entity/monster/EntityWitch.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityWitch.java rename to src/game/java/net/minecraft/entity/monster/EntityWitch.java diff --git a/src/main/java/net/minecraft/entity/monster/EntityZombie.java b/src/game/java/net/minecraft/entity/monster/EntityZombie.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/EntityZombie.java rename to src/game/java/net/minecraft/entity/monster/EntityZombie.java diff --git a/src/main/java/net/minecraft/entity/monster/IMob.java b/src/game/java/net/minecraft/entity/monster/IMob.java similarity index 100% rename from src/main/java/net/minecraft/entity/monster/IMob.java rename to src/game/java/net/minecraft/entity/monster/IMob.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityAmbientCreature.java b/src/game/java/net/minecraft/entity/passive/EntityAmbientCreature.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityAmbientCreature.java rename to src/game/java/net/minecraft/entity/passive/EntityAmbientCreature.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityAnimal.java b/src/game/java/net/minecraft/entity/passive/EntityAnimal.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityAnimal.java rename to src/game/java/net/minecraft/entity/passive/EntityAnimal.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityBat.java b/src/game/java/net/minecraft/entity/passive/EntityBat.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityBat.java rename to src/game/java/net/minecraft/entity/passive/EntityBat.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityChicken.java b/src/game/java/net/minecraft/entity/passive/EntityChicken.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityChicken.java rename to src/game/java/net/minecraft/entity/passive/EntityChicken.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityCow.java b/src/game/java/net/minecraft/entity/passive/EntityCow.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityCow.java rename to src/game/java/net/minecraft/entity/passive/EntityCow.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityHorse.java b/src/game/java/net/minecraft/entity/passive/EntityHorse.java similarity index 99% rename from src/main/java/net/minecraft/entity/passive/EntityHorse.java rename to src/game/java/net/minecraft/entity/passive/EntityHorse.java index 0cbffe2..67ee856 100644 --- a/src/main/java/net/minecraft/entity/passive/EntityHorse.java +++ b/src/game/java/net/minecraft/entity/passive/EntityHorse.java @@ -174,8 +174,16 @@ public class EntityHorse extends EntityAnimal implements IInvBasic { * possibly "Rcon") */ public String getName() { + return getNameImpl(false); + } + + public String getNameProfanityFilter() { + return getNameImpl(true); + } + + private String getNameImpl(boolean filter) { if (this.hasCustomName()) { - return this.getCustomNameTag(); + return filter ? this.getCustomNameTagProfanityFilter() : this.getCustomNameTag(); } else { int i = this.getHorseType(); switch (i) { diff --git a/src/main/java/net/minecraft/entity/passive/EntityMooshroom.java b/src/game/java/net/minecraft/entity/passive/EntityMooshroom.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityMooshroom.java rename to src/game/java/net/minecraft/entity/passive/EntityMooshroom.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityOcelot.java b/src/game/java/net/minecraft/entity/passive/EntityOcelot.java similarity index 97% rename from src/main/java/net/minecraft/entity/passive/EntityOcelot.java rename to src/game/java/net/minecraft/entity/passive/EntityOcelot.java index d10ea3f..6c4933e 100644 --- a/src/main/java/net/minecraft/entity/passive/EntityOcelot.java +++ b/src/game/java/net/minecraft/entity/passive/EntityOcelot.java @@ -332,8 +332,17 @@ public class EntityOcelot extends EntityTameable { * possibly "Rcon") */ public String getName() { - return this.hasCustomName() ? this.getCustomNameTag() - : (this.isTamed() ? StatCollector.translateToLocal("entity.Cat.name") : super.getName()); + return getNameImpl(false); + } + + public String getNameProfanityFilter() { + return getNameImpl(true); + } + + private String getNameImpl(boolean filter) { + return this.hasCustomName() ? (filter ? this.getCustomNameTagProfanityFilter() : this.getCustomNameTag()) + : (this.isTamed() ? StatCollector.translateToLocal("entity.Cat.name") + : (filter ? super.getNameProfanityFilter() : super.getName())); } public void setTamed(boolean flag) { diff --git a/src/main/java/net/minecraft/entity/passive/EntityPig.java b/src/game/java/net/minecraft/entity/passive/EntityPig.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityPig.java rename to src/game/java/net/minecraft/entity/passive/EntityPig.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityRabbit.java b/src/game/java/net/minecraft/entity/passive/EntityRabbit.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityRabbit.java rename to src/game/java/net/minecraft/entity/passive/EntityRabbit.java diff --git a/src/main/java/net/minecraft/entity/passive/EntitySheep.java b/src/game/java/net/minecraft/entity/passive/EntitySheep.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntitySheep.java rename to src/game/java/net/minecraft/entity/passive/EntitySheep.java diff --git a/src/main/java/net/minecraft/entity/passive/EntitySquid.java b/src/game/java/net/minecraft/entity/passive/EntitySquid.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntitySquid.java rename to src/game/java/net/minecraft/entity/passive/EntitySquid.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityTameable.java b/src/game/java/net/minecraft/entity/passive/EntityTameable.java similarity index 96% rename from src/main/java/net/minecraft/entity/passive/EntityTameable.java rename to src/game/java/net/minecraft/entity/passive/EntityTameable.java index dd1e693..c2c5b52 100644 --- a/src/main/java/net/minecraft/entity/passive/EntityTameable.java +++ b/src/game/java/net/minecraft/entity/passive/EntityTameable.java @@ -1,5 +1,7 @@ package net.minecraft.entity.passive; +import org.apache.commons.lang3.StringUtils; + import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.minecraft.entity.EntityLivingBase; @@ -183,11 +185,15 @@ public abstract class EntityTameable extends EntityAnimal implements IEntityOwna } public EntityLivingBase getOwner() { + String ownerName = this.getOwnerId(); + if (StringUtils.isEmpty(ownerName)) { + return null; + } try { - EaglercraftUUID uuid = EaglercraftUUID.fromString(this.getOwnerId()); + EaglercraftUUID uuid = EaglercraftUUID.fromString(ownerName); return uuid == null ? null : this.worldObj.getPlayerEntityByUUID(uuid); } catch (IllegalArgumentException var2) { - return null; + return this.worldObj.getPlayerEntityByName(ownerName); } } diff --git a/src/main/java/net/minecraft/entity/passive/EntityVillager.java b/src/game/java/net/minecraft/entity/passive/EntityVillager.java similarity index 99% rename from src/main/java/net/minecraft/entity/passive/EntityVillager.java rename to src/game/java/net/minecraft/entity/passive/EntityVillager.java index b35f9e9..a0c4a7e 100644 --- a/src/main/java/net/minecraft/entity/passive/EntityVillager.java +++ b/src/game/java/net/minecraft/entity/passive/EntityVillager.java @@ -751,7 +751,15 @@ public class EntityVillager extends EntityAgeable implements IMerchant, INpc { * sender's username in chat */ public IChatComponent getDisplayName() { - String s = this.getCustomNameTag(); + return getDisplayNameImpl(false); + } + + public IChatComponent getDisplayNameProfanityFilter() { + return getDisplayNameImpl(true); + } + + private IChatComponent getDisplayNameImpl(boolean filter) { + String s = filter ? this.getCustomNameTagProfanityFilter() : this.getCustomNameTag(); if (s != null && s.length() > 0) { ChatComponentText chatcomponenttext = new ChatComponentText(s); chatcomponenttext.getChatStyle().setChatHoverEvent(this.getHoverEvent()); @@ -805,7 +813,7 @@ public class EntityVillager extends EntityAgeable implements IMerchant, INpc { chatcomponenttranslation.getChatStyle().setInsertion(this.getUniqueID().toString()); return chatcomponenttranslation; } else { - return super.getDisplayName(); + return filter ? super.getDisplayNameProfanityFilter() : super.getDisplayName(); } } } diff --git a/src/main/java/net/minecraft/entity/passive/EntityWaterMob.java b/src/game/java/net/minecraft/entity/passive/EntityWaterMob.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityWaterMob.java rename to src/game/java/net/minecraft/entity/passive/EntityWaterMob.java diff --git a/src/main/java/net/minecraft/entity/passive/EntityWolf.java b/src/game/java/net/minecraft/entity/passive/EntityWolf.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/EntityWolf.java rename to src/game/java/net/minecraft/entity/passive/EntityWolf.java diff --git a/src/main/java/net/minecraft/entity/passive/IAnimals.java b/src/game/java/net/minecraft/entity/passive/IAnimals.java similarity index 100% rename from src/main/java/net/minecraft/entity/passive/IAnimals.java rename to src/game/java/net/minecraft/entity/passive/IAnimals.java diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayer.java b/src/game/java/net/minecraft/entity/player/EntityPlayer.java similarity index 99% rename from src/main/java/net/minecraft/entity/player/EntityPlayer.java rename to src/game/java/net/minecraft/entity/player/EntityPlayer.java index eeafc6d..d608050 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayer.java +++ b/src/game/java/net/minecraft/entity/player/EntityPlayer.java @@ -226,8 +226,16 @@ public abstract class EntityPlayer extends EntityLivingBase implements ICommandS return this.itemInUse != null; } - /** - * + + public boolean getItemShouldUseOnTouchEagler() { + if (itemInUse != null) { + return itemInUse.getItem().shouldUseOnTouchEagler(itemInUse); + } else { + ItemStack st = getHeldItem(); + return st != null && st.getItem().shouldUseOnTouchEagler(st); + } + } + + /**+ * gets the duration for how long the current itemInUse has been * in use */ diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java b/src/game/java/net/minecraft/entity/player/EntityPlayerMP.java similarity index 99% rename from src/main/java/net/minecraft/entity/player/EntityPlayerMP.java rename to src/game/java/net/minecraft/entity/player/EntityPlayerMP.java index 3171224..91d573d 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java +++ b/src/game/java/net/minecraft/entity/player/EntityPlayerMP.java @@ -96,6 +96,8 @@ import net.minecraft.world.WorldServer; import net.minecraft.world.WorldSettings; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.chunk.Chunk; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -178,9 +180,8 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting { private int respawnInvulnerabilityTicks = 60; private EntityPlayer.EnumChatVisibility chatVisibility; private boolean chatColours = true; - private long playerLastActiveTime = System.currentTimeMillis(); - /** - * + + private long playerLastActiveTime = EagRuntime.steadyTimeMillis(); + /**+ * The entity the player is currently spectating through. */ private Entity spectatingEntity = null; @@ -189,6 +190,7 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting { public int ping; public boolean playerConqueredTheEnd; public byte[] updateCertificate = null; + public EaglercraftUUID clientBrandUUID = null; public EntityPlayerMP(MinecraftServer server, WorldServer worldIn, GameProfile profile, ItemInWorldManager interactionManager) { diff --git a/src/main/java/net/minecraft/entity/player/EnumPlayerModelParts.java b/src/game/java/net/minecraft/entity/player/EnumPlayerModelParts.java similarity index 100% rename from src/main/java/net/minecraft/entity/player/EnumPlayerModelParts.java rename to src/game/java/net/minecraft/entity/player/EnumPlayerModelParts.java diff --git a/src/main/java/net/minecraft/entity/player/InventoryPlayer.java b/src/game/java/net/minecraft/entity/player/InventoryPlayer.java similarity index 100% rename from src/main/java/net/minecraft/entity/player/InventoryPlayer.java rename to src/game/java/net/minecraft/entity/player/InventoryPlayer.java diff --git a/src/main/java/net/minecraft/entity/player/PlayerCapabilities.java b/src/game/java/net/minecraft/entity/player/PlayerCapabilities.java similarity index 100% rename from src/main/java/net/minecraft/entity/player/PlayerCapabilities.java rename to src/game/java/net/minecraft/entity/player/PlayerCapabilities.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityArrow.java b/src/game/java/net/minecraft/entity/projectile/EntityArrow.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityArrow.java rename to src/game/java/net/minecraft/entity/projectile/EntityArrow.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityEgg.java b/src/game/java/net/minecraft/entity/projectile/EntityEgg.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityEgg.java rename to src/game/java/net/minecraft/entity/projectile/EntityEgg.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityFireball.java b/src/game/java/net/minecraft/entity/projectile/EntityFireball.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityFireball.java rename to src/game/java/net/minecraft/entity/projectile/EntityFireball.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityFishHook.java b/src/game/java/net/minecraft/entity/projectile/EntityFishHook.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityFishHook.java rename to src/game/java/net/minecraft/entity/projectile/EntityFishHook.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityLargeFireball.java b/src/game/java/net/minecraft/entity/projectile/EntityLargeFireball.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityLargeFireball.java rename to src/game/java/net/minecraft/entity/projectile/EntityLargeFireball.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityPotion.java b/src/game/java/net/minecraft/entity/projectile/EntityPotion.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityPotion.java rename to src/game/java/net/minecraft/entity/projectile/EntityPotion.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntitySmallFireball.java b/src/game/java/net/minecraft/entity/projectile/EntitySmallFireball.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntitySmallFireball.java rename to src/game/java/net/minecraft/entity/projectile/EntitySmallFireball.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntitySnowball.java b/src/game/java/net/minecraft/entity/projectile/EntitySnowball.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntitySnowball.java rename to src/game/java/net/minecraft/entity/projectile/EntitySnowball.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityThrowable.java b/src/game/java/net/minecraft/entity/projectile/EntityThrowable.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityThrowable.java rename to src/game/java/net/minecraft/entity/projectile/EntityThrowable.java diff --git a/src/main/java/net/minecraft/entity/projectile/EntityWitherSkull.java b/src/game/java/net/minecraft/entity/projectile/EntityWitherSkull.java similarity index 100% rename from src/main/java/net/minecraft/entity/projectile/EntityWitherSkull.java rename to src/game/java/net/minecraft/entity/projectile/EntityWitherSkull.java diff --git a/src/main/java/net/minecraft/event/ClickEvent.java b/src/game/java/net/minecraft/event/ClickEvent.java similarity index 100% rename from src/main/java/net/minecraft/event/ClickEvent.java rename to src/game/java/net/minecraft/event/ClickEvent.java diff --git a/src/main/java/net/minecraft/event/HoverEvent.java b/src/game/java/net/minecraft/event/HoverEvent.java similarity index 100% rename from src/main/java/net/minecraft/event/HoverEvent.java rename to src/game/java/net/minecraft/event/HoverEvent.java diff --git a/src/main/java/net/minecraft/init/Blocks.java b/src/game/java/net/minecraft/init/Blocks.java similarity index 100% rename from src/main/java/net/minecraft/init/Blocks.java rename to src/game/java/net/minecraft/init/Blocks.java diff --git a/src/main/java/net/minecraft/init/Bootstrap.java b/src/game/java/net/minecraft/init/Bootstrap.java similarity index 100% rename from src/main/java/net/minecraft/init/Bootstrap.java rename to src/game/java/net/minecraft/init/Bootstrap.java diff --git a/src/main/java/net/minecraft/init/Items.java b/src/game/java/net/minecraft/init/Items.java similarity index 100% rename from src/main/java/net/minecraft/init/Items.java rename to src/game/java/net/minecraft/init/Items.java diff --git a/src/main/java/net/minecraft/inventory/AnimalChest.java b/src/game/java/net/minecraft/inventory/AnimalChest.java similarity index 100% rename from src/main/java/net/minecraft/inventory/AnimalChest.java rename to src/game/java/net/minecraft/inventory/AnimalChest.java diff --git a/src/main/java/net/minecraft/inventory/Container.java b/src/game/java/net/minecraft/inventory/Container.java similarity index 100% rename from src/main/java/net/minecraft/inventory/Container.java rename to src/game/java/net/minecraft/inventory/Container.java diff --git a/src/main/java/net/minecraft/inventory/ContainerBeacon.java b/src/game/java/net/minecraft/inventory/ContainerBeacon.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerBeacon.java rename to src/game/java/net/minecraft/inventory/ContainerBeacon.java diff --git a/src/main/java/net/minecraft/inventory/ContainerBrewingStand.java b/src/game/java/net/minecraft/inventory/ContainerBrewingStand.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerBrewingStand.java rename to src/game/java/net/minecraft/inventory/ContainerBrewingStand.java diff --git a/src/main/java/net/minecraft/inventory/ContainerChest.java b/src/game/java/net/minecraft/inventory/ContainerChest.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerChest.java rename to src/game/java/net/minecraft/inventory/ContainerChest.java diff --git a/src/main/java/net/minecraft/inventory/ContainerDispenser.java b/src/game/java/net/minecraft/inventory/ContainerDispenser.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerDispenser.java rename to src/game/java/net/minecraft/inventory/ContainerDispenser.java diff --git a/src/main/java/net/minecraft/inventory/ContainerEnchantment.java b/src/game/java/net/minecraft/inventory/ContainerEnchantment.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerEnchantment.java rename to src/game/java/net/minecraft/inventory/ContainerEnchantment.java diff --git a/src/main/java/net/minecraft/inventory/ContainerFurnace.java b/src/game/java/net/minecraft/inventory/ContainerFurnace.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerFurnace.java rename to src/game/java/net/minecraft/inventory/ContainerFurnace.java diff --git a/src/main/java/net/minecraft/inventory/ContainerHopper.java b/src/game/java/net/minecraft/inventory/ContainerHopper.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerHopper.java rename to src/game/java/net/minecraft/inventory/ContainerHopper.java diff --git a/src/main/java/net/minecraft/inventory/ContainerHorseInventory.java b/src/game/java/net/minecraft/inventory/ContainerHorseInventory.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerHorseInventory.java rename to src/game/java/net/minecraft/inventory/ContainerHorseInventory.java diff --git a/src/main/java/net/minecraft/inventory/ContainerMerchant.java b/src/game/java/net/minecraft/inventory/ContainerMerchant.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerMerchant.java rename to src/game/java/net/minecraft/inventory/ContainerMerchant.java diff --git a/src/main/java/net/minecraft/inventory/ContainerPlayer.java b/src/game/java/net/minecraft/inventory/ContainerPlayer.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerPlayer.java rename to src/game/java/net/minecraft/inventory/ContainerPlayer.java diff --git a/src/main/java/net/minecraft/inventory/ContainerRepair.java b/src/game/java/net/minecraft/inventory/ContainerRepair.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerRepair.java rename to src/game/java/net/minecraft/inventory/ContainerRepair.java diff --git a/src/main/java/net/minecraft/inventory/ContainerWorkbench.java b/src/game/java/net/minecraft/inventory/ContainerWorkbench.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ContainerWorkbench.java rename to src/game/java/net/minecraft/inventory/ContainerWorkbench.java diff --git a/src/main/java/net/minecraft/inventory/EntityEquipmentSlot.java b/src/game/java/net/minecraft/inventory/EntityEquipmentSlot.java similarity index 100% rename from src/main/java/net/minecraft/inventory/EntityEquipmentSlot.java rename to src/game/java/net/minecraft/inventory/EntityEquipmentSlot.java diff --git a/src/main/java/net/minecraft/inventory/ICrafting.java b/src/game/java/net/minecraft/inventory/ICrafting.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ICrafting.java rename to src/game/java/net/minecraft/inventory/ICrafting.java diff --git a/src/main/java/net/minecraft/inventory/IInvBasic.java b/src/game/java/net/minecraft/inventory/IInvBasic.java similarity index 100% rename from src/main/java/net/minecraft/inventory/IInvBasic.java rename to src/game/java/net/minecraft/inventory/IInvBasic.java diff --git a/src/main/java/net/minecraft/inventory/IInventory.java b/src/game/java/net/minecraft/inventory/IInventory.java similarity index 100% rename from src/main/java/net/minecraft/inventory/IInventory.java rename to src/game/java/net/minecraft/inventory/IInventory.java diff --git a/src/main/java/net/minecraft/inventory/ISidedInventory.java b/src/game/java/net/minecraft/inventory/ISidedInventory.java similarity index 100% rename from src/main/java/net/minecraft/inventory/ISidedInventory.java rename to src/game/java/net/minecraft/inventory/ISidedInventory.java diff --git a/src/main/java/net/minecraft/inventory/InventoryBasic.java b/src/game/java/net/minecraft/inventory/InventoryBasic.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryBasic.java rename to src/game/java/net/minecraft/inventory/InventoryBasic.java diff --git a/src/main/java/net/minecraft/inventory/InventoryCraftResult.java b/src/game/java/net/minecraft/inventory/InventoryCraftResult.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryCraftResult.java rename to src/game/java/net/minecraft/inventory/InventoryCraftResult.java diff --git a/src/main/java/net/minecraft/inventory/InventoryCrafting.java b/src/game/java/net/minecraft/inventory/InventoryCrafting.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryCrafting.java rename to src/game/java/net/minecraft/inventory/InventoryCrafting.java diff --git a/src/main/java/net/minecraft/inventory/InventoryEnderChest.java b/src/game/java/net/minecraft/inventory/InventoryEnderChest.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryEnderChest.java rename to src/game/java/net/minecraft/inventory/InventoryEnderChest.java diff --git a/src/main/java/net/minecraft/inventory/InventoryHelper.java b/src/game/java/net/minecraft/inventory/InventoryHelper.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryHelper.java rename to src/game/java/net/minecraft/inventory/InventoryHelper.java diff --git a/src/main/java/net/minecraft/inventory/InventoryLargeChest.java b/src/game/java/net/minecraft/inventory/InventoryLargeChest.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryLargeChest.java rename to src/game/java/net/minecraft/inventory/InventoryLargeChest.java diff --git a/src/main/java/net/minecraft/inventory/InventoryMerchant.java b/src/game/java/net/minecraft/inventory/InventoryMerchant.java similarity index 100% rename from src/main/java/net/minecraft/inventory/InventoryMerchant.java rename to src/game/java/net/minecraft/inventory/InventoryMerchant.java diff --git a/src/main/java/net/minecraft/inventory/Slot.java b/src/game/java/net/minecraft/inventory/Slot.java similarity index 100% rename from src/main/java/net/minecraft/inventory/Slot.java rename to src/game/java/net/minecraft/inventory/Slot.java diff --git a/src/main/java/net/minecraft/inventory/SlotCrafting.java b/src/game/java/net/minecraft/inventory/SlotCrafting.java similarity index 100% rename from src/main/java/net/minecraft/inventory/SlotCrafting.java rename to src/game/java/net/minecraft/inventory/SlotCrafting.java diff --git a/src/main/java/net/minecraft/inventory/SlotFurnaceFuel.java b/src/game/java/net/minecraft/inventory/SlotFurnaceFuel.java similarity index 100% rename from src/main/java/net/minecraft/inventory/SlotFurnaceFuel.java rename to src/game/java/net/minecraft/inventory/SlotFurnaceFuel.java diff --git a/src/main/java/net/minecraft/inventory/SlotFurnaceOutput.java b/src/game/java/net/minecraft/inventory/SlotFurnaceOutput.java similarity index 100% rename from src/main/java/net/minecraft/inventory/SlotFurnaceOutput.java rename to src/game/java/net/minecraft/inventory/SlotFurnaceOutput.java diff --git a/src/main/java/net/minecraft/inventory/SlotMerchantResult.java b/src/game/java/net/minecraft/inventory/SlotMerchantResult.java similarity index 100% rename from src/main/java/net/minecraft/inventory/SlotMerchantResult.java rename to src/game/java/net/minecraft/inventory/SlotMerchantResult.java diff --git a/src/main/java/net/minecraft/item/EnumAction.java b/src/game/java/net/minecraft/item/EnumAction.java similarity index 100% rename from src/main/java/net/minecraft/item/EnumAction.java rename to src/game/java/net/minecraft/item/EnumAction.java diff --git a/src/main/java/net/minecraft/item/EnumDyeColor.java b/src/game/java/net/minecraft/item/EnumDyeColor.java similarity index 100% rename from src/main/java/net/minecraft/item/EnumDyeColor.java rename to src/game/java/net/minecraft/item/EnumDyeColor.java diff --git a/src/main/java/net/minecraft/item/EnumRarity.java b/src/game/java/net/minecraft/item/EnumRarity.java similarity index 100% rename from src/main/java/net/minecraft/item/EnumRarity.java rename to src/game/java/net/minecraft/item/EnumRarity.java diff --git a/src/main/java/net/minecraft/item/IItemPropertyGetter.java b/src/game/java/net/minecraft/item/IItemPropertyGetter.java similarity index 100% rename from src/main/java/net/minecraft/item/IItemPropertyGetter.java rename to src/game/java/net/minecraft/item/IItemPropertyGetter.java diff --git a/src/main/java/net/minecraft/item/Item.java b/src/game/java/net/minecraft/item/Item.java similarity index 99% rename from src/main/java/net/minecraft/item/Item.java rename to src/game/java/net/minecraft/item/Item.java index 644f370..2bbe746 100644 --- a/src/main/java/net/minecraft/item/Item.java +++ b/src/game/java/net/minecraft/item/Item.java @@ -1244,7 +1244,15 @@ public class Item { } } - public float getHeldItemBrightnessEagler() { + public float getHeldItemBrightnessEagler(ItemStack itemStack) { return 0.0f; } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + /**+ + * returns the action that specifies what animation to play when + * the items is being used + */ + return getItemUseAction(itemStack) != EnumAction.NONE; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemAnvilBlock.java b/src/game/java/net/minecraft/item/ItemAnvilBlock.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemAnvilBlock.java rename to src/game/java/net/minecraft/item/ItemAnvilBlock.java diff --git a/src/main/java/net/minecraft/item/ItemAppleGold.java b/src/game/java/net/minecraft/item/ItemAppleGold.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemAppleGold.java rename to src/game/java/net/minecraft/item/ItemAppleGold.java diff --git a/src/main/java/net/minecraft/item/ItemArmor.java b/src/game/java/net/minecraft/item/ItemArmor.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemArmor.java rename to src/game/java/net/minecraft/item/ItemArmor.java diff --git a/src/main/java/net/minecraft/item/ItemArmorStand.java b/src/game/java/net/minecraft/item/ItemArmorStand.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemArmorStand.java rename to src/game/java/net/minecraft/item/ItemArmorStand.java diff --git a/src/main/java/net/minecraft/item/ItemAxe.java b/src/game/java/net/minecraft/item/ItemAxe.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemAxe.java rename to src/game/java/net/minecraft/item/ItemAxe.java diff --git a/src/main/java/net/minecraft/item/ItemBanner.java b/src/game/java/net/minecraft/item/ItemBanner.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBanner.java rename to src/game/java/net/minecraft/item/ItemBanner.java diff --git a/src/main/java/net/minecraft/item/ItemBed.java b/src/game/java/net/minecraft/item/ItemBed.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBed.java rename to src/game/java/net/minecraft/item/ItemBed.java diff --git a/src/main/java/net/minecraft/item/ItemBlock.java b/src/game/java/net/minecraft/item/ItemBlock.java similarity index 98% rename from src/main/java/net/minecraft/item/ItemBlock.java rename to src/game/java/net/minecraft/item/ItemBlock.java index 7c19677..10e68d1 100644 --- a/src/main/java/net/minecraft/item/ItemBlock.java +++ b/src/game/java/net/minecraft/item/ItemBlock.java @@ -183,7 +183,7 @@ public class ItemBlock extends Item { return this.block; } - public float getHeldItemBrightnessEagler() { + public float getHeldItemBrightnessEagler(ItemStack itemStack) { return this.block.getLightValue() * 0.06667f; } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemBoat.java b/src/game/java/net/minecraft/item/ItemBoat.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBoat.java rename to src/game/java/net/minecraft/item/ItemBoat.java diff --git a/src/main/java/net/minecraft/item/ItemBook.java b/src/game/java/net/minecraft/item/ItemBook.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBook.java rename to src/game/java/net/minecraft/item/ItemBook.java diff --git a/src/main/java/net/minecraft/item/ItemBow.java b/src/game/java/net/minecraft/item/ItemBow.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBow.java rename to src/game/java/net/minecraft/item/ItemBow.java diff --git a/src/main/java/net/minecraft/item/ItemBucket.java b/src/game/java/net/minecraft/item/ItemBucket.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBucket.java rename to src/game/java/net/minecraft/item/ItemBucket.java diff --git a/src/main/java/net/minecraft/item/ItemBucketMilk.java b/src/game/java/net/minecraft/item/ItemBucketMilk.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemBucketMilk.java rename to src/game/java/net/minecraft/item/ItemBucketMilk.java diff --git a/src/main/java/net/minecraft/item/ItemCarrotOnAStick.java b/src/game/java/net/minecraft/item/ItemCarrotOnAStick.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemCarrotOnAStick.java rename to src/game/java/net/minecraft/item/ItemCarrotOnAStick.java diff --git a/src/main/java/net/minecraft/item/ItemChorusFruit.java b/src/game/java/net/minecraft/item/ItemChorusFruit.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemChorusFruit.java rename to src/game/java/net/minecraft/item/ItemChorusFruit.java diff --git a/src/main/java/net/minecraft/item/ItemCloth.java b/src/game/java/net/minecraft/item/ItemCloth.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemCloth.java rename to src/game/java/net/minecraft/item/ItemCloth.java diff --git a/src/main/java/net/minecraft/item/ItemCoal.java b/src/game/java/net/minecraft/item/ItemCoal.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemCoal.java rename to src/game/java/net/minecraft/item/ItemCoal.java diff --git a/src/main/java/net/minecraft/item/ItemColored.java b/src/game/java/net/minecraft/item/ItemColored.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemColored.java rename to src/game/java/net/minecraft/item/ItemColored.java diff --git a/src/main/java/net/minecraft/item/ItemDoor.java b/src/game/java/net/minecraft/item/ItemDoor.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemDoor.java rename to src/game/java/net/minecraft/item/ItemDoor.java diff --git a/src/main/java/net/minecraft/item/ItemDoublePlant.java b/src/game/java/net/minecraft/item/ItemDoublePlant.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemDoublePlant.java rename to src/game/java/net/minecraft/item/ItemDoublePlant.java diff --git a/src/main/java/net/minecraft/item/ItemDye.java b/src/game/java/net/minecraft/item/ItemDye.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemDye.java rename to src/game/java/net/minecraft/item/ItemDye.java diff --git a/src/main/java/net/minecraft/item/ItemEditableBook.java b/src/game/java/net/minecraft/item/ItemEditableBook.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemEditableBook.java rename to src/game/java/net/minecraft/item/ItemEditableBook.java diff --git a/src/main/java/net/minecraft/item/ItemEgg.java b/src/game/java/net/minecraft/item/ItemEgg.java similarity index 96% rename from src/main/java/net/minecraft/item/ItemEgg.java rename to src/game/java/net/minecraft/item/ItemEgg.java index cc8720b..b2ed93d 100644 --- a/src/main/java/net/minecraft/item/ItemEgg.java +++ b/src/game/java/net/minecraft/item/ItemEgg.java @@ -59,4 +59,8 @@ public class ItemEgg extends Item { entityplayer.triggerAchievement(StatList.objectUseStats[Item.getIdFromItem(this)]); return itemstack; } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemElytra.java b/src/game/java/net/minecraft/item/ItemElytra.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemElytra.java rename to src/game/java/net/minecraft/item/ItemElytra.java diff --git a/src/main/java/net/minecraft/item/ItemEmptyMap.java b/src/game/java/net/minecraft/item/ItemEmptyMap.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemEmptyMap.java rename to src/game/java/net/minecraft/item/ItemEmptyMap.java diff --git a/src/main/java/net/minecraft/item/ItemEnchantedBook.java b/src/game/java/net/minecraft/item/ItemEnchantedBook.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemEnchantedBook.java rename to src/game/java/net/minecraft/item/ItemEnchantedBook.java diff --git a/src/main/java/net/minecraft/item/ItemEnderEye.java b/src/game/java/net/minecraft/item/ItemEnderEye.java similarity index 98% rename from src/main/java/net/minecraft/item/ItemEnderEye.java rename to src/game/java/net/minecraft/item/ItemEnderEye.java index 30e2e9d..ee36e7b 100644 --- a/src/main/java/net/minecraft/item/ItemEnderEye.java +++ b/src/game/java/net/minecraft/item/ItemEnderEye.java @@ -178,4 +178,8 @@ public class ItemEnderEye extends Item { return itemstack; } } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemEnderPearl.java b/src/game/java/net/minecraft/item/ItemEnderPearl.java similarity index 96% rename from src/main/java/net/minecraft/item/ItemEnderPearl.java rename to src/game/java/net/minecraft/item/ItemEnderPearl.java index e4fb63b..befbc86 100644 --- a/src/main/java/net/minecraft/item/ItemEnderPearl.java +++ b/src/game/java/net/minecraft/item/ItemEnderPearl.java @@ -65,4 +65,8 @@ public class ItemEnderPearl extends Item { return itemstack; } } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemExpBottle.java b/src/game/java/net/minecraft/item/ItemExpBottle.java similarity index 96% rename from src/main/java/net/minecraft/item/ItemExpBottle.java rename to src/game/java/net/minecraft/item/ItemExpBottle.java index f1e455d..3cea537 100644 --- a/src/main/java/net/minecraft/item/ItemExpBottle.java +++ b/src/game/java/net/minecraft/item/ItemExpBottle.java @@ -62,4 +62,8 @@ public class ItemExpBottle extends Item { entityplayer.triggerAchievement(StatList.objectUseStats[Item.getIdFromItem(this)]); return itemstack; } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemFireball.java b/src/game/java/net/minecraft/item/ItemFireball.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemFireball.java rename to src/game/java/net/minecraft/item/ItemFireball.java diff --git a/src/main/java/net/minecraft/item/ItemFirework.java b/src/game/java/net/minecraft/item/ItemFirework.java similarity index 97% rename from src/main/java/net/minecraft/item/ItemFirework.java rename to src/game/java/net/minecraft/item/ItemFirework.java index b4ef164..9e1b970 100644 --- a/src/main/java/net/minecraft/item/ItemFirework.java +++ b/src/game/java/net/minecraft/item/ItemFirework.java @@ -110,4 +110,8 @@ public class ItemFirework extends Item { } } } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemFireworkCharge.java b/src/game/java/net/minecraft/item/ItemFireworkCharge.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemFireworkCharge.java rename to src/game/java/net/minecraft/item/ItemFireworkCharge.java diff --git a/src/main/java/net/minecraft/item/ItemFishFood.java b/src/game/java/net/minecraft/item/ItemFishFood.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemFishFood.java rename to src/game/java/net/minecraft/item/ItemFishFood.java diff --git a/src/main/java/net/minecraft/item/ItemFishingRod.java b/src/game/java/net/minecraft/item/ItemFishingRod.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemFishingRod.java rename to src/game/java/net/minecraft/item/ItemFishingRod.java diff --git a/src/main/java/net/minecraft/item/ItemFlintAndSteel.java b/src/game/java/net/minecraft/item/ItemFlintAndSteel.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemFlintAndSteel.java rename to src/game/java/net/minecraft/item/ItemFlintAndSteel.java diff --git a/src/main/java/net/minecraft/item/ItemFood.java b/src/game/java/net/minecraft/item/ItemFood.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemFood.java rename to src/game/java/net/minecraft/item/ItemFood.java diff --git a/src/main/java/net/minecraft/item/ItemGlassBottle.java b/src/game/java/net/minecraft/item/ItemGlassBottle.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemGlassBottle.java rename to src/game/java/net/minecraft/item/ItemGlassBottle.java diff --git a/src/main/java/net/minecraft/item/ItemHangingEntity.java b/src/game/java/net/minecraft/item/ItemHangingEntity.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemHangingEntity.java rename to src/game/java/net/minecraft/item/ItemHangingEntity.java diff --git a/src/main/java/net/minecraft/item/ItemHoe.java b/src/game/java/net/minecraft/item/ItemHoe.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemHoe.java rename to src/game/java/net/minecraft/item/ItemHoe.java diff --git a/src/main/java/net/minecraft/item/ItemLead.java b/src/game/java/net/minecraft/item/ItemLead.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemLead.java rename to src/game/java/net/minecraft/item/ItemLead.java diff --git a/src/main/java/net/minecraft/item/ItemLeaves.java b/src/game/java/net/minecraft/item/ItemLeaves.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemLeaves.java rename to src/game/java/net/minecraft/item/ItemLeaves.java diff --git a/src/main/java/net/minecraft/item/ItemLilyPad.java b/src/game/java/net/minecraft/item/ItemLilyPad.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemLilyPad.java rename to src/game/java/net/minecraft/item/ItemLilyPad.java diff --git a/src/main/java/net/minecraft/item/ItemMap.java b/src/game/java/net/minecraft/item/ItemMap.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemMap.java rename to src/game/java/net/minecraft/item/ItemMap.java diff --git a/src/main/java/net/minecraft/item/ItemMapBase.java b/src/game/java/net/minecraft/item/ItemMapBase.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemMapBase.java rename to src/game/java/net/minecraft/item/ItemMapBase.java diff --git a/src/main/java/net/minecraft/item/ItemMinecart.java b/src/game/java/net/minecraft/item/ItemMinecart.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemMinecart.java rename to src/game/java/net/minecraft/item/ItemMinecart.java diff --git a/src/main/java/net/minecraft/item/ItemMonsterPlacer.java b/src/game/java/net/minecraft/item/ItemMonsterPlacer.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemMonsterPlacer.java rename to src/game/java/net/minecraft/item/ItemMonsterPlacer.java diff --git a/src/main/java/net/minecraft/item/ItemMultiTexture.java b/src/game/java/net/minecraft/item/ItemMultiTexture.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemMultiTexture.java rename to src/game/java/net/minecraft/item/ItemMultiTexture.java diff --git a/src/main/java/net/minecraft/item/ItemNameTag.java b/src/game/java/net/minecraft/item/ItemNameTag.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemNameTag.java rename to src/game/java/net/minecraft/item/ItemNameTag.java diff --git a/src/main/java/net/minecraft/item/ItemPickaxe.java b/src/game/java/net/minecraft/item/ItemPickaxe.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemPickaxe.java rename to src/game/java/net/minecraft/item/ItemPickaxe.java diff --git a/src/main/java/net/minecraft/item/ItemPiston.java b/src/game/java/net/minecraft/item/ItemPiston.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemPiston.java rename to src/game/java/net/minecraft/item/ItemPiston.java diff --git a/src/main/java/net/minecraft/item/ItemPotion.java b/src/game/java/net/minecraft/item/ItemPotion.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemPotion.java rename to src/game/java/net/minecraft/item/ItemPotion.java diff --git a/src/main/java/net/minecraft/item/ItemRecord.java b/src/game/java/net/minecraft/item/ItemRecord.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemRecord.java rename to src/game/java/net/minecraft/item/ItemRecord.java diff --git a/src/main/java/net/minecraft/item/ItemRedstone.java b/src/game/java/net/minecraft/item/ItemRedstone.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemRedstone.java rename to src/game/java/net/minecraft/item/ItemRedstone.java diff --git a/src/main/java/net/minecraft/item/ItemReed.java b/src/game/java/net/minecraft/item/ItemReed.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemReed.java rename to src/game/java/net/minecraft/item/ItemReed.java diff --git a/src/main/java/net/minecraft/item/ItemSaddle.java b/src/game/java/net/minecraft/item/ItemSaddle.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSaddle.java rename to src/game/java/net/minecraft/item/ItemSaddle.java diff --git a/src/main/java/net/minecraft/item/ItemSeedFood.java b/src/game/java/net/minecraft/item/ItemSeedFood.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSeedFood.java rename to src/game/java/net/minecraft/item/ItemSeedFood.java diff --git a/src/main/java/net/minecraft/item/ItemSeeds.java b/src/game/java/net/minecraft/item/ItemSeeds.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSeeds.java rename to src/game/java/net/minecraft/item/ItemSeeds.java diff --git a/src/main/java/net/minecraft/item/ItemShears.java b/src/game/java/net/minecraft/item/ItemShears.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemShears.java rename to src/game/java/net/minecraft/item/ItemShears.java diff --git a/src/main/java/net/minecraft/item/ItemSign.java b/src/game/java/net/minecraft/item/ItemSign.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSign.java rename to src/game/java/net/minecraft/item/ItemSign.java diff --git a/src/main/java/net/minecraft/item/ItemSimpleFoiled.java b/src/game/java/net/minecraft/item/ItemSimpleFoiled.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSimpleFoiled.java rename to src/game/java/net/minecraft/item/ItemSimpleFoiled.java diff --git a/src/main/java/net/minecraft/item/ItemSkull.java b/src/game/java/net/minecraft/item/ItemSkull.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSkull.java rename to src/game/java/net/minecraft/item/ItemSkull.java diff --git a/src/main/java/net/minecraft/item/ItemSlab.java b/src/game/java/net/minecraft/item/ItemSlab.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSlab.java rename to src/game/java/net/minecraft/item/ItemSlab.java diff --git a/src/main/java/net/minecraft/item/ItemSnow.java b/src/game/java/net/minecraft/item/ItemSnow.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSnow.java rename to src/game/java/net/minecraft/item/ItemSnow.java diff --git a/src/main/java/net/minecraft/item/ItemSnowball.java b/src/game/java/net/minecraft/item/ItemSnowball.java similarity index 96% rename from src/main/java/net/minecraft/item/ItemSnowball.java rename to src/game/java/net/minecraft/item/ItemSnowball.java index 239329b..420588c 100644 --- a/src/main/java/net/minecraft/item/ItemSnowball.java +++ b/src/game/java/net/minecraft/item/ItemSnowball.java @@ -59,4 +59,8 @@ public class ItemSnowball extends Item { entityplayer.triggerAchievement(StatList.objectUseStats[Item.getIdFromItem(this)]); return itemstack; } + + public boolean shouldUseOnTouchEagler(ItemStack itemStack) { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/item/ItemSoup.java b/src/game/java/net/minecraft/item/ItemSoup.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSoup.java rename to src/game/java/net/minecraft/item/ItemSoup.java diff --git a/src/main/java/net/minecraft/item/ItemSpade.java b/src/game/java/net/minecraft/item/ItemSpade.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSpade.java rename to src/game/java/net/minecraft/item/ItemSpade.java diff --git a/src/main/java/net/minecraft/item/ItemStack.java b/src/game/java/net/minecraft/item/ItemStack.java similarity index 95% rename from src/main/java/net/minecraft/item/ItemStack.java rename to src/game/java/net/minecraft/item/ItemStack.java index 5a4db5b..affd3fe 100644 --- a/src/main/java/net/minecraft/item/ItemStack.java +++ b/src/game/java/net/minecraft/item/ItemStack.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.Map.Entry; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; + import java.util.Set; import javax.annotation.Nullable; @@ -16,6 +18,7 @@ import com.google.common.collect.Multimap; import net.minecraft.block.Block; import net.minecraft.client.resources.I18n; +import net.minecraft.client.Minecraft; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentDurability; import net.minecraft.enchantment.EnchantmentHelper; @@ -77,6 +80,8 @@ public final class ItemStack { public int animationsToGo; private Item item; private NBTTagCompound stackTagCompound; + private String profanityFilteredName; + private String profanityFilteredNameFiltered; private int itemDamage; private EntityItemFrame itemFrame; private Block canDestroyCacheBlock; @@ -600,6 +605,27 @@ public final class ItemStack { return s; } + public String getDisplayNameProfanityFilter() { + String s = this.getItem().getItemStackDisplayName(this); + if (this.stackTagCompound != null && this.stackTagCompound.hasKey("display", 10)) { + NBTTagCompound nbttagcompound = this.stackTagCompound.getCompoundTag("display"); + if (nbttagcompound.hasKey("Name", 8)) { + s = nbttagcompound.getString("Name"); + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (!s.equals(profanityFilteredName)) { + profanityFilteredName = s; + profanityFilteredNameFiltered = ProfanityFilter.getInstance().profanityFilterString(s); + } + if (profanityFilteredNameFiltered != null) { + s = profanityFilteredNameFiltered; + } + } + } + } + + return s; + } + public ItemStack setStackDisplayName(String displayName) { cachedDisplayName = null; if (this.stackTagCompound == null) { @@ -650,9 +676,16 @@ public final class ItemStack { * item */ public List getTooltip(EntityPlayer playerIn, boolean advanced) { - List list = Lists.newArrayList(); - String s = this.getDisplayName(); + return getTooltipImpl(playerIn, advanced, false); + } + public List getTooltipProfanityFilter(EntityPlayer playerIn, boolean advanced) { + return getTooltipImpl(playerIn, advanced, true); + } + + public List getTooltipImpl(EntityPlayer playerIn, boolean advanced, boolean profanityFilter) { + ArrayList arraylist = Lists.newArrayList(); + String s = profanityFilter ? this.getDisplayNameProfanityFilter() : this.getDisplayName(); if (this.hasDisplayName()) { s = EnumChatFormatting.ITALIC + s; } diff --git a/src/main/java/net/minecraft/item/ItemSword.java b/src/game/java/net/minecraft/item/ItemSword.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemSword.java rename to src/game/java/net/minecraft/item/ItemSword.java diff --git a/src/main/java/net/minecraft/item/ItemTool.java b/src/game/java/net/minecraft/item/ItemTool.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemTool.java rename to src/game/java/net/minecraft/item/ItemTool.java diff --git a/src/main/java/net/minecraft/item/ItemWritableBook.java b/src/game/java/net/minecraft/item/ItemWritableBook.java similarity index 100% rename from src/main/java/net/minecraft/item/ItemWritableBook.java rename to src/game/java/net/minecraft/item/ItemWritableBook.java diff --git a/src/main/java/net/minecraft/item/crafting/CraftingManager.java b/src/game/java/net/minecraft/item/crafting/CraftingManager.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/CraftingManager.java rename to src/game/java/net/minecraft/item/crafting/CraftingManager.java diff --git a/src/main/java/net/minecraft/item/crafting/FurnaceRecipes.java b/src/game/java/net/minecraft/item/crafting/FurnaceRecipes.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/FurnaceRecipes.java rename to src/game/java/net/minecraft/item/crafting/FurnaceRecipes.java diff --git a/src/main/java/net/minecraft/item/crafting/IRecipe.java b/src/game/java/net/minecraft/item/crafting/IRecipe.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/IRecipe.java rename to src/game/java/net/minecraft/item/crafting/IRecipe.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipeBookCloning.java b/src/game/java/net/minecraft/item/crafting/RecipeBookCloning.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipeBookCloning.java rename to src/game/java/net/minecraft/item/crafting/RecipeBookCloning.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipeFireworks.java b/src/game/java/net/minecraft/item/crafting/RecipeFireworks.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipeFireworks.java rename to src/game/java/net/minecraft/item/crafting/RecipeFireworks.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipeRepairItem.java b/src/game/java/net/minecraft/item/crafting/RecipeRepairItem.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipeRepairItem.java rename to src/game/java/net/minecraft/item/crafting/RecipeRepairItem.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesArmor.java b/src/game/java/net/minecraft/item/crafting/RecipesArmor.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesArmor.java rename to src/game/java/net/minecraft/item/crafting/RecipesArmor.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesArmorDyes.java b/src/game/java/net/minecraft/item/crafting/RecipesArmorDyes.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesArmorDyes.java rename to src/game/java/net/minecraft/item/crafting/RecipesArmorDyes.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesBanners.java b/src/game/java/net/minecraft/item/crafting/RecipesBanners.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesBanners.java rename to src/game/java/net/minecraft/item/crafting/RecipesBanners.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesCrafting.java b/src/game/java/net/minecraft/item/crafting/RecipesCrafting.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesCrafting.java rename to src/game/java/net/minecraft/item/crafting/RecipesCrafting.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesDyes.java b/src/game/java/net/minecraft/item/crafting/RecipesDyes.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesDyes.java rename to src/game/java/net/minecraft/item/crafting/RecipesDyes.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesFood.java b/src/game/java/net/minecraft/item/crafting/RecipesFood.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesFood.java rename to src/game/java/net/minecraft/item/crafting/RecipesFood.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesIngots.java b/src/game/java/net/minecraft/item/crafting/RecipesIngots.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesIngots.java rename to src/game/java/net/minecraft/item/crafting/RecipesIngots.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesMapCloning.java b/src/game/java/net/minecraft/item/crafting/RecipesMapCloning.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesMapCloning.java rename to src/game/java/net/minecraft/item/crafting/RecipesMapCloning.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesMapExtending.java b/src/game/java/net/minecraft/item/crafting/RecipesMapExtending.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesMapExtending.java rename to src/game/java/net/minecraft/item/crafting/RecipesMapExtending.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesTools.java b/src/game/java/net/minecraft/item/crafting/RecipesTools.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesTools.java rename to src/game/java/net/minecraft/item/crafting/RecipesTools.java diff --git a/src/main/java/net/minecraft/item/crafting/RecipesWeapons.java b/src/game/java/net/minecraft/item/crafting/RecipesWeapons.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/RecipesWeapons.java rename to src/game/java/net/minecraft/item/crafting/RecipesWeapons.java diff --git a/src/main/java/net/minecraft/item/crafting/ShapedRecipes.java b/src/game/java/net/minecraft/item/crafting/ShapedRecipes.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/ShapedRecipes.java rename to src/game/java/net/minecraft/item/crafting/ShapedRecipes.java diff --git a/src/main/java/net/minecraft/item/crafting/ShapelessRecipes.java b/src/game/java/net/minecraft/item/crafting/ShapelessRecipes.java similarity index 100% rename from src/main/java/net/minecraft/item/crafting/ShapelessRecipes.java rename to src/game/java/net/minecraft/item/crafting/ShapelessRecipes.java diff --git a/src/main/java/net/minecraft/nbt/CompressedStreamTools.java b/src/game/java/net/minecraft/nbt/CompressedStreamTools.java similarity index 100% rename from src/main/java/net/minecraft/nbt/CompressedStreamTools.java rename to src/game/java/net/minecraft/nbt/CompressedStreamTools.java diff --git a/src/main/java/net/minecraft/nbt/JsonToNBT.java b/src/game/java/net/minecraft/nbt/JsonToNBT.java similarity index 100% rename from src/main/java/net/minecraft/nbt/JsonToNBT.java rename to src/game/java/net/minecraft/nbt/JsonToNBT.java diff --git a/src/main/java/net/minecraft/nbt/NBTBase.java b/src/game/java/net/minecraft/nbt/NBTBase.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTBase.java rename to src/game/java/net/minecraft/nbt/NBTBase.java diff --git a/src/main/java/net/minecraft/nbt/NBTException.java b/src/game/java/net/minecraft/nbt/NBTException.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTException.java rename to src/game/java/net/minecraft/nbt/NBTException.java diff --git a/src/main/java/net/minecraft/nbt/NBTSizeTracker.java b/src/game/java/net/minecraft/nbt/NBTSizeTracker.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTSizeTracker.java rename to src/game/java/net/minecraft/nbt/NBTSizeTracker.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagByte.java b/src/game/java/net/minecraft/nbt/NBTTagByte.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagByte.java rename to src/game/java/net/minecraft/nbt/NBTTagByte.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagByteArray.java b/src/game/java/net/minecraft/nbt/NBTTagByteArray.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagByteArray.java rename to src/game/java/net/minecraft/nbt/NBTTagByteArray.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagCompound.java b/src/game/java/net/minecraft/nbt/NBTTagCompound.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagCompound.java rename to src/game/java/net/minecraft/nbt/NBTTagCompound.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagDouble.java b/src/game/java/net/minecraft/nbt/NBTTagDouble.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagDouble.java rename to src/game/java/net/minecraft/nbt/NBTTagDouble.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagEnd.java b/src/game/java/net/minecraft/nbt/NBTTagEnd.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagEnd.java rename to src/game/java/net/minecraft/nbt/NBTTagEnd.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagFloat.java b/src/game/java/net/minecraft/nbt/NBTTagFloat.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagFloat.java rename to src/game/java/net/minecraft/nbt/NBTTagFloat.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagInt.java b/src/game/java/net/minecraft/nbt/NBTTagInt.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagInt.java rename to src/game/java/net/minecraft/nbt/NBTTagInt.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagIntArray.java b/src/game/java/net/minecraft/nbt/NBTTagIntArray.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagIntArray.java rename to src/game/java/net/minecraft/nbt/NBTTagIntArray.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagList.java b/src/game/java/net/minecraft/nbt/NBTTagList.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagList.java rename to src/game/java/net/minecraft/nbt/NBTTagList.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagLong.java b/src/game/java/net/minecraft/nbt/NBTTagLong.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagLong.java rename to src/game/java/net/minecraft/nbt/NBTTagLong.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagShort.java b/src/game/java/net/minecraft/nbt/NBTTagShort.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagShort.java rename to src/game/java/net/minecraft/nbt/NBTTagShort.java diff --git a/src/main/java/net/minecraft/nbt/NBTTagString.java b/src/game/java/net/minecraft/nbt/NBTTagString.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTTagString.java rename to src/game/java/net/minecraft/nbt/NBTTagString.java diff --git a/src/main/java/net/minecraft/nbt/NBTUtil.java b/src/game/java/net/minecraft/nbt/NBTUtil.java similarity index 100% rename from src/main/java/net/minecraft/nbt/NBTUtil.java rename to src/game/java/net/minecraft/nbt/NBTUtil.java diff --git a/src/main/java/net/minecraft/network/EnumConnectionState.java b/src/game/java/net/minecraft/network/EnumConnectionState.java similarity index 100% rename from src/main/java/net/minecraft/network/EnumConnectionState.java rename to src/game/java/net/minecraft/network/EnumConnectionState.java diff --git a/src/main/java/net/minecraft/network/EnumPacketDirection.java b/src/game/java/net/minecraft/network/EnumPacketDirection.java similarity index 100% rename from src/main/java/net/minecraft/network/EnumPacketDirection.java rename to src/game/java/net/minecraft/network/EnumPacketDirection.java diff --git a/src/main/java/net/minecraft/network/INetHandler.java b/src/game/java/net/minecraft/network/INetHandler.java similarity index 100% rename from src/main/java/net/minecraft/network/INetHandler.java rename to src/game/java/net/minecraft/network/INetHandler.java diff --git a/src/main/java/net/minecraft/network/NetHandlerPlayServer.java b/src/game/java/net/minecraft/network/NetHandlerPlayServer.java similarity index 96% rename from src/main/java/net/minecraft/network/NetHandlerPlayServer.java rename to src/game/java/net/minecraft/network/NetHandlerPlayServer.java index 6df74dd..c7ccf98 100644 --- a/src/main/java/net/minecraft/network/NetHandlerPlayServer.java +++ b/src/game/java/net/minecraft/network/NetHandlerPlayServer.java @@ -3,7 +3,11 @@ package net.minecraft.network; import com.google.common.collect.Lists; import com.google.common.primitives.Doubles; import com.google.common.primitives.Floats; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketUpdateCertEAG; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -11,7 +15,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.Callable; -import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; import net.minecraft.block.material.Material; import net.minecraft.command.server.CommandBlockLogic; import net.minecraft.crash.CrashReport; @@ -71,7 +74,6 @@ import net.minecraft.network.play.server.S23PacketBlockChange; import net.minecraft.network.play.server.S2FPacketSetSlot; import net.minecraft.network.play.server.S32PacketConfirmTransaction; import net.minecraft.network.play.server.S3APacketTabComplete; -import net.minecraft.network.play.server.S3FPacketCustomPayload; import net.minecraft.network.play.server.S40PacketDisconnect; import net.minecraft.server.MinecraftServer; import net.minecraft.stats.AchievementList; @@ -91,11 +93,12 @@ import net.minecraft.util.IntHashMap; import net.minecraft.util.ReportedException; import net.minecraft.world.WorldServer; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; -import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import org.apache.commons.lang3.StringUtils; import net.hoosiertransfer.EaglerItems; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -133,7 +136,7 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { private static final Logger logger = LogManager.getLogger(); public final IntegratedServerPlayerNetworkManager netManager; - private final MinecraftServer serverController; + public final MinecraftServer serverController; public EntityPlayerMP playerEntity; private int networkTickCount; private int field_175090_f; @@ -150,6 +153,7 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { private double lastPosZ; private boolean hasMoved = true; private boolean hasDisconnected = false; + private GameProtocolMessageController eaglerMessageController = null; public NetHandlerPlayServer(MinecraftServer server, IntegratedServerPlayerNetworkManager networkManagerIn, EntityPlayerMP playerIn) { @@ -160,14 +164,33 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { playerIn.playerNetServerHandler = this; } - /** - * + + public GameProtocolMessageController getEaglerMessageController() { + return eaglerMessageController; + } + + public void setEaglerMessageController(GameProtocolMessageController eaglerMessageController) { + this.eaglerMessageController = eaglerMessageController; + } + + public GamePluginMessageProtocol getEaglerMessageProtocol() { + return eaglerMessageController != null ? eaglerMessageController.protocol : null; + } + + public void sendEaglerMessage(GameMessagePacket packet) { + try { + eaglerMessageController.sendPacket(packet); + } catch (IOException e) { + logger.error("Failed to send eaglercraft plugin message packet: " + packet); + logger.error(e); + } + } + + /**+ * Like the old updateEntity(), except more generic. */ public void update() { this.field_147366_g = false; ++this.networkTickCount; - this.serverController.theProfiler.startSection("keepAlive"); if ((long) this.networkTickCount - this.lastSentPingPacket > 40L) { this.lastSentPingPacket = (long) this.networkTickCount; this.lastPingTime = this.currentTimeMillis(); @@ -175,7 +198,6 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { this.sendPacket(new S00PacketKeepAlive(this.field_147378_h)); } - this.serverController.theProfiler.endSection(); if (this.chatSpamThresholdCount > 0) { --this.chatSpamThresholdCount; } @@ -190,6 +212,9 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { this.kickPlayerFromServer("You have been idle for too long!"); } + if (this.eaglerMessageController != null) { + this.eaglerMessageController.flush(); + } } public IntegratedServerPlayerNetworkManager getNetworkManager() { @@ -1097,7 +1122,7 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { } private long currentTimeMillis() { - return System.nanoTime() / 1000000L; + return EagRuntime.steadyTimeMillis(); } /** @@ -1288,19 +1313,6 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { } else { containerrepair.updateItemName(""); } - } else if ("EAG|Skins-1.8".equals(c17packetcustompayload.getChannelName())) { - byte[] r = new byte[c17packetcustompayload.getBufferData().readableBytes()]; - c17packetcustompayload.getBufferData().readBytes(r); - ((EaglerMinecraftServer) serverController).getSkinService().processPacket(r, playerEntity); - } else if ("EAG|Capes-1.8".equals(c17packetcustompayload.getChannelName())) { - byte[] r = new byte[c17packetcustompayload.getBufferData().readableBytes()]; - c17packetcustompayload.getBufferData().readBytes(r); - ((EaglerMinecraftServer) serverController).getCapeService().processPacket(r, playerEntity); - } else if ("EAG|Voice-1.8".equals(c17packetcustompayload.getChannelName())) { - IntegratedVoiceService vcs = ((EaglerMinecraftServer) serverController).getVoiceService(); - if (vcs != null) { - vcs.processPacket(c17packetcustompayload.getBufferData(), playerEntity); - } } else if ("EAG|MyUpdCert-1.8".equals(c17packetcustompayload.getChannelName())) { if (playerEntity.updateCertificate == null) { PacketBuffer pb = c17packetcustompayload.getBufferData(); @@ -1311,11 +1323,19 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { for (int i = 0, l = lst.size(); i < l; ++i) { EntityPlayerMP player = lst.get(i); if (player != playerEntity) { - player.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload("EAG|UpdateCert-1.8", - new PacketBuffer(Unpooled.buffer(cert, cert.length).writerIndex(cert.length)))); + player.playerNetServerHandler.sendEaglerMessage(new SPacketUpdateCertEAG(cert)); } } } + } else { + try { + eaglerMessageController.handlePacket(c17packetcustompayload.getChannelName(), + c17packetcustompayload.getBufferData()); + } catch (IOException e) { + logger.error("Couldn't read \"{}\" packet as an eaglercraft plugin message!", + c17packetcustompayload.getChannelName()); + logger.error(e); + } } } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/network/Packet.java b/src/game/java/net/minecraft/network/Packet.java similarity index 100% rename from src/main/java/net/minecraft/network/Packet.java rename to src/game/java/net/minecraft/network/Packet.java diff --git a/src/main/java/net/minecraft/network/PacketBuffer.java b/src/game/java/net/minecraft/network/PacketBuffer.java similarity index 100% rename from src/main/java/net/minecraft/network/PacketBuffer.java rename to src/game/java/net/minecraft/network/PacketBuffer.java diff --git a/src/main/java/net/minecraft/network/ServerStatusResponse.java b/src/game/java/net/minecraft/network/ServerStatusResponse.java similarity index 100% rename from src/main/java/net/minecraft/network/ServerStatusResponse.java rename to src/game/java/net/minecraft/network/ServerStatusResponse.java diff --git a/src/main/java/net/minecraft/network/handshake/INetHandlerHandshakeServer.java b/src/game/java/net/minecraft/network/handshake/INetHandlerHandshakeServer.java similarity index 100% rename from src/main/java/net/minecraft/network/handshake/INetHandlerHandshakeServer.java rename to src/game/java/net/minecraft/network/handshake/INetHandlerHandshakeServer.java diff --git a/src/main/java/net/minecraft/network/handshake/client/C00Handshake.java b/src/game/java/net/minecraft/network/handshake/client/C00Handshake.java similarity index 100% rename from src/main/java/net/minecraft/network/handshake/client/C00Handshake.java rename to src/game/java/net/minecraft/network/handshake/client/C00Handshake.java diff --git a/src/main/java/net/minecraft/network/login/INetHandlerLoginClient.java b/src/game/java/net/minecraft/network/login/INetHandlerLoginClient.java similarity index 100% rename from src/main/java/net/minecraft/network/login/INetHandlerLoginClient.java rename to src/game/java/net/minecraft/network/login/INetHandlerLoginClient.java diff --git a/src/main/java/net/minecraft/network/login/INetHandlerLoginServer.java b/src/game/java/net/minecraft/network/login/INetHandlerLoginServer.java similarity index 100% rename from src/main/java/net/minecraft/network/login/INetHandlerLoginServer.java rename to src/game/java/net/minecraft/network/login/INetHandlerLoginServer.java diff --git a/src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java b/src/game/java/net/minecraft/network/login/client/C00PacketLoginStart.java similarity index 80% rename from src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java rename to src/game/java/net/minecraft/network/login/client/C00PacketLoginStart.java index 177f897..f013f78 100644 --- a/src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java +++ b/src/game/java/net/minecraft/network/login/client/C00PacketLoginStart.java @@ -1,92 +1,109 @@ -package net.minecraft.network.login.client; - -import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; -import java.io.IOException; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.minecraft.network.Packet; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.login.INetHandlerLoginServer; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class C00PacketLoginStart implements Packet { - private GameProfile profile; - private byte[] skin; - private byte[] cape; - - public C00PacketLoginStart() { - } - - public C00PacketLoginStart(GameProfile profileIn, byte[] skin, byte[] cape) { - this.profile = profileIn; - this.skin = skin; - this.cape = cape; - } - - /** - * + - * Reads the raw packet data from the data stream. - */ - public void readPacketData(PacketBuffer parPacketBuffer) throws IOException { - this.profile = new GameProfile((EaglercraftUUID) null, parPacketBuffer.readStringFromBuffer(16)); - this.skin = parPacketBuffer.readByteArray(); - this.cape = parPacketBuffer.readableBytes() > 0 ? parPacketBuffer.readByteArray() : null; - } - - /** - * + - * Writes the raw packet data to the data stream. - */ - public void writePacketData(PacketBuffer parPacketBuffer) throws IOException { - parPacketBuffer.writeString(this.profile.getName()); - parPacketBuffer.writeByteArray(this.skin); - parPacketBuffer.writeByteArray(this.cape); - } - - /** - * + - * Passes this Packet on to the NetHandler for processing. - */ - public void processPacket(INetHandlerLoginServer inethandlerloginserver) { - inethandlerloginserver.processLoginStart(this); - } - - public GameProfile getProfile() { - return this.profile; - } - - public byte[] getSkin() { - return this.skin; - } - - public byte[] getCape() { - return this.cape; - } +package net.minecraft.network.login.client; + +import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import java.io.IOException; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.minecraft.network.Packet; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.login.INetHandlerLoginServer; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class C00PacketLoginStart implements Packet { + private GameProfile profile; + private byte[] skin; + private byte[] cape; + private byte[] protocols; + private EaglercraftUUID brandUUID; + + public C00PacketLoginStart() { + } + + public C00PacketLoginStart(GameProfile profileIn, byte[] skin, byte[] cape, byte[] protocols, + EaglercraftUUID brandUUID) { + this.profile = profileIn; + this.skin = skin; + this.cape = cape; + this.protocols = protocols; + this.brandUUID = brandUUID; + } + + /** + * + + * Reads the raw packet data from the data stream. + */ + public void readPacketData(PacketBuffer parPacketBuffer) throws IOException { + this.profile = new GameProfile((EaglercraftUUID) null, parPacketBuffer.readStringFromBuffer(16)); + this.skin = parPacketBuffer.readByteArray(); + this.cape = parPacketBuffer.readableBytes() > 0 ? parPacketBuffer.readByteArray() : null; + this.protocols = parPacketBuffer.readableBytes() > 0 ? parPacketBuffer.readByteArray() : null; + this.brandUUID = parPacketBuffer.readableBytes() > 0 ? parPacketBuffer.readUuid() : null; + } + + /** + * + + * Writes the raw packet data to the data stream. + */ + public void writePacketData(PacketBuffer parPacketBuffer) throws IOException { + parPacketBuffer.writeString(this.profile.getName()); + parPacketBuffer.writeByteArray(this.skin); + parPacketBuffer.writeByteArray(this.cape); + parPacketBuffer.writeByteArray(this.protocols); + parPacketBuffer.writeUuid(brandUUID); + } + + /** + * + + * Passes this Packet on to the NetHandler for processing. + */ + public void processPacket(INetHandlerLoginServer inethandlerloginserver) { + inethandlerloginserver.processLoginStart(this); + } + + public GameProfile getProfile() { + return this.profile; + } + + public byte[] getSkin() { + return this.skin; + } + + public byte[] getCape() { + return this.cape; + } + + public byte[] getProtocols() { + return this.protocols; + } + + public EaglercraftUUID getBrandUUID() { + return this.brandUUID; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/network/login/client/C01PacketEncryptionResponse.java b/src/game/java/net/minecraft/network/login/client/C01PacketEncryptionResponse.java similarity index 100% rename from src/main/java/net/minecraft/network/login/client/C01PacketEncryptionResponse.java rename to src/game/java/net/minecraft/network/login/client/C01PacketEncryptionResponse.java diff --git a/src/main/java/net/minecraft/network/login/server/S00PacketDisconnect.java b/src/game/java/net/minecraft/network/login/server/S00PacketDisconnect.java similarity index 100% rename from src/main/java/net/minecraft/network/login/server/S00PacketDisconnect.java rename to src/game/java/net/minecraft/network/login/server/S00PacketDisconnect.java diff --git a/src/main/java/net/minecraft/network/login/server/S01PacketEncryptionRequest.java b/src/game/java/net/minecraft/network/login/server/S01PacketEncryptionRequest.java similarity index 100% rename from src/main/java/net/minecraft/network/login/server/S01PacketEncryptionRequest.java rename to src/game/java/net/minecraft/network/login/server/S01PacketEncryptionRequest.java diff --git a/src/main/java/net/minecraft/network/login/server/S02PacketLoginSuccess.java b/src/game/java/net/minecraft/network/login/server/S02PacketLoginSuccess.java similarity index 86% rename from src/main/java/net/minecraft/network/login/server/S02PacketLoginSuccess.java rename to src/game/java/net/minecraft/network/login/server/S02PacketLoginSuccess.java index adffeb4..da9830e 100644 --- a/src/main/java/net/minecraft/network/login/server/S02PacketLoginSuccess.java +++ b/src/game/java/net/minecraft/network/login/server/S02PacketLoginSuccess.java @@ -39,12 +39,14 @@ import net.minecraft.network.login.INetHandlerLoginClient; */ public class S02PacketLoginSuccess implements Packet { private GameProfile profile; + private int selectedProtocol = 3; public S02PacketLoginSuccess() { } - public S02PacketLoginSuccess(GameProfile profileIn) { + public S02PacketLoginSuccess(GameProfile profileIn, int selectedProtocol) { this.profile = profileIn; + this.selectedProtocol = selectedProtocol; } /** @@ -54,6 +56,7 @@ public class S02PacketLoginSuccess implements Packet { public void readPacketData(PacketBuffer parPacketBuffer) throws IOException { String s = parPacketBuffer.readStringFromBuffer(36); String s1 = parPacketBuffer.readStringFromBuffer(16); + selectedProtocol = parPacketBuffer.readableBytes() > 0 ? parPacketBuffer.readShort() : 3; EaglercraftUUID uuid = EaglercraftUUID.fromString(s); this.profile = new GameProfile(uuid, s1); } @@ -66,6 +69,9 @@ public class S02PacketLoginSuccess implements Packet { EaglercraftUUID uuid = this.profile.getId(); parPacketBuffer.writeString(uuid == null ? "" : uuid.toString()); parPacketBuffer.writeString(this.profile.getName()); + if (selectedProtocol != 3) { + parPacketBuffer.writeShort(selectedProtocol); + } } /** @@ -79,4 +85,8 @@ public class S02PacketLoginSuccess implements Packet { public GameProfile getProfile() { return this.profile; } + + public int getSelectedProtocol() { + return selectedProtocol; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/network/login/server/S03PacketEnableCompression.java b/src/game/java/net/minecraft/network/login/server/S03PacketEnableCompression.java similarity index 100% rename from src/main/java/net/minecraft/network/login/server/S03PacketEnableCompression.java rename to src/game/java/net/minecraft/network/login/server/S03PacketEnableCompression.java diff --git a/src/main/java/net/minecraft/network/play/INetHandlerPlayClient.java b/src/game/java/net/minecraft/network/play/INetHandlerPlayClient.java similarity index 100% rename from src/main/java/net/minecraft/network/play/INetHandlerPlayClient.java rename to src/game/java/net/minecraft/network/play/INetHandlerPlayClient.java diff --git a/src/main/java/net/minecraft/network/play/INetHandlerPlayServer.java b/src/game/java/net/minecraft/network/play/INetHandlerPlayServer.java similarity index 100% rename from src/main/java/net/minecraft/network/play/INetHandlerPlayServer.java rename to src/game/java/net/minecraft/network/play/INetHandlerPlayServer.java diff --git a/src/main/java/net/minecraft/network/play/client/C00PacketKeepAlive.java b/src/game/java/net/minecraft/network/play/client/C00PacketKeepAlive.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C00PacketKeepAlive.java rename to src/game/java/net/minecraft/network/play/client/C00PacketKeepAlive.java diff --git a/src/main/java/net/minecraft/network/play/client/C01PacketChatMessage.java b/src/game/java/net/minecraft/network/play/client/C01PacketChatMessage.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C01PacketChatMessage.java rename to src/game/java/net/minecraft/network/play/client/C01PacketChatMessage.java diff --git a/src/main/java/net/minecraft/network/play/client/C02PacketUseEntity.java b/src/game/java/net/minecraft/network/play/client/C02PacketUseEntity.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C02PacketUseEntity.java rename to src/game/java/net/minecraft/network/play/client/C02PacketUseEntity.java diff --git a/src/main/java/net/minecraft/network/play/client/C03PacketPlayer.java b/src/game/java/net/minecraft/network/play/client/C03PacketPlayer.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C03PacketPlayer.java rename to src/game/java/net/minecraft/network/play/client/C03PacketPlayer.java diff --git a/src/main/java/net/minecraft/network/play/client/C07PacketPlayerDigging.java b/src/game/java/net/minecraft/network/play/client/C07PacketPlayerDigging.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C07PacketPlayerDigging.java rename to src/game/java/net/minecraft/network/play/client/C07PacketPlayerDigging.java diff --git a/src/main/java/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.java b/src/game/java/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.java rename to src/game/java/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.java diff --git a/src/main/java/net/minecraft/network/play/client/C09PacketHeldItemChange.java b/src/game/java/net/minecraft/network/play/client/C09PacketHeldItemChange.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C09PacketHeldItemChange.java rename to src/game/java/net/minecraft/network/play/client/C09PacketHeldItemChange.java diff --git a/src/main/java/net/minecraft/network/play/client/C0APacketAnimation.java b/src/game/java/net/minecraft/network/play/client/C0APacketAnimation.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C0APacketAnimation.java rename to src/game/java/net/minecraft/network/play/client/C0APacketAnimation.java diff --git a/src/main/java/net/minecraft/network/play/client/C0BPacketEntityAction.java b/src/game/java/net/minecraft/network/play/client/C0BPacketEntityAction.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C0BPacketEntityAction.java rename to src/game/java/net/minecraft/network/play/client/C0BPacketEntityAction.java diff --git a/src/main/java/net/minecraft/network/play/client/C0CPacketInput.java b/src/game/java/net/minecraft/network/play/client/C0CPacketInput.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C0CPacketInput.java rename to src/game/java/net/minecraft/network/play/client/C0CPacketInput.java diff --git a/src/main/java/net/minecraft/network/play/client/C0DPacketCloseWindow.java b/src/game/java/net/minecraft/network/play/client/C0DPacketCloseWindow.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C0DPacketCloseWindow.java rename to src/game/java/net/minecraft/network/play/client/C0DPacketCloseWindow.java diff --git a/src/main/java/net/minecraft/network/play/client/C0EPacketClickWindow.java b/src/game/java/net/minecraft/network/play/client/C0EPacketClickWindow.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C0EPacketClickWindow.java rename to src/game/java/net/minecraft/network/play/client/C0EPacketClickWindow.java diff --git a/src/main/java/net/minecraft/network/play/client/C0FPacketConfirmTransaction.java b/src/game/java/net/minecraft/network/play/client/C0FPacketConfirmTransaction.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C0FPacketConfirmTransaction.java rename to src/game/java/net/minecraft/network/play/client/C0FPacketConfirmTransaction.java diff --git a/src/main/java/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.java b/src/game/java/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.java rename to src/game/java/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.java diff --git a/src/main/java/net/minecraft/network/play/client/C11PacketEnchantItem.java b/src/game/java/net/minecraft/network/play/client/C11PacketEnchantItem.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C11PacketEnchantItem.java rename to src/game/java/net/minecraft/network/play/client/C11PacketEnchantItem.java diff --git a/src/main/java/net/minecraft/network/play/client/C12PacketUpdateSign.java b/src/game/java/net/minecraft/network/play/client/C12PacketUpdateSign.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C12PacketUpdateSign.java rename to src/game/java/net/minecraft/network/play/client/C12PacketUpdateSign.java diff --git a/src/main/java/net/minecraft/network/play/client/C13PacketPlayerAbilities.java b/src/game/java/net/minecraft/network/play/client/C13PacketPlayerAbilities.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C13PacketPlayerAbilities.java rename to src/game/java/net/minecraft/network/play/client/C13PacketPlayerAbilities.java diff --git a/src/main/java/net/minecraft/network/play/client/C14PacketTabComplete.java b/src/game/java/net/minecraft/network/play/client/C14PacketTabComplete.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C14PacketTabComplete.java rename to src/game/java/net/minecraft/network/play/client/C14PacketTabComplete.java diff --git a/src/main/java/net/minecraft/network/play/client/C15PacketClientSettings.java b/src/game/java/net/minecraft/network/play/client/C15PacketClientSettings.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C15PacketClientSettings.java rename to src/game/java/net/minecraft/network/play/client/C15PacketClientSettings.java diff --git a/src/main/java/net/minecraft/network/play/client/C16PacketClientStatus.java b/src/game/java/net/minecraft/network/play/client/C16PacketClientStatus.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C16PacketClientStatus.java rename to src/game/java/net/minecraft/network/play/client/C16PacketClientStatus.java diff --git a/src/main/java/net/minecraft/network/play/client/C17PacketCustomPayload.java b/src/game/java/net/minecraft/network/play/client/C17PacketCustomPayload.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C17PacketCustomPayload.java rename to src/game/java/net/minecraft/network/play/client/C17PacketCustomPayload.java diff --git a/src/main/java/net/minecraft/network/play/client/C18PacketSpectate.java b/src/game/java/net/minecraft/network/play/client/C18PacketSpectate.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C18PacketSpectate.java rename to src/game/java/net/minecraft/network/play/client/C18PacketSpectate.java diff --git a/src/main/java/net/minecraft/network/play/client/C19PacketResourcePackStatus.java b/src/game/java/net/minecraft/network/play/client/C19PacketResourcePackStatus.java similarity index 100% rename from src/main/java/net/minecraft/network/play/client/C19PacketResourcePackStatus.java rename to src/game/java/net/minecraft/network/play/client/C19PacketResourcePackStatus.java diff --git a/src/main/java/net/minecraft/network/play/server/S00PacketKeepAlive.java b/src/game/java/net/minecraft/network/play/server/S00PacketKeepAlive.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S00PacketKeepAlive.java rename to src/game/java/net/minecraft/network/play/server/S00PacketKeepAlive.java diff --git a/src/main/java/net/minecraft/network/play/server/S01PacketJoinGame.java b/src/game/java/net/minecraft/network/play/server/S01PacketJoinGame.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S01PacketJoinGame.java rename to src/game/java/net/minecraft/network/play/server/S01PacketJoinGame.java diff --git a/src/main/java/net/minecraft/network/play/server/S02PacketChat.java b/src/game/java/net/minecraft/network/play/server/S02PacketChat.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S02PacketChat.java rename to src/game/java/net/minecraft/network/play/server/S02PacketChat.java diff --git a/src/main/java/net/minecraft/network/play/server/S03PacketTimeUpdate.java b/src/game/java/net/minecraft/network/play/server/S03PacketTimeUpdate.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S03PacketTimeUpdate.java rename to src/game/java/net/minecraft/network/play/server/S03PacketTimeUpdate.java diff --git a/src/main/java/net/minecraft/network/play/server/S04PacketEntityEquipment.java b/src/game/java/net/minecraft/network/play/server/S04PacketEntityEquipment.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S04PacketEntityEquipment.java rename to src/game/java/net/minecraft/network/play/server/S04PacketEntityEquipment.java diff --git a/src/main/java/net/minecraft/network/play/server/S05PacketSpawnPosition.java b/src/game/java/net/minecraft/network/play/server/S05PacketSpawnPosition.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S05PacketSpawnPosition.java rename to src/game/java/net/minecraft/network/play/server/S05PacketSpawnPosition.java diff --git a/src/main/java/net/minecraft/network/play/server/S06PacketUpdateHealth.java b/src/game/java/net/minecraft/network/play/server/S06PacketUpdateHealth.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S06PacketUpdateHealth.java rename to src/game/java/net/minecraft/network/play/server/S06PacketUpdateHealth.java diff --git a/src/main/java/net/minecraft/network/play/server/S07PacketRespawn.java b/src/game/java/net/minecraft/network/play/server/S07PacketRespawn.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S07PacketRespawn.java rename to src/game/java/net/minecraft/network/play/server/S07PacketRespawn.java diff --git a/src/main/java/net/minecraft/network/play/server/S08PacketPlayerPosLook.java b/src/game/java/net/minecraft/network/play/server/S08PacketPlayerPosLook.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S08PacketPlayerPosLook.java rename to src/game/java/net/minecraft/network/play/server/S08PacketPlayerPosLook.java diff --git a/src/main/java/net/minecraft/network/play/server/S09PacketHeldItemChange.java b/src/game/java/net/minecraft/network/play/server/S09PacketHeldItemChange.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S09PacketHeldItemChange.java rename to src/game/java/net/minecraft/network/play/server/S09PacketHeldItemChange.java diff --git a/src/main/java/net/minecraft/network/play/server/S0APacketUseBed.java b/src/game/java/net/minecraft/network/play/server/S0APacketUseBed.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S0APacketUseBed.java rename to src/game/java/net/minecraft/network/play/server/S0APacketUseBed.java diff --git a/src/main/java/net/minecraft/network/play/server/S0BPacketAnimation.java b/src/game/java/net/minecraft/network/play/server/S0BPacketAnimation.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S0BPacketAnimation.java rename to src/game/java/net/minecraft/network/play/server/S0BPacketAnimation.java diff --git a/src/main/java/net/minecraft/network/play/server/S0CPacketSpawnPlayer.java b/src/game/java/net/minecraft/network/play/server/S0CPacketSpawnPlayer.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S0CPacketSpawnPlayer.java rename to src/game/java/net/minecraft/network/play/server/S0CPacketSpawnPlayer.java diff --git a/src/main/java/net/minecraft/network/play/server/S0DPacketCollectItem.java b/src/game/java/net/minecraft/network/play/server/S0DPacketCollectItem.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S0DPacketCollectItem.java rename to src/game/java/net/minecraft/network/play/server/S0DPacketCollectItem.java diff --git a/src/main/java/net/minecraft/network/play/server/S0EPacketSpawnObject.java b/src/game/java/net/minecraft/network/play/server/S0EPacketSpawnObject.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S0EPacketSpawnObject.java rename to src/game/java/net/minecraft/network/play/server/S0EPacketSpawnObject.java diff --git a/src/main/java/net/minecraft/network/play/server/S0FPacketSpawnMob.java b/src/game/java/net/minecraft/network/play/server/S0FPacketSpawnMob.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S0FPacketSpawnMob.java rename to src/game/java/net/minecraft/network/play/server/S0FPacketSpawnMob.java diff --git a/src/main/java/net/minecraft/network/play/server/S10PacketSpawnPainting.java b/src/game/java/net/minecraft/network/play/server/S10PacketSpawnPainting.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S10PacketSpawnPainting.java rename to src/game/java/net/minecraft/network/play/server/S10PacketSpawnPainting.java diff --git a/src/main/java/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.java b/src/game/java/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.java rename to src/game/java/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.java diff --git a/src/main/java/net/minecraft/network/play/server/S12PacketEntityVelocity.java b/src/game/java/net/minecraft/network/play/server/S12PacketEntityVelocity.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S12PacketEntityVelocity.java rename to src/game/java/net/minecraft/network/play/server/S12PacketEntityVelocity.java diff --git a/src/main/java/net/minecraft/network/play/server/S13PacketDestroyEntities.java b/src/game/java/net/minecraft/network/play/server/S13PacketDestroyEntities.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S13PacketDestroyEntities.java rename to src/game/java/net/minecraft/network/play/server/S13PacketDestroyEntities.java diff --git a/src/main/java/net/minecraft/network/play/server/S14PacketEntity.java b/src/game/java/net/minecraft/network/play/server/S14PacketEntity.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S14PacketEntity.java rename to src/game/java/net/minecraft/network/play/server/S14PacketEntity.java diff --git a/src/main/java/net/minecraft/network/play/server/S18PacketEntityTeleport.java b/src/game/java/net/minecraft/network/play/server/S18PacketEntityTeleport.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S18PacketEntityTeleport.java rename to src/game/java/net/minecraft/network/play/server/S18PacketEntityTeleport.java diff --git a/src/main/java/net/minecraft/network/play/server/S19PacketEntityHeadLook.java b/src/game/java/net/minecraft/network/play/server/S19PacketEntityHeadLook.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S19PacketEntityHeadLook.java rename to src/game/java/net/minecraft/network/play/server/S19PacketEntityHeadLook.java diff --git a/src/main/java/net/minecraft/network/play/server/S19PacketEntityStatus.java b/src/game/java/net/minecraft/network/play/server/S19PacketEntityStatus.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S19PacketEntityStatus.java rename to src/game/java/net/minecraft/network/play/server/S19PacketEntityStatus.java diff --git a/src/main/java/net/minecraft/network/play/server/S1BPacketEntityAttach.java b/src/game/java/net/minecraft/network/play/server/S1BPacketEntityAttach.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S1BPacketEntityAttach.java rename to src/game/java/net/minecraft/network/play/server/S1BPacketEntityAttach.java diff --git a/src/main/java/net/minecraft/network/play/server/S1CPacketEntityMetadata.java b/src/game/java/net/minecraft/network/play/server/S1CPacketEntityMetadata.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S1CPacketEntityMetadata.java rename to src/game/java/net/minecraft/network/play/server/S1CPacketEntityMetadata.java diff --git a/src/main/java/net/minecraft/network/play/server/S1DPacketEntityEffect.java b/src/game/java/net/minecraft/network/play/server/S1DPacketEntityEffect.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S1DPacketEntityEffect.java rename to src/game/java/net/minecraft/network/play/server/S1DPacketEntityEffect.java diff --git a/src/main/java/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.java b/src/game/java/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.java rename to src/game/java/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.java diff --git a/src/main/java/net/minecraft/network/play/server/S1FPacketSetExperience.java b/src/game/java/net/minecraft/network/play/server/S1FPacketSetExperience.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S1FPacketSetExperience.java rename to src/game/java/net/minecraft/network/play/server/S1FPacketSetExperience.java diff --git a/src/main/java/net/minecraft/network/play/server/S20PacketEntityProperties.java b/src/game/java/net/minecraft/network/play/server/S20PacketEntityProperties.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S20PacketEntityProperties.java rename to src/game/java/net/minecraft/network/play/server/S20PacketEntityProperties.java diff --git a/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java b/src/game/java/net/minecraft/network/play/server/S21PacketChunkData.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java rename to src/game/java/net/minecraft/network/play/server/S21PacketChunkData.java diff --git a/src/main/java/net/minecraft/network/play/server/S22PacketMultiBlockChange.java b/src/game/java/net/minecraft/network/play/server/S22PacketMultiBlockChange.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S22PacketMultiBlockChange.java rename to src/game/java/net/minecraft/network/play/server/S22PacketMultiBlockChange.java diff --git a/src/main/java/net/minecraft/network/play/server/S23PacketBlockChange.java b/src/game/java/net/minecraft/network/play/server/S23PacketBlockChange.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S23PacketBlockChange.java rename to src/game/java/net/minecraft/network/play/server/S23PacketBlockChange.java diff --git a/src/main/java/net/minecraft/network/play/server/S24PacketBlockAction.java b/src/game/java/net/minecraft/network/play/server/S24PacketBlockAction.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S24PacketBlockAction.java rename to src/game/java/net/minecraft/network/play/server/S24PacketBlockAction.java diff --git a/src/main/java/net/minecraft/network/play/server/S25PacketBlockBreakAnim.java b/src/game/java/net/minecraft/network/play/server/S25PacketBlockBreakAnim.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S25PacketBlockBreakAnim.java rename to src/game/java/net/minecraft/network/play/server/S25PacketBlockBreakAnim.java diff --git a/src/main/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java b/src/game/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java rename to src/game/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java diff --git a/src/main/java/net/minecraft/network/play/server/S27PacketExplosion.java b/src/game/java/net/minecraft/network/play/server/S27PacketExplosion.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S27PacketExplosion.java rename to src/game/java/net/minecraft/network/play/server/S27PacketExplosion.java diff --git a/src/main/java/net/minecraft/network/play/server/S28PacketEffect.java b/src/game/java/net/minecraft/network/play/server/S28PacketEffect.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S28PacketEffect.java rename to src/game/java/net/minecraft/network/play/server/S28PacketEffect.java diff --git a/src/main/java/net/minecraft/network/play/server/S29PacketSoundEffect.java b/src/game/java/net/minecraft/network/play/server/S29PacketSoundEffect.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S29PacketSoundEffect.java rename to src/game/java/net/minecraft/network/play/server/S29PacketSoundEffect.java diff --git a/src/main/java/net/minecraft/network/play/server/S2APacketParticles.java b/src/game/java/net/minecraft/network/play/server/S2APacketParticles.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S2APacketParticles.java rename to src/game/java/net/minecraft/network/play/server/S2APacketParticles.java diff --git a/src/main/java/net/minecraft/network/play/server/S2BPacketChangeGameState.java b/src/game/java/net/minecraft/network/play/server/S2BPacketChangeGameState.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S2BPacketChangeGameState.java rename to src/game/java/net/minecraft/network/play/server/S2BPacketChangeGameState.java diff --git a/src/main/java/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.java b/src/game/java/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.java rename to src/game/java/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.java diff --git a/src/main/java/net/minecraft/network/play/server/S2DPacketOpenWindow.java b/src/game/java/net/minecraft/network/play/server/S2DPacketOpenWindow.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S2DPacketOpenWindow.java rename to src/game/java/net/minecraft/network/play/server/S2DPacketOpenWindow.java diff --git a/src/main/java/net/minecraft/network/play/server/S2EPacketCloseWindow.java b/src/game/java/net/minecraft/network/play/server/S2EPacketCloseWindow.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S2EPacketCloseWindow.java rename to src/game/java/net/minecraft/network/play/server/S2EPacketCloseWindow.java diff --git a/src/main/java/net/minecraft/network/play/server/S2FPacketSetSlot.java b/src/game/java/net/minecraft/network/play/server/S2FPacketSetSlot.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S2FPacketSetSlot.java rename to src/game/java/net/minecraft/network/play/server/S2FPacketSetSlot.java diff --git a/src/main/java/net/minecraft/network/play/server/S30PacketWindowItems.java b/src/game/java/net/minecraft/network/play/server/S30PacketWindowItems.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S30PacketWindowItems.java rename to src/game/java/net/minecraft/network/play/server/S30PacketWindowItems.java diff --git a/src/main/java/net/minecraft/network/play/server/S31PacketWindowProperty.java b/src/game/java/net/minecraft/network/play/server/S31PacketWindowProperty.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S31PacketWindowProperty.java rename to src/game/java/net/minecraft/network/play/server/S31PacketWindowProperty.java diff --git a/src/main/java/net/minecraft/network/play/server/S32PacketConfirmTransaction.java b/src/game/java/net/minecraft/network/play/server/S32PacketConfirmTransaction.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S32PacketConfirmTransaction.java rename to src/game/java/net/minecraft/network/play/server/S32PacketConfirmTransaction.java diff --git a/src/main/java/net/minecraft/network/play/server/S33PacketUpdateSign.java b/src/game/java/net/minecraft/network/play/server/S33PacketUpdateSign.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S33PacketUpdateSign.java rename to src/game/java/net/minecraft/network/play/server/S33PacketUpdateSign.java diff --git a/src/main/java/net/minecraft/network/play/server/S34PacketMaps.java b/src/game/java/net/minecraft/network/play/server/S34PacketMaps.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S34PacketMaps.java rename to src/game/java/net/minecraft/network/play/server/S34PacketMaps.java diff --git a/src/main/java/net/minecraft/network/play/server/S35PacketUpdateTileEntity.java b/src/game/java/net/minecraft/network/play/server/S35PacketUpdateTileEntity.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S35PacketUpdateTileEntity.java rename to src/game/java/net/minecraft/network/play/server/S35PacketUpdateTileEntity.java diff --git a/src/main/java/net/minecraft/network/play/server/S36PacketSignEditorOpen.java b/src/game/java/net/minecraft/network/play/server/S36PacketSignEditorOpen.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S36PacketSignEditorOpen.java rename to src/game/java/net/minecraft/network/play/server/S36PacketSignEditorOpen.java diff --git a/src/main/java/net/minecraft/network/play/server/S37PacketStatistics.java b/src/game/java/net/minecraft/network/play/server/S37PacketStatistics.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S37PacketStatistics.java rename to src/game/java/net/minecraft/network/play/server/S37PacketStatistics.java diff --git a/src/main/java/net/minecraft/network/play/server/S38PacketPlayerListItem.java b/src/game/java/net/minecraft/network/play/server/S38PacketPlayerListItem.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S38PacketPlayerListItem.java rename to src/game/java/net/minecraft/network/play/server/S38PacketPlayerListItem.java diff --git a/src/main/java/net/minecraft/network/play/server/S39PacketPlayerAbilities.java b/src/game/java/net/minecraft/network/play/server/S39PacketPlayerAbilities.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S39PacketPlayerAbilities.java rename to src/game/java/net/minecraft/network/play/server/S39PacketPlayerAbilities.java diff --git a/src/main/java/net/minecraft/network/play/server/S3APacketTabComplete.java b/src/game/java/net/minecraft/network/play/server/S3APacketTabComplete.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S3APacketTabComplete.java rename to src/game/java/net/minecraft/network/play/server/S3APacketTabComplete.java diff --git a/src/main/java/net/minecraft/network/play/server/S3BPacketScoreboardObjective.java b/src/game/java/net/minecraft/network/play/server/S3BPacketScoreboardObjective.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S3BPacketScoreboardObjective.java rename to src/game/java/net/minecraft/network/play/server/S3BPacketScoreboardObjective.java diff --git a/src/main/java/net/minecraft/network/play/server/S3CPacketUpdateScore.java b/src/game/java/net/minecraft/network/play/server/S3CPacketUpdateScore.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S3CPacketUpdateScore.java rename to src/game/java/net/minecraft/network/play/server/S3CPacketUpdateScore.java diff --git a/src/main/java/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.java b/src/game/java/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.java rename to src/game/java/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.java diff --git a/src/main/java/net/minecraft/network/play/server/S3EPacketTeams.java b/src/game/java/net/minecraft/network/play/server/S3EPacketTeams.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S3EPacketTeams.java rename to src/game/java/net/minecraft/network/play/server/S3EPacketTeams.java diff --git a/src/main/java/net/minecraft/network/play/server/S3FPacketCustomPayload.java b/src/game/java/net/minecraft/network/play/server/S3FPacketCustomPayload.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S3FPacketCustomPayload.java rename to src/game/java/net/minecraft/network/play/server/S3FPacketCustomPayload.java diff --git a/src/main/java/net/minecraft/network/play/server/S40PacketDisconnect.java b/src/game/java/net/minecraft/network/play/server/S40PacketDisconnect.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S40PacketDisconnect.java rename to src/game/java/net/minecraft/network/play/server/S40PacketDisconnect.java diff --git a/src/main/java/net/minecraft/network/play/server/S41PacketServerDifficulty.java b/src/game/java/net/minecraft/network/play/server/S41PacketServerDifficulty.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S41PacketServerDifficulty.java rename to src/game/java/net/minecraft/network/play/server/S41PacketServerDifficulty.java diff --git a/src/main/java/net/minecraft/network/play/server/S42PacketCombatEvent.java b/src/game/java/net/minecraft/network/play/server/S42PacketCombatEvent.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S42PacketCombatEvent.java rename to src/game/java/net/minecraft/network/play/server/S42PacketCombatEvent.java diff --git a/src/main/java/net/minecraft/network/play/server/S43PacketCamera.java b/src/game/java/net/minecraft/network/play/server/S43PacketCamera.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S43PacketCamera.java rename to src/game/java/net/minecraft/network/play/server/S43PacketCamera.java diff --git a/src/main/java/net/minecraft/network/play/server/S44PacketWorldBorder.java b/src/game/java/net/minecraft/network/play/server/S44PacketWorldBorder.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S44PacketWorldBorder.java rename to src/game/java/net/minecraft/network/play/server/S44PacketWorldBorder.java diff --git a/src/main/java/net/minecraft/network/play/server/S45PacketTitle.java b/src/game/java/net/minecraft/network/play/server/S45PacketTitle.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S45PacketTitle.java rename to src/game/java/net/minecraft/network/play/server/S45PacketTitle.java diff --git a/src/main/java/net/minecraft/network/play/server/S46PacketSetCompressionLevel.java b/src/game/java/net/minecraft/network/play/server/S46PacketSetCompressionLevel.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S46PacketSetCompressionLevel.java rename to src/game/java/net/minecraft/network/play/server/S46PacketSetCompressionLevel.java diff --git a/src/main/java/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.java b/src/game/java/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.java rename to src/game/java/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.java diff --git a/src/main/java/net/minecraft/network/play/server/S48PacketResourcePackSend.java b/src/game/java/net/minecraft/network/play/server/S48PacketResourcePackSend.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S48PacketResourcePackSend.java rename to src/game/java/net/minecraft/network/play/server/S48PacketResourcePackSend.java diff --git a/src/main/java/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.java b/src/game/java/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.java rename to src/game/java/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.java diff --git a/src/main/java/net/minecraft/network/play/server/SPacketCooldown.java b/src/game/java/net/minecraft/network/play/server/SPacketCooldown.java similarity index 100% rename from src/main/java/net/minecraft/network/play/server/SPacketCooldown.java rename to src/game/java/net/minecraft/network/play/server/SPacketCooldown.java diff --git a/src/main/java/net/minecraft/pathfinding/Path.java b/src/game/java/net/minecraft/pathfinding/Path.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/Path.java rename to src/game/java/net/minecraft/pathfinding/Path.java diff --git a/src/main/java/net/minecraft/pathfinding/PathEntity.java b/src/game/java/net/minecraft/pathfinding/PathEntity.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/PathEntity.java rename to src/game/java/net/minecraft/pathfinding/PathEntity.java diff --git a/src/main/java/net/minecraft/pathfinding/PathFinder.java b/src/game/java/net/minecraft/pathfinding/PathFinder.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/PathFinder.java rename to src/game/java/net/minecraft/pathfinding/PathFinder.java diff --git a/src/main/java/net/minecraft/pathfinding/PathNavigate.java b/src/game/java/net/minecraft/pathfinding/PathNavigate.java similarity index 98% rename from src/main/java/net/minecraft/pathfinding/PathNavigate.java rename to src/game/java/net/minecraft/pathfinding/PathNavigate.java index bac4c0a..ea1727f 100644 --- a/src/main/java/net/minecraft/pathfinding/PathNavigate.java +++ b/src/game/java/net/minecraft/pathfinding/PathNavigate.java @@ -101,12 +101,10 @@ public abstract class PathNavigate { return null; } else { float f = this.getPathSearchRange(); - this.worldObj.theProfiler.startSection("pathfind"); BlockPos blockpos = new BlockPos(this.theEntity); int i = (int) (f + 8.0F); ChunkCache chunkcache = new ChunkCache(this.worldObj, blockpos.add(-i, -i, -i), blockpos.add(i, i, i), 0); PathEntity pathentity = this.pathFinder.createEntityPathTo(chunkcache, this.theEntity, (BlockPos) pos, f); - this.worldObj.theProfiler.endSection(); return pathentity; } } @@ -139,13 +137,11 @@ public abstract class PathNavigate { return null; } else { float f = this.getPathSearchRange(); - this.worldObj.theProfiler.startSection("pathfind"); BlockPos blockpos = (new BlockPos(this.theEntity)).up(); int i = (int) (f + 16.0F); ChunkCache chunkcache = new ChunkCache(this.worldObj, blockpos.add(-i, -i, -i), blockpos.add(i, i, i), 0); PathEntity pathentity = this.pathFinder.createEntityPathTo(chunkcache, this.theEntity, (Entity) entityIn, f); - this.worldObj.theProfiler.endSection(); return pathentity; } } diff --git a/src/main/java/net/minecraft/pathfinding/PathNavigateClimber.java b/src/game/java/net/minecraft/pathfinding/PathNavigateClimber.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/PathNavigateClimber.java rename to src/game/java/net/minecraft/pathfinding/PathNavigateClimber.java diff --git a/src/main/java/net/minecraft/pathfinding/PathNavigateGround.java b/src/game/java/net/minecraft/pathfinding/PathNavigateGround.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/PathNavigateGround.java rename to src/game/java/net/minecraft/pathfinding/PathNavigateGround.java diff --git a/src/main/java/net/minecraft/pathfinding/PathNavigateSwimmer.java b/src/game/java/net/minecraft/pathfinding/PathNavigateSwimmer.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/PathNavigateSwimmer.java rename to src/game/java/net/minecraft/pathfinding/PathNavigateSwimmer.java diff --git a/src/main/java/net/minecraft/pathfinding/PathPoint.java b/src/game/java/net/minecraft/pathfinding/PathPoint.java similarity index 100% rename from src/main/java/net/minecraft/pathfinding/PathPoint.java rename to src/game/java/net/minecraft/pathfinding/PathPoint.java diff --git a/src/main/java/net/minecraft/potion/Potion.java b/src/game/java/net/minecraft/potion/Potion.java similarity index 100% rename from src/main/java/net/minecraft/potion/Potion.java rename to src/game/java/net/minecraft/potion/Potion.java diff --git a/src/main/java/net/minecraft/potion/PotionAbsorption.java b/src/game/java/net/minecraft/potion/PotionAbsorption.java similarity index 100% rename from src/main/java/net/minecraft/potion/PotionAbsorption.java rename to src/game/java/net/minecraft/potion/PotionAbsorption.java diff --git a/src/main/java/net/minecraft/potion/PotionAttackDamage.java b/src/game/java/net/minecraft/potion/PotionAttackDamage.java similarity index 100% rename from src/main/java/net/minecraft/potion/PotionAttackDamage.java rename to src/game/java/net/minecraft/potion/PotionAttackDamage.java diff --git a/src/main/java/net/minecraft/potion/PotionEffect.java b/src/game/java/net/minecraft/potion/PotionEffect.java similarity index 100% rename from src/main/java/net/minecraft/potion/PotionEffect.java rename to src/game/java/net/minecraft/potion/PotionEffect.java diff --git a/src/main/java/net/minecraft/potion/PotionHealth.java b/src/game/java/net/minecraft/potion/PotionHealth.java similarity index 100% rename from src/main/java/net/minecraft/potion/PotionHealth.java rename to src/game/java/net/minecraft/potion/PotionHealth.java diff --git a/src/main/java/net/minecraft/potion/PotionHealthBoost.java b/src/game/java/net/minecraft/potion/PotionHealthBoost.java similarity index 100% rename from src/main/java/net/minecraft/potion/PotionHealthBoost.java rename to src/game/java/net/minecraft/potion/PotionHealthBoost.java diff --git a/src/main/java/net/minecraft/potion/PotionHelper.java b/src/game/java/net/minecraft/potion/PotionHelper.java similarity index 100% rename from src/main/java/net/minecraft/potion/PotionHelper.java rename to src/game/java/net/minecraft/potion/PotionHelper.java diff --git a/src/main/java/net/minecraft/scoreboard/GoalColor.java b/src/game/java/net/minecraft/scoreboard/GoalColor.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/GoalColor.java rename to src/game/java/net/minecraft/scoreboard/GoalColor.java diff --git a/src/main/java/net/minecraft/scoreboard/IScoreObjectiveCriteria.java b/src/game/java/net/minecraft/scoreboard/IScoreObjectiveCriteria.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/IScoreObjectiveCriteria.java rename to src/game/java/net/minecraft/scoreboard/IScoreObjectiveCriteria.java diff --git a/src/main/java/net/minecraft/scoreboard/Score.java b/src/game/java/net/minecraft/scoreboard/Score.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/Score.java rename to src/game/java/net/minecraft/scoreboard/Score.java diff --git a/src/main/java/net/minecraft/scoreboard/ScoreDummyCriteria.java b/src/game/java/net/minecraft/scoreboard/ScoreDummyCriteria.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/ScoreDummyCriteria.java rename to src/game/java/net/minecraft/scoreboard/ScoreDummyCriteria.java diff --git a/src/main/java/net/minecraft/scoreboard/ScoreHealthCriteria.java b/src/game/java/net/minecraft/scoreboard/ScoreHealthCriteria.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/ScoreHealthCriteria.java rename to src/game/java/net/minecraft/scoreboard/ScoreHealthCriteria.java diff --git a/src/main/java/net/minecraft/scoreboard/ScoreObjective.java b/src/game/java/net/minecraft/scoreboard/ScoreObjective.java similarity index 80% rename from src/main/java/net/minecraft/scoreboard/ScoreObjective.java rename to src/game/java/net/minecraft/scoreboard/ScoreObjective.java index 7b63f5b..30d5656 100644 --- a/src/main/java/net/minecraft/scoreboard/ScoreObjective.java +++ b/src/game/java/net/minecraft/scoreboard/ScoreObjective.java @@ -1,76 +1,90 @@ -package net.minecraft.scoreboard; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ScoreObjective { - private final Scoreboard theScoreboard; - private final String name; - private final IScoreObjectiveCriteria objectiveCriteria; - private IScoreObjectiveCriteria.EnumRenderType renderType; - private String displayName; - - public ScoreObjective(Scoreboard theScoreboardIn, String nameIn, IScoreObjectiveCriteria objectiveCriteriaIn) { - this.theScoreboard = theScoreboardIn; - this.name = nameIn; - this.objectiveCriteria = objectiveCriteriaIn; - this.displayName = nameIn; - this.renderType = objectiveCriteriaIn.getRenderType(); - } - - public Scoreboard getScoreboard() { - return this.theScoreboard; - } - - public String getName() { - return this.name; - } - - public IScoreObjectiveCriteria getCriteria() { - return this.objectiveCriteria; - } - - public String getDisplayName() { - return this.displayName; - } - - public void setDisplayName(String nameIn) { - this.displayName = nameIn; - this.theScoreboard.func_96532_b(this); - } - - public IScoreObjectiveCriteria.EnumRenderType getRenderType() { - return this.renderType; - } - - public void setRenderType(IScoreObjectiveCriteria.EnumRenderType type) { - this.renderType = type; - this.theScoreboard.func_96532_b(this); - } +package net.minecraft.scoreboard; + +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; +import net.minecraft.client.Minecraft; + +/**+ + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ScoreObjective { + private final Scoreboard theScoreboard; + private final String name; + private final IScoreObjectiveCriteria objectiveCriteria; + private IScoreObjectiveCriteria.EnumRenderType renderType; + private String displayName; + private String displayNameProfanityFilter; + + public ScoreObjective(Scoreboard theScoreboardIn, String nameIn, IScoreObjectiveCriteria objectiveCriteriaIn) { + this.theScoreboard = theScoreboardIn; + this.name = nameIn; + this.objectiveCriteria = objectiveCriteriaIn; + this.displayName = nameIn; + this.renderType = objectiveCriteriaIn.getRenderType(); + } + + public Scoreboard getScoreboard() { + return this.theScoreboard; + } + + public String getName() { + return this.name; + } + + public IScoreObjectiveCriteria getCriteria() { + return this.objectiveCriteria; + } + + public String getDisplayName() { + return this.displayName; + } + + public String getDisplayNameProfanityFilter() { + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (displayNameProfanityFilter == null) { + displayNameProfanityFilter = ProfanityFilter.getInstance().profanityFilterString(displayName); + } + return displayNameProfanityFilter; + } else { + return this.displayName; + } + } + + public void setDisplayName(String nameIn) { + this.displayName = nameIn; + this.theScoreboard.func_96532_b(this); + } + + public IScoreObjectiveCriteria.EnumRenderType getRenderType() { + return this.renderType; + } + + public void setRenderType(IScoreObjectiveCriteria.EnumRenderType type) { + this.renderType = type; + this.theScoreboard.func_96532_b(this); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/scoreboard/ScorePlayerTeam.java b/src/game/java/net/minecraft/scoreboard/ScorePlayerTeam.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/ScorePlayerTeam.java rename to src/game/java/net/minecraft/scoreboard/ScorePlayerTeam.java diff --git a/src/main/java/net/minecraft/scoreboard/Scoreboard.java b/src/game/java/net/minecraft/scoreboard/Scoreboard.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/Scoreboard.java rename to src/game/java/net/minecraft/scoreboard/Scoreboard.java diff --git a/src/main/java/net/minecraft/scoreboard/ScoreboardSaveData.java b/src/game/java/net/minecraft/scoreboard/ScoreboardSaveData.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/ScoreboardSaveData.java rename to src/game/java/net/minecraft/scoreboard/ScoreboardSaveData.java diff --git a/src/main/java/net/minecraft/scoreboard/ServerScoreboard.java b/src/game/java/net/minecraft/scoreboard/ServerScoreboard.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/ServerScoreboard.java rename to src/game/java/net/minecraft/scoreboard/ServerScoreboard.java diff --git a/src/main/java/net/minecraft/scoreboard/Team.java b/src/game/java/net/minecraft/scoreboard/Team.java similarity index 100% rename from src/main/java/net/minecraft/scoreboard/Team.java rename to src/game/java/net/minecraft/scoreboard/Team.java diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/game/java/net/minecraft/server/MinecraftServer.java similarity index 94% rename from src/main/java/net/minecraft/server/MinecraftServer.java rename to src/game/java/net/minecraft/server/MinecraftServer.java index f49ba85..b2cdb23 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/game/java/net/minecraft/server/MinecraftServer.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Queue; import java.util.concurrent.Callable; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.futures.FutureTask; @@ -24,7 +25,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.network.play.server.S03PacketTimeUpdate; import net.minecraft.network.play.server.S41PacketServerDifficulty; -import net.minecraft.profiler.Profiler; import net.minecraft.server.management.ServerConfigurationManager; import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentText; @@ -88,7 +88,6 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre */ protected final List playersOnline = Lists.newArrayList(); protected final ICommandManager commandManager; - public final Profiler theProfiler = new Profiler(); private final EaglercraftRandom random = new EaglercraftRandom(); /** * + @@ -134,7 +133,7 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre private boolean startProfiling; private boolean isGamemodeForced; private long nanoTimeSinceStatusRefresh = 0L; - protected final Queue> futureTaskQueue = new LinkedList(); + protected final Queue> futureTaskQueue = new LinkedList<>(); private Thread serverThread; protected long currentTime = getCurrentTimeMillis(); private boolean paused = false; @@ -201,17 +200,16 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre if (j == 0) { if (this.isDemo()) { - this.worldServers[j] = (WorldServer) (new DemoWorldServer(this, isavehandler, worldinfo, b0, - this.theProfiler)).init(); + this.worldServers[j] = (WorldServer) (new DemoWorldServer(this, isavehandler, worldinfo, b0)) + .init(); } else { - this.worldServers[j] = (WorldServer) (new WorldServer(this, isavehandler, worldinfo, b0, - this.theProfiler)).init(); + this.worldServers[j] = (WorldServer) (new WorldServer(this, isavehandler, worldinfo, b0)).init(); } this.worldServers[j].initialize(worldsettings); } else { - this.worldServers[j] = (WorldServer) (new WorldServerMulti(this, isavehandler, b0, this.worldServers[0], - this.theProfiler)).init(); + this.worldServers[j] = (WorldServer) (new WorldServerMulti(this, isavehandler, b0, + this.worldServers[0])).init(); } this.worldServers[j].addWorldAccess(new WorldManager(this, this.worldServers[j])); @@ -484,15 +482,12 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre * Main function called by run() every loop. */ public void tick() { - long i = System.nanoTime(); + long i = EagRuntime.nanoTime(); ++this.tickCounter; if (this.startProfiling) { this.startProfiling = false; - this.theProfiler.profilingEnabled = true; - this.theProfiler.clearProfiling(); } - this.theProfiler.startSection("root"); this.updateTimeLightAndEntities(); boolean loadSpawnChunks = this.worldServers[0].getWorldInfo().getGameRulesInstance() @@ -507,47 +502,31 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre } if (this.tickCounter % 900 == 0) { - this.theProfiler.startSection("save"); this.serverConfigManager.saveAllPlayerData(); this.saveAllWorlds(true); - this.theProfiler.endSection(); } - this.theProfiler.startSection("tallying"); - this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - i; - this.theProfiler.endSection(); - this.theProfiler.startSection("snooper"); - - this.theProfiler.endSection(); - this.theProfiler.endSection(); + this.tickTimeArray[this.tickCounter % 100] = EagRuntime.nanoTime() - i; } public void updateTimeLightAndEntities() { - this.theProfiler.startSection("jobs"); synchronized (this.futureTaskQueue) { while (!this.futureTaskQueue.isEmpty()) { Util.func_181617_a((FutureTask) this.futureTaskQueue.poll(), logger); } } - this.theProfiler.endStartSection("levels"); - for (int j = 0; j < this.worldServers.length; ++j) { - long i = System.nanoTime(); + long i = EagRuntime.nanoTime(); if (j == 0 || this.getAllowNether()) { WorldServer worldserver = this.worldServers[j]; - this.theProfiler.startSection(worldserver.getWorldInfo().getWorldName()); if (this.tickCounter % 20 == 0) { - this.theProfiler.startSection("timeSync"); this.serverConfigManager.sendPacketToAllPlayersInDimension( new S03PacketTimeUpdate(worldserver.getTotalWorldTime(), worldserver.getWorldTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")), worldserver.provider.getDimensionId()); - this.theProfiler.endSection(); } - this.theProfiler.startSection("tick"); - try { worldserver.tick(); } catch (Throwable throwable1) { @@ -565,27 +544,18 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre throw new ReportedException(crashreport1); } - this.theProfiler.endSection(); - this.theProfiler.startSection("tracker"); worldserver.getEntityTracker().updateTrackedEntities(); - this.theProfiler.endSection(); - this.theProfiler.endSection(); } - this.timeOfLastDimensionTick[j][this.tickCounter % 100] = System.nanoTime() - i; + this.timeOfLastDimensionTick[j][this.tickCounter % 100] = EagRuntime.nanoTime() - i; } - this.theProfiler.endStartSection("connection"); EaglerIntegratedServerWorker.tick(); - this.theProfiler.endStartSection("players"); this.serverConfigManager.onTick(); - this.theProfiler.endStartSection("tickables"); for (int k = 0; k < this.playersOnline.size(); ++k) { ((ITickable) this.playersOnline.get(k)).update(); } - - this.theProfiler.endSection(); } public boolean getAllowNether() { @@ -667,9 +637,7 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre public CrashReport addServerInfoToCrashReport(CrashReport crashreport) { crashreport.getCategory().addCrashSectionCallable("Profiler Position", new Callable() { public String call() throws Exception { - return MinecraftServer.this.theProfiler.profilingEnabled - ? MinecraftServer.this.theProfiler.getNameOfLastSection() - : "N/A (disabled)"; + return "N/A (disabled)"; } }); if (this.serverConfigManager != null) { @@ -1020,7 +988,7 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre } public static long getCurrentTimeMillis() { - return System.currentTimeMillis(); + return EagRuntime.steadyTimeMillis(); } public int getMaxPlayerIdleMinutes() { diff --git a/src/main/java/net/minecraft/server/management/ItemInWorldManager.java b/src/game/java/net/minecraft/server/management/ItemInWorldManager.java similarity index 100% rename from src/main/java/net/minecraft/server/management/ItemInWorldManager.java rename to src/game/java/net/minecraft/server/management/ItemInWorldManager.java diff --git a/src/main/java/net/minecraft/server/management/LowerStringMap.java b/src/game/java/net/minecraft/server/management/LowerStringMap.java similarity index 100% rename from src/main/java/net/minecraft/server/management/LowerStringMap.java rename to src/game/java/net/minecraft/server/management/LowerStringMap.java diff --git a/src/main/java/net/minecraft/server/management/PlayerManager.java b/src/game/java/net/minecraft/server/management/PlayerManager.java similarity index 100% rename from src/main/java/net/minecraft/server/management/PlayerManager.java rename to src/game/java/net/minecraft/server/management/PlayerManager.java diff --git a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/game/java/net/minecraft/server/management/ServerConfigurationManager.java similarity index 96% rename from src/main/java/net/minecraft/server/management/ServerConfigurationManager.java rename to src/game/java/net/minecraft/server/management/ServerConfigurationManager.java index c03f605..1899ffa 100644 --- a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/game/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -60,7 +60,12 @@ import net.minecraft.world.border.WorldBorder; import net.minecraft.world.demo.DemoWorldManager; import net.minecraft.world.storage.IPlayerFileData; import net.minecraft.world.storage.WorldInfo; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketUpdateCertEAG; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -123,7 +128,9 @@ public abstract class ServerConfigurationManager { this.maxPlayers = 100; } - public void initializeConnectionToPlayer(IntegratedServerPlayerNetworkManager netManager, EntityPlayerMP playerIn) { + public void initializeConnectionToPlayer(IntegratedServerPlayerNetworkManager netManager, EntityPlayerMP playerIn, + int protocolVersion, EaglercraftUUID clientBrandUUID) { + playerIn.clientBrandUUID = clientBrandUUID; GameProfile gameprofile1 = playerIn.getGameProfile(); NBTTagCompound nbttagcompound = this.readPlayerDataFromFile(playerIn); playerIn.setWorld(this.mcServer.worldServerForDimension(playerIn.dimension)); @@ -137,6 +144,10 @@ public abstract class ServerConfigurationManager { BlockPos blockpos = worldserver.getSpawnPoint(); this.setPlayerGameTypeBasedOnOther(playerIn, (EntityPlayerMP) null, worldserver); NetHandlerPlayServer nethandlerplayserver = new NetHandlerPlayServer(this.mcServer, netManager, playerIn); + nethandlerplayserver.setEaglerMessageController(new GameProtocolMessageController( + GamePluginMessageProtocol.getByVersion(protocolVersion), GamePluginMessageConstants.SERVER_TO_CLIENT, + GameProtocolMessageController.createServerHandler(protocolVersion, nethandlerplayserver), + (ch, msg) -> nethandlerplayserver.sendPacket(new S3FPacketCustomPayload(ch, msg)))); nethandlerplayserver.sendPacket(new S01PacketJoinGame(playerIn.getEntityId(), playerIn.theItemInWorldManager.getGameType(), worldinfo.isHardcoreModeEnabled(), worldserver.provider.getDimensionId(), worldserver.getDifficulty(), this.getMaxPlayers(), @@ -193,11 +204,7 @@ public abstract class ServerConfigurationManager { for (int i = 0, l = playerEntityList.size(); i < l; ++i) { EntityPlayerMP playerItr = playerEntityList.get(i); if (playerItr != playerIn && playerItr.updateCertificate != null) { - nethandlerplayserver - .sendPacket(new S3FPacketCustomPayload("EAG|UpdateCert-1.8", - new PacketBuffer(Unpooled - .buffer(playerItr.updateCertificate, playerItr.updateCertificate.length) - .writerIndex(playerItr.updateCertificate.length)))); + nethandlerplayserver.sendEaglerMessage(new SPacketUpdateCertEAG(playerItr.updateCertificate)); } } } @@ -457,6 +464,8 @@ public abstract class ServerConfigurationManager { EntityPlayerMP entityplayermp = new EntityPlayerMP(this.mcServer, this.mcServer.worldServerForDimension(playerIn.dimension), playerIn.getGameProfile(), (ItemInWorldManager) object); + entityplayermp.updateCertificate = playerIn.updateCertificate; + entityplayermp.clientBrandUUID = playerIn.clientBrandUUID; entityplayermp.playerNetServerHandler = playerIn.playerNetServerHandler; entityplayermp.clonePlayer(playerIn, conqueredEnd); entityplayermp.setEntityId(playerIn.getEntityId()); @@ -540,7 +549,6 @@ public abstract class ServerConfigurationManager { double d1 = entityIn.posZ; double d2 = 8.0D; float f = entityIn.rotationYaw; - parWorldServer.theProfiler.startSection("moving"); if (entityIn.dimension == -1) { d0 = MathHelper.clamp_double(d0 / d2, parWorldServer2.getWorldBorder().minX() + 16.0D, parWorldServer2.getWorldBorder().maxX() - 16.0D); @@ -576,9 +584,7 @@ public abstract class ServerConfigurationManager { } } - parWorldServer.theProfiler.endSection(); if (parInt1 != 1) { - parWorldServer.theProfiler.startSection("placing"); d0 = (double) MathHelper.clamp_int((int) d0, -29999872, 29999872); d1 = (double) MathHelper.clamp_int((int) d1, -29999872, 29999872); if (entityIn.isEntityAlive()) { @@ -587,8 +593,6 @@ public abstract class ServerConfigurationManager { parWorldServer2.spawnEntityInWorld(entityIn); parWorldServer2.updateEntityWithOptionalForce(entityIn, false); } - - parWorldServer.theProfiler.endSection(); } entityIn.setWorld(parWorldServer2); @@ -909,9 +913,9 @@ public abstract class ServerConfigurationManager { String name = playerIn.getName(); StatisticsFile statisticsfile = (StatisticsFile) this.playerStatFiles.get(name); if (statisticsfile == null) { - VFile2 file1 = new VFile2(this.mcServer.worldServerForDimension(0).getSaveHandler().getWorldDirectory(), - "stats"); - VFile2 file2 = new VFile2(file1, name + ".json"); + VFile2 file1 = WorldsDB + .newVFile(this.mcServer.worldServerForDimension(0).getSaveHandler().getWorldDirectory(), "stats"); + VFile2 file2 = WorldsDB.newVFile(file1, name + ".json"); statisticsfile = new StatisticsFile(this.mcServer, file2); statisticsfile.readStatFile(); this.playerStatFiles.put(name, statisticsfile); diff --git a/src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java b/src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java similarity index 79% rename from src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java rename to src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java index 742b6d0..465fe9b 100644 --- a/src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java +++ b/src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java @@ -1,190 +1,226 @@ -package net.minecraft.server.network; - -import com.google.common.base.Charsets; -import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; -import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.EnumConnectionState; -import net.minecraft.network.login.INetHandlerLoginServer; -import net.minecraft.network.login.client.C00PacketLoginStart; -import net.minecraft.network.login.client.C01PacketEncryptionResponse; -import net.minecraft.network.login.server.S00PacketDisconnect; -import net.minecraft.network.login.server.S02PacketLoginSuccess; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.ITickable; -import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; -import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; - -import org.apache.commons.lang3.Validate; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable { - private static final Logger logger = LogManager.getLogger(); - private static final EaglercraftRandom RANDOM = new EaglercraftRandom(); - private final byte[] verifyToken = new byte[4]; - private final MinecraftServer server; - public final IntegratedServerPlayerNetworkManager networkManager; - private NetHandlerLoginServer.LoginState currentLoginState = NetHandlerLoginServer.LoginState.HELLO; - private int connectionTimer; - private GameProfile loginGameProfile; - private byte[] loginSkinPacket; - private byte[] loginCapePacket; - private String serverId = ""; - private EntityPlayerMP field_181025_l; - - public NetHandlerLoginServer(MinecraftServer parMinecraftServer, - IntegratedServerPlayerNetworkManager parNetworkManager) { - this.server = parMinecraftServer; - this.networkManager = parNetworkManager; - RANDOM.nextBytes(this.verifyToken); - } - - /** - * + - * Like the old updateEntity(), except more generic. - */ - public void update() { - if (this.currentLoginState == NetHandlerLoginServer.LoginState.READY_TO_ACCEPT) { - this.tryAcceptPlayer(); - } else if (this.currentLoginState == NetHandlerLoginServer.LoginState.DELAY_ACCEPT) { - EntityPlayerMP entityplayermp = this.server.getConfigurationManager() - .getPlayerByUUID(this.loginGameProfile.getId()); - if (entityplayermp == null) { - this.currentLoginState = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; - this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, - this.field_181025_l); - ((EaglerMinecraftServer) field_181025_l.mcServer).getSkinService() - .processLoginPacket(this.loginSkinPacket, field_181025_l); - if (this.loginCapePacket != null) { - ((EaglerMinecraftServer) field_181025_l.mcServer).getCapeService() - .processLoginPacket(this.loginCapePacket, field_181025_l); - } - IntegratedVoiceService svc = ((EaglerMinecraftServer) field_181025_l.mcServer).getVoiceService(); - if (svc != null) { - svc.handlePlayerLoggedIn(entityplayermp); - } - this.field_181025_l = null; - } - } - - if (this.connectionTimer++ == 600) { - this.closeConnection("Took too long to log in"); - } - - } - - public void closeConnection(String reason) { - try { - logger.info("Disconnecting " + this.getConnectionInfo() + ": " + reason); - ChatComponentText chatcomponenttext = new ChatComponentText(reason); - this.networkManager.sendPacket(new S00PacketDisconnect(chatcomponenttext)); - this.networkManager.closeChannel(chatcomponenttext); - } catch (Exception exception) { - logger.error("Error whilst disconnecting player", exception); - } - - } - - public void tryAcceptPlayer() { - String s = this.server.getConfigurationManager().allowUserToConnect(this.loginGameProfile); - if (s != null) { - this.closeConnection(s); - } else { - this.currentLoginState = NetHandlerLoginServer.LoginState.ACCEPTED; - this.networkManager.sendPacket(new S02PacketLoginSuccess(this.loginGameProfile)); - this.networkManager.setConnectionState(EnumConnectionState.PLAY); - EntityPlayerMP entityplayermp = this.server.getConfigurationManager() - .getPlayerByUUID(this.loginGameProfile.getId()); - if (entityplayermp != null) { - this.currentLoginState = NetHandlerLoginServer.LoginState.DELAY_ACCEPT; - this.field_181025_l = this.server.getConfigurationManager().createPlayerForUser(this.loginGameProfile); - } else { - entityplayermp = this.server.getConfigurationManager().createPlayerForUser(this.loginGameProfile); - this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, entityplayermp); - ((EaglerMinecraftServer) entityplayermp.mcServer).getSkinService() - .processLoginPacket(this.loginSkinPacket, entityplayermp); - if (this.loginCapePacket != null) { - ((EaglerMinecraftServer) entityplayermp.mcServer).getCapeService() - .processLoginPacket(this.loginCapePacket, entityplayermp); - } - IntegratedVoiceService svc = ((EaglerMinecraftServer) entityplayermp.mcServer).getVoiceService(); - if (svc != null) { - svc.handlePlayerLoggedIn(entityplayermp); - } - } - } - - } - - /** - * + - * Invoked when disconnecting, the parameter is a ChatComponent - * describing the reason for termination - */ - public void onDisconnect(IChatComponent ichatcomponent) { - logger.info(this.getConnectionInfo() + " lost connection: " + ichatcomponent.getUnformattedText()); - } - - public String getConnectionInfo() { - return this.loginGameProfile != null - ? this.loginGameProfile.toString() + " (channel:" + this.networkManager.playerChannel + ")" - : ("channel:" + this.networkManager.playerChannel); - } - - public void processLoginStart(C00PacketLoginStart c00packetloginstart) { - Validate.validState(this.currentLoginState == NetHandlerLoginServer.LoginState.HELLO, "Unexpected hello packet", - new Object[0]); - this.loginGameProfile = this.getOfflineProfile(c00packetloginstart.getProfile()); - this.loginSkinPacket = c00packetloginstart.getSkin(); - this.loginCapePacket = c00packetloginstart.getCape(); - this.currentLoginState = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; - } - - public void processEncryptionResponse(C01PacketEncryptionResponse c01packetencryptionresponse) { - - } - - protected GameProfile getOfflineProfile(GameProfile original) { - EaglercraftUUID uuid = EaglercraftUUID - .nameUUIDFromBytes(("OfflinePlayer:" + original.getName()).getBytes(Charsets.UTF_8)); - return new GameProfile(uuid, original.getName()); - } - - static enum LoginState { - HELLO, KEY, AUTHENTICATING, READY_TO_ACCEPT, DELAY_ACCEPT, ACCEPTED; - } +package net.minecraft.server.network; + +import com.google.common.base.Charsets; +import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.EnumConnectionState; +import net.minecraft.network.login.INetHandlerLoginServer; +import net.minecraft.network.login.client.C00PacketLoginStart; +import net.minecraft.network.login.client.C01PacketEncryptionResponse; +import net.minecraft.network.login.server.S00PacketDisconnect; +import net.minecraft.network.login.server.S02PacketLoginSuccess; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.ITickable; +import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; + +import java.io.DataInputStream; +import java.io.IOException; + +import org.apache.commons.lang3.Validate; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable { + private static final Logger logger = LogManager.getLogger(); + private final MinecraftServer server; + public final IntegratedServerPlayerNetworkManager networkManager; + private NetHandlerLoginServer.LoginState currentLoginState = NetHandlerLoginServer.LoginState.HELLO; + private int connectionTimer; + private GameProfile loginGameProfile; + private byte[] loginSkinPacket; + private byte[] loginCapePacket; + private int selectedProtocol = 3; + private EaglercraftUUID clientBrandUUID; + private String serverId = ""; + private EntityPlayerMP field_181025_l; + + public NetHandlerLoginServer(MinecraftServer parMinecraftServer, + IntegratedServerPlayerNetworkManager parNetworkManager) { + this.server = parMinecraftServer; + this.networkManager = parNetworkManager; + } + + /** + * + + * Like the old updateEntity(), except more generic. + */ + public void update() { + if (this.currentLoginState == NetHandlerLoginServer.LoginState.READY_TO_ACCEPT) { + this.tryAcceptPlayer(); + } else if (this.currentLoginState == NetHandlerLoginServer.LoginState.DELAY_ACCEPT) { + EntityPlayerMP entityplayermp = this.server.getConfigurationManager() + .getPlayerByUUID(this.loginGameProfile.getId()); + if (entityplayermp == null) { + this.currentLoginState = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; + this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, + this.field_181025_l, this.selectedProtocol, this.clientBrandUUID); + ((EaglerMinecraftServer) field_181025_l.mcServer).getSkinService() + .processLoginPacket(this.loginSkinPacket, field_181025_l, 3); // singleplayer always sends V3 + // skin in handshake + if (this.loginCapePacket != null) { + ((EaglerMinecraftServer) field_181025_l.mcServer).getCapeService() + .processLoginPacket(this.loginCapePacket, field_181025_l); + } + IntegratedVoiceService svc = ((EaglerMinecraftServer) field_181025_l.mcServer).getVoiceService(); + if (svc != null) { + svc.handlePlayerLoggedIn(entityplayermp); + } + this.field_181025_l = null; + } + } + + if (this.connectionTimer++ == 600) { + this.closeConnection("Took too long to log in"); + } + + } + + public void closeConnection(String reason) { + try { + logger.info("Disconnecting " + this.getConnectionInfo() + ": " + reason); + ChatComponentText chatcomponenttext = new ChatComponentText(reason); + this.networkManager.sendPacket(new S00PacketDisconnect(chatcomponenttext)); + this.networkManager.closeChannel(chatcomponenttext); + } catch (Exception exception) { + logger.error("Error whilst disconnecting player", exception); + } + + } + + public void tryAcceptPlayer() { + String s = this.server.getConfigurationManager().allowUserToConnect(this.loginGameProfile); + if (s != null) { + this.closeConnection(s); + } else { + this.currentLoginState = NetHandlerLoginServer.LoginState.ACCEPTED; + this.networkManager.sendPacket(new S02PacketLoginSuccess(this.loginGameProfile, this.selectedProtocol)); + this.networkManager.setConnectionState(EnumConnectionState.PLAY); + EntityPlayerMP entityplayermp = this.server.getConfigurationManager() + .getPlayerByUUID(this.loginGameProfile.getId()); + if (entityplayermp != null) { + this.currentLoginState = NetHandlerLoginServer.LoginState.DELAY_ACCEPT; + this.field_181025_l = this.server.getConfigurationManager().createPlayerForUser(this.loginGameProfile); + } else { + entityplayermp = this.server.getConfigurationManager().createPlayerForUser(this.loginGameProfile); + this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, entityplayermp, + this.selectedProtocol, this.clientBrandUUID); + ((EaglerMinecraftServer) entityplayermp.mcServer).getSkinService() + .processLoginPacket(this.loginSkinPacket, entityplayermp, 3); // singleplayer always sends V3 + // skin in handshake + if (this.loginCapePacket != null) { + ((EaglerMinecraftServer) entityplayermp.mcServer).getCapeService() + .processLoginPacket(this.loginCapePacket, entityplayermp); + } + IntegratedVoiceService svc = ((EaglerMinecraftServer) entityplayermp.mcServer).getVoiceService(); + if (svc != null) { + svc.handlePlayerLoggedIn(entityplayermp); + } + } + } + + } + + /** + * + + * Invoked when disconnecting, the parameter is a ChatComponent + * describing the reason for termination + */ + public void onDisconnect(IChatComponent ichatcomponent) { + logger.info(this.getConnectionInfo() + " lost connection: " + ichatcomponent.getUnformattedText()); + } + + public String getConnectionInfo() { + return this.loginGameProfile != null + ? this.loginGameProfile.toString() + " (channel:" + this.networkManager.playerChannel + ")" + : ("channel:" + this.networkManager.playerChannel); + } + + public void processLoginStart(C00PacketLoginStart c00packetloginstart) { + Validate.validState(this.currentLoginState == NetHandlerLoginServer.LoginState.HELLO, "Unexpected hello packet", + new Object[0]); + if (c00packetloginstart.getProtocols() != null) { + try { + DataInputStream dis = new DataInputStream(new EaglerInputStream(c00packetloginstart.getProtocols())); + int maxSupported = -1; + int protocolCount = dis.readUnsignedShort(); + for (int i = 0; i < protocolCount; ++i) { + int p = dis.readUnsignedShort(); + if ((p == 3 || p == 4) && p > maxSupported) { + maxSupported = p; + } + } + if (maxSupported != -1) { + selectedProtocol = maxSupported; + } else { + this.closeConnection("Unknown protocol!"); + return; + } + } catch (IOException ex) { + selectedProtocol = 3; + } + } else { + selectedProtocol = 3; + } + this.loginGameProfile = this.getOfflineProfile(c00packetloginstart.getProfile()); + this.loginSkinPacket = c00packetloginstart.getSkin(); + this.loginCapePacket = c00packetloginstart.getCape(); + this.clientBrandUUID = selectedProtocol <= 3 ? EaglercraftVersion.legacyClientUUIDInSharedWorld + : c00packetloginstart.getBrandUUID(); + if (ClientUUIDLoadingCache.PENDING_UUID.equals(clientBrandUUID) + || ClientUUIDLoadingCache.VANILLA_UUID.equals(clientBrandUUID)) { + this.clientBrandUUID = null; + } + this.currentLoginState = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; + } + + public void processEncryptionResponse(C01PacketEncryptionResponse c01packetencryptionresponse) { + + } + + protected GameProfile getOfflineProfile(GameProfile original) { + EaglercraftUUID uuid = EaglercraftUUID + .nameUUIDFromBytes(("OfflinePlayer:" + original.getName()).getBytes(Charsets.UTF_8)); + return new GameProfile(uuid, original.getName()); + } + + static enum LoginState { + HELLO, KEY, AUTHENTICATING, READY_TO_ACCEPT, DELAY_ACCEPT, ACCEPTED; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/stats/Achievement.java b/src/game/java/net/minecraft/stats/Achievement.java similarity index 100% rename from src/main/java/net/minecraft/stats/Achievement.java rename to src/game/java/net/minecraft/stats/Achievement.java diff --git a/src/main/java/net/minecraft/stats/AchievementList.java b/src/game/java/net/minecraft/stats/AchievementList.java similarity index 100% rename from src/main/java/net/minecraft/stats/AchievementList.java rename to src/game/java/net/minecraft/stats/AchievementList.java diff --git a/src/main/java/net/minecraft/stats/IStatStringFormat.java b/src/game/java/net/minecraft/stats/IStatStringFormat.java similarity index 100% rename from src/main/java/net/minecraft/stats/IStatStringFormat.java rename to src/game/java/net/minecraft/stats/IStatStringFormat.java diff --git a/src/main/java/net/minecraft/stats/IStatType.java b/src/game/java/net/minecraft/stats/IStatType.java similarity index 100% rename from src/main/java/net/minecraft/stats/IStatType.java rename to src/game/java/net/minecraft/stats/IStatType.java diff --git a/src/main/java/net/minecraft/stats/ObjectiveStat.java b/src/game/java/net/minecraft/stats/ObjectiveStat.java similarity index 100% rename from src/main/java/net/minecraft/stats/ObjectiveStat.java rename to src/game/java/net/minecraft/stats/ObjectiveStat.java diff --git a/src/main/java/net/minecraft/stats/StatBase.java b/src/game/java/net/minecraft/stats/StatBase.java similarity index 100% rename from src/main/java/net/minecraft/stats/StatBase.java rename to src/game/java/net/minecraft/stats/StatBase.java diff --git a/src/main/java/net/minecraft/stats/StatBasic.java b/src/game/java/net/minecraft/stats/StatBasic.java similarity index 100% rename from src/main/java/net/minecraft/stats/StatBasic.java rename to src/game/java/net/minecraft/stats/StatBasic.java diff --git a/src/main/java/net/minecraft/stats/StatCrafting.java b/src/game/java/net/minecraft/stats/StatCrafting.java similarity index 100% rename from src/main/java/net/minecraft/stats/StatCrafting.java rename to src/game/java/net/minecraft/stats/StatCrafting.java diff --git a/src/main/java/net/minecraft/stats/StatFileWriter.java b/src/game/java/net/minecraft/stats/StatFileWriter.java similarity index 100% rename from src/main/java/net/minecraft/stats/StatFileWriter.java rename to src/game/java/net/minecraft/stats/StatFileWriter.java diff --git a/src/main/java/net/minecraft/stats/StatList.java b/src/game/java/net/minecraft/stats/StatList.java similarity index 100% rename from src/main/java/net/minecraft/stats/StatList.java rename to src/game/java/net/minecraft/stats/StatList.java diff --git a/src/main/java/net/minecraft/stats/StatisticsFile.java b/src/game/java/net/minecraft/stats/StatisticsFile.java similarity index 100% rename from src/main/java/net/minecraft/stats/StatisticsFile.java rename to src/game/java/net/minecraft/stats/StatisticsFile.java diff --git a/src/main/java/net/minecraft/tileentity/IHopper.java b/src/game/java/net/minecraft/tileentity/IHopper.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/IHopper.java rename to src/game/java/net/minecraft/tileentity/IHopper.java diff --git a/src/main/java/net/minecraft/tileentity/MobSpawnerBaseLogic.java b/src/game/java/net/minecraft/tileentity/MobSpawnerBaseLogic.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/MobSpawnerBaseLogic.java rename to src/game/java/net/minecraft/tileentity/MobSpawnerBaseLogic.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntity.java b/src/game/java/net/minecraft/tileentity/TileEntity.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntity.java rename to src/game/java/net/minecraft/tileentity/TileEntity.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityBanner.java b/src/game/java/net/minecraft/tileentity/TileEntityBanner.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityBanner.java rename to src/game/java/net/minecraft/tileentity/TileEntityBanner.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityBeacon.java b/src/game/java/net/minecraft/tileentity/TileEntityBeacon.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityBeacon.java rename to src/game/java/net/minecraft/tileentity/TileEntityBeacon.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityBrewingStand.java b/src/game/java/net/minecraft/tileentity/TileEntityBrewingStand.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityBrewingStand.java rename to src/game/java/net/minecraft/tileentity/TileEntityBrewingStand.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityChest.java b/src/game/java/net/minecraft/tileentity/TileEntityChest.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityChest.java rename to src/game/java/net/minecraft/tileentity/TileEntityChest.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityCommandBlock.java b/src/game/java/net/minecraft/tileentity/TileEntityCommandBlock.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityCommandBlock.java rename to src/game/java/net/minecraft/tileentity/TileEntityCommandBlock.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityComparator.java b/src/game/java/net/minecraft/tileentity/TileEntityComparator.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityComparator.java rename to src/game/java/net/minecraft/tileentity/TileEntityComparator.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityDaylightDetector.java b/src/game/java/net/minecraft/tileentity/TileEntityDaylightDetector.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityDaylightDetector.java rename to src/game/java/net/minecraft/tileentity/TileEntityDaylightDetector.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityDispenser.java b/src/game/java/net/minecraft/tileentity/TileEntityDispenser.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityDispenser.java rename to src/game/java/net/minecraft/tileentity/TileEntityDispenser.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityDropper.java b/src/game/java/net/minecraft/tileentity/TileEntityDropper.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityDropper.java rename to src/game/java/net/minecraft/tileentity/TileEntityDropper.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityEnchantmentTable.java b/src/game/java/net/minecraft/tileentity/TileEntityEnchantmentTable.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityEnchantmentTable.java rename to src/game/java/net/minecraft/tileentity/TileEntityEnchantmentTable.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityEndPortal.java b/src/game/java/net/minecraft/tileentity/TileEntityEndPortal.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityEndPortal.java rename to src/game/java/net/minecraft/tileentity/TileEntityEndPortal.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityEnderChest.java b/src/game/java/net/minecraft/tileentity/TileEntityEnderChest.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityEnderChest.java rename to src/game/java/net/minecraft/tileentity/TileEntityEnderChest.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityFlowerPot.java b/src/game/java/net/minecraft/tileentity/TileEntityFlowerPot.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityFlowerPot.java rename to src/game/java/net/minecraft/tileentity/TileEntityFlowerPot.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityFurnace.java b/src/game/java/net/minecraft/tileentity/TileEntityFurnace.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityFurnace.java rename to src/game/java/net/minecraft/tileentity/TileEntityFurnace.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityHopper.java b/src/game/java/net/minecraft/tileentity/TileEntityHopper.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityHopper.java rename to src/game/java/net/minecraft/tileentity/TileEntityHopper.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityLockable.java b/src/game/java/net/minecraft/tileentity/TileEntityLockable.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityLockable.java rename to src/game/java/net/minecraft/tileentity/TileEntityLockable.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityMobSpawner.java b/src/game/java/net/minecraft/tileentity/TileEntityMobSpawner.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityMobSpawner.java rename to src/game/java/net/minecraft/tileentity/TileEntityMobSpawner.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityNote.java b/src/game/java/net/minecraft/tileentity/TileEntityNote.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityNote.java rename to src/game/java/net/minecraft/tileentity/TileEntityNote.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntityPiston.java b/src/game/java/net/minecraft/tileentity/TileEntityPiston.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntityPiston.java rename to src/game/java/net/minecraft/tileentity/TileEntityPiston.java diff --git a/src/main/java/net/minecraft/tileentity/TileEntitySign.java b/src/game/java/net/minecraft/tileentity/TileEntitySign.java similarity index 87% rename from src/main/java/net/minecraft/tileentity/TileEntitySign.java rename to src/game/java/net/minecraft/tileentity/TileEntitySign.java index dfbb053..5ca9a2d 100644 --- a/src/main/java/net/minecraft/tileentity/TileEntitySign.java +++ b/src/game/java/net/minecraft/tileentity/TileEntitySign.java @@ -1,242 +1,263 @@ -package net.minecraft.tileentity; - -import net.minecraft.command.CommandException; -import net.minecraft.command.CommandResultStats; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.event.ClickEvent; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.Packet; -import net.minecraft.network.play.server.S33PacketUpdateSign; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.BlockPos; -import net.minecraft.util.ChatComponentProcessor; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.ChatStyle; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.Vec3; -import net.minecraft.world.World; - -import org.json.JSONException; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class TileEntitySign extends TileEntity { - public final IChatComponent[] signText = new IChatComponent[] { new ChatComponentText(""), - new ChatComponentText(""), new ChatComponentText(""), new ChatComponentText("") }; - /** - * + - * The index of the line currently being edited. Only used on - * client side, but defined on both. Note this is only really - * used when the > < are going to be visible. - */ - public int lineBeingEdited = -1; - private boolean isEditable = true; - private EntityPlayer player; - private final CommandResultStats stats = new CommandResultStats(); - - public void writeToNBT(NBTTagCompound nbttagcompound) { - super.writeToNBT(nbttagcompound); - - for (int i = 0; i < 4; ++i) { - String s = IChatComponent.Serializer.componentToJson(this.signText[i]); - nbttagcompound.setString("Text" + (i + 1), s); - } - - this.stats.writeStatsToNBT(nbttagcompound); - } - - public void readFromNBT(NBTTagCompound nbttagcompound) { - this.isEditable = false; - super.readFromNBT(nbttagcompound); - ICommandSender icommandsender = new ICommandSender() { - public String getName() { - return "Sign"; - } - - public IChatComponent getDisplayName() { - return new ChatComponentText(this.getName()); - } - - public void addChatMessage(IChatComponent var1) { - } - - public boolean canCommandSenderUseCommand(int var1, String var2) { - return true; - } - - public BlockPos getPosition() { - return TileEntitySign.this.pos; - } - - public Vec3 getPositionVector() { - return new Vec3((double) TileEntitySign.this.pos.getX() + 0.5D, - (double) TileEntitySign.this.pos.getY() + 0.5D, (double) TileEntitySign.this.pos.getZ() + 0.5D); - } - - public World getEntityWorld() { - return TileEntitySign.this.worldObj; - } - - public Entity getCommandSenderEntity() { - return null; - } - - public boolean sendCommandFeedback() { - return false; - } - - public void setCommandStat(CommandResultStats.Type var1, int var2) { - } - }; - - for (int i = 0; i < 4; ++i) { - String s = nbttagcompound.getString("Text" + (i + 1)); - - try { - IChatComponent ichatcomponent = IChatComponent.Serializer.jsonToComponent(s); - - try { - this.signText[i] = ChatComponentProcessor.processComponent(icommandsender, ichatcomponent, - (Entity) null); - } catch (CommandException var7) { - this.signText[i] = ichatcomponent; - } - } catch (JSONException var8) { - this.signText[i] = new ChatComponentText(s); - } - } - - this.stats.readStatsFromNBT(nbttagcompound); - } - - /** - * + - * Allows for a specialized description packet to be created. - * This is often used to sync tile entity data from the server - * to the client easily. For example this is used by signs to - * synchronise the text to be displayed. - */ - public Packet getDescriptionPacket() { - IChatComponent[] aichatcomponent = new IChatComponent[4]; - System.arraycopy(this.signText, 0, aichatcomponent, 0, 4); - return new S33PacketUpdateSign(this.worldObj, this.pos, aichatcomponent); - } - - public boolean func_183000_F() { - return true; - } - - public boolean getIsEditable() { - return this.isEditable; - } - - /** - * + - * Sets the sign's isEditable flag to the specified parameter. - */ - public void setEditable(boolean isEditableIn) { - this.isEditable = isEditableIn; - if (!isEditableIn) { - this.player = null; - } - - } - - public void setPlayer(EntityPlayer playerIn) { - this.player = playerIn; - } - - public EntityPlayer getPlayer() { - return this.player; - } - - public boolean executeCommand(final EntityPlayer playerIn) { - ICommandSender icommandsender = new ICommandSender() { - public String getName() { - return playerIn.getName(); - } - - public IChatComponent getDisplayName() { - return playerIn.getDisplayName(); - } - - public void addChatMessage(IChatComponent var1) { - } - - public boolean canCommandSenderUseCommand(int j, String var2) { - return j <= 2; - } - - public BlockPos getPosition() { - return TileEntitySign.this.pos; - } - - public Vec3 getPositionVector() { - return new Vec3((double) TileEntitySign.this.pos.getX() + 0.5D, - (double) TileEntitySign.this.pos.getY() + 0.5D, (double) TileEntitySign.this.pos.getZ() + 0.5D); - } - - public World getEntityWorld() { - return playerIn.getEntityWorld(); - } - - public Entity getCommandSenderEntity() { - return playerIn; - } - - public boolean sendCommandFeedback() { - return false; - } - - public void setCommandStat(CommandResultStats.Type commandresultstats$type, int j) { - TileEntitySign.this.stats.func_179672_a(this, commandresultstats$type, j); - } - }; - - for (int i = 0; i < this.signText.length; ++i) { - ChatStyle chatstyle = this.signText[i] == null ? null : this.signText[i].getChatStyle(); - if (chatstyle != null && chatstyle.getChatClickEvent() != null) { - ClickEvent clickevent = chatstyle.getChatClickEvent(); - if (clickevent.getAction() == ClickEvent.Action.RUN_COMMAND) { - MinecraftServer.getServer().getCommandManager().executeCommand(icommandsender, - clickevent.getValue()); - } - } - } - - return true; - } - - public CommandResultStats getStats() { - return this.stats; - } +package net.minecraft.tileentity; + +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; +import net.minecraft.client.Minecraft; +import net.minecraft.command.CommandException; +import net.minecraft.command.CommandResultStats; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.event.ClickEvent; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.Packet; +import net.minecraft.network.play.server.S33PacketUpdateSign; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentProcessor; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.Vec3; +import net.minecraft.world.World; + +import org.json.JSONException; + +/** + * + + * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + * + * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" + * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + * + * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, + * ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TileEntitySign extends TileEntity { + public final IChatComponent[] signText = new IChatComponent[] { new ChatComponentText(""), + new ChatComponentText(""), new ChatComponentText(""), new ChatComponentText("") }; + protected IChatComponent[] signTextProfanityFilter = null; + /**+ + * The index of the line currently being edited. Only used on + * client side, but defined on both. Note this is only really + * used when the > < are going to be visible. + */ + public int lineBeingEdited = -1; + private boolean isEditable = true; + private EntityPlayer player; + private final CommandResultStats stats = new CommandResultStats(); + + public void writeToNBT(NBTTagCompound nbttagcompound) { + super.writeToNBT(nbttagcompound); + + for (int i = 0; i < 4; ++i) { + String s = IChatComponent.Serializer.componentToJson(this.signText[i]); + nbttagcompound.setString("Text" + (i + 1), s); + } + + this.stats.writeStatsToNBT(nbttagcompound); + } + + public IChatComponent[] getSignTextProfanityFilter() { + if (Minecraft.getMinecraft().isEnableProfanityFilter()) { + if (signTextProfanityFilter == null) { + signTextProfanityFilter = new IChatComponent[4]; + ProfanityFilter filter = ProfanityFilter.getInstance(); + for (int i = 0; i < 4; ++i) { + signTextProfanityFilter[i] = filter.profanityFilterChatComponent(signText[i]); + } + } + return signTextProfanityFilter; + } else { + return signText; + } + } + + public void clearProfanityFilterCache() { + signTextProfanityFilter = null; + } + + public void readFromNBT(NBTTagCompound nbttagcompound) { + this.isEditable = false; + super.readFromNBT(nbttagcompound); + ICommandSender icommandsender = new ICommandSender() { + public String getName() { + return "Sign"; + } + + public IChatComponent getDisplayName() { + return new ChatComponentText(this.getName()); + } + + public void addChatMessage(IChatComponent var1) { + } + + public boolean canCommandSenderUseCommand(int var1, String var2) { + return true; + } + + public BlockPos getPosition() { + return TileEntitySign.this.pos; + } + + public Vec3 getPositionVector() { + return new Vec3((double) TileEntitySign.this.pos.getX() + 0.5D, + (double) TileEntitySign.this.pos.getY() + 0.5D, (double) TileEntitySign.this.pos.getZ() + 0.5D); + } + + public World getEntityWorld() { + return TileEntitySign.this.worldObj; + } + + public Entity getCommandSenderEntity() { + return null; + } + + public boolean sendCommandFeedback() { + return false; + } + + public void setCommandStat(CommandResultStats.Type var1, int var2) { + } + }; + + for (int i = 0; i < 4; ++i) { + String s = nbttagcompound.getString("Text" + (i + 1)); + + try { + IChatComponent ichatcomponent = IChatComponent.Serializer.jsonToComponent(s); + + try { + this.signText[i] = ChatComponentProcessor.processComponent(icommandsender, ichatcomponent, + (Entity) null); + } catch (CommandException var7) { + this.signText[i] = ichatcomponent; + } + } catch (JSONException var8) { + this.signText[i] = new ChatComponentText(s); + } + } + + this.stats.readStatsFromNBT(nbttagcompound); + } + + /** + * + + * Allows for a specialized description packet to be created. + * This is often used to sync tile entity data from the server + * to the client easily. For example this is used by signs to + * synchronise the text to be displayed. + */ + public Packet getDescriptionPacket() { + IChatComponent[] aichatcomponent = new IChatComponent[4]; + System.arraycopy(this.signText, 0, aichatcomponent, 0, 4); + return new S33PacketUpdateSign(this.worldObj, this.pos, aichatcomponent); + } + + public boolean func_183000_F() { + return true; + } + + public boolean getIsEditable() { + return this.isEditable; + } + + /** + * + + * Sets the sign's isEditable flag to the specified parameter. + */ + public void setEditable(boolean isEditableIn) { + this.isEditable = isEditableIn; + if (!isEditableIn) { + this.player = null; + } + + } + + public void setPlayer(EntityPlayer playerIn) { + this.player = playerIn; + } + + public EntityPlayer getPlayer() { + return this.player; + } + + public boolean executeCommand(final EntityPlayer playerIn) { + ICommandSender icommandsender = new ICommandSender() { + public String getName() { + return playerIn.getName(); + } + + public IChatComponent getDisplayName() { + return playerIn.getDisplayName(); + } + + public void addChatMessage(IChatComponent var1) { + } + + public boolean canCommandSenderUseCommand(int j, String var2) { + return j <= 2; + } + + public BlockPos getPosition() { + return TileEntitySign.this.pos; + } + + public Vec3 getPositionVector() { + return new Vec3((double) TileEntitySign.this.pos.getX() + 0.5D, + (double) TileEntitySign.this.pos.getY() + 0.5D, (double) TileEntitySign.this.pos.getZ() + 0.5D); + } + + public World getEntityWorld() { + return playerIn.getEntityWorld(); + } + + public Entity getCommandSenderEntity() { + return playerIn; + } + + public boolean sendCommandFeedback() { + return false; + } + + public void setCommandStat(CommandResultStats.Type commandresultstats$type, int j) { + TileEntitySign.this.stats.func_179672_a(this, commandresultstats$type, j); + } + }; + + for (int i = 0; i < this.signText.length; ++i) { + ChatStyle chatstyle = this.signText[i] == null ? null : this.signText[i].getChatStyle(); + if (chatstyle != null && chatstyle.getChatClickEvent() != null) { + ClickEvent clickevent = chatstyle.getChatClickEvent(); + if (clickevent.getAction() == ClickEvent.Action.RUN_COMMAND) { + MinecraftServer.getServer().getCommandManager().executeCommand(icommandsender, + clickevent.getValue()); + } + } + } + + return true; + } + + public CommandResultStats getStats() { + return this.stats; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/tileentity/TileEntitySkull.java b/src/game/java/net/minecraft/tileentity/TileEntitySkull.java similarity index 100% rename from src/main/java/net/minecraft/tileentity/TileEntitySkull.java rename to src/game/java/net/minecraft/tileentity/TileEntitySkull.java diff --git a/src/main/java/net/minecraft/util/AxisAlignedBB.java b/src/game/java/net/minecraft/util/AxisAlignedBB.java similarity index 100% rename from src/main/java/net/minecraft/util/AxisAlignedBB.java rename to src/game/java/net/minecraft/util/AxisAlignedBB.java diff --git a/src/main/java/net/minecraft/util/BlockPos.java b/src/game/java/net/minecraft/util/BlockPos.java similarity index 100% rename from src/main/java/net/minecraft/util/BlockPos.java rename to src/game/java/net/minecraft/util/BlockPos.java diff --git a/src/main/java/net/minecraft/util/Cartesian.java b/src/game/java/net/minecraft/util/Cartesian.java similarity index 100% rename from src/main/java/net/minecraft/util/Cartesian.java rename to src/game/java/net/minecraft/util/Cartesian.java diff --git a/src/main/java/net/minecraft/util/ChatAllowedCharacters.java b/src/game/java/net/minecraft/util/ChatAllowedCharacters.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatAllowedCharacters.java rename to src/game/java/net/minecraft/util/ChatAllowedCharacters.java diff --git a/src/main/java/net/minecraft/util/ChatComponentProcessor.java b/src/game/java/net/minecraft/util/ChatComponentProcessor.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatComponentProcessor.java rename to src/game/java/net/minecraft/util/ChatComponentProcessor.java diff --git a/src/main/java/net/minecraft/util/ChatComponentScore.java b/src/game/java/net/minecraft/util/ChatComponentScore.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatComponentScore.java rename to src/game/java/net/minecraft/util/ChatComponentScore.java diff --git a/src/main/java/net/minecraft/util/ChatComponentSelector.java b/src/game/java/net/minecraft/util/ChatComponentSelector.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatComponentSelector.java rename to src/game/java/net/minecraft/util/ChatComponentSelector.java diff --git a/src/main/java/net/minecraft/util/ChatComponentStyle.java b/src/game/java/net/minecraft/util/ChatComponentStyle.java similarity index 98% rename from src/main/java/net/minecraft/util/ChatComponentStyle.java rename to src/game/java/net/minecraft/util/ChatComponentStyle.java index 3579c6b..bb04378 100644 --- a/src/main/java/net/minecraft/util/ChatComponentStyle.java +++ b/src/game/java/net/minecraft/util/ChatComponentStyle.java @@ -94,6 +94,10 @@ public abstract class ChatComponentStyle implements IChatComponent { return this.style; } + public ChatStyle getChatStyleIfPresent() { + return this.style; + } + public Iterator iterator() { return Iterators.concat(Iterators.forArray(new ChatComponentStyle[] { this }), createDeepCopyIterator(this.siblings)); diff --git a/src/main/java/net/minecraft/util/ChatComponentText.java b/src/game/java/net/minecraft/util/ChatComponentText.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatComponentText.java rename to src/game/java/net/minecraft/util/ChatComponentText.java diff --git a/src/main/java/net/minecraft/util/ChatComponentTranslation.java b/src/game/java/net/minecraft/util/ChatComponentTranslation.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatComponentTranslation.java rename to src/game/java/net/minecraft/util/ChatComponentTranslation.java diff --git a/src/main/java/net/minecraft/util/ChatComponentTranslationFormatException.java b/src/game/java/net/minecraft/util/ChatComponentTranslationFormatException.java similarity index 100% rename from src/main/java/net/minecraft/util/ChatComponentTranslationFormatException.java rename to src/game/java/net/minecraft/util/ChatComponentTranslationFormatException.java diff --git a/src/main/java/net/minecraft/util/ChatStyle.java b/src/game/java/net/minecraft/util/ChatStyle.java similarity index 98% rename from src/main/java/net/minecraft/util/ChatStyle.java rename to src/game/java/net/minecraft/util/ChatStyle.java index 1014b0e..6455fc4 100644 --- a/src/main/java/net/minecraft/util/ChatStyle.java +++ b/src/game/java/net/minecraft/util/ChatStyle.java @@ -655,8 +655,15 @@ public class ChatStyle { String jsonprimitive2 = jsonobject2.getString("action"); HoverEvent.Action hoverevent$action = jsonprimitive2 == null ? null : HoverEvent.Action.getValueByCanonicalName(jsonprimitive2); - IChatComponent ichatcomponent = JSONTypeProvider.deserializeNoCast(jsonobject2.get("value"), - IChatComponent.class); + Object val = jsonobject2.opt("value"); + if (val == null) { + val = jsonobject2.opt("contents"); + if (val == null) { + throw new JSONException( + "JSONObject[\"value\"] or JSONObject[\"contents\"] could not be found"); + } + } + IChatComponent ichatcomponent = JSONTypeProvider.deserializeNoCast(val, IChatComponent.class); if (hoverevent$action != null && ichatcomponent != null && hoverevent$action.shouldAllowInChat()) { chatstyle.chatHoverEvent = new HoverEvent(hoverevent$action, ichatcomponent); diff --git a/src/main/java/net/minecraft/util/ClassInheritanceMultiMap.java b/src/game/java/net/minecraft/util/ClassInheritanceMultiMap.java similarity index 100% rename from src/main/java/net/minecraft/util/ClassInheritanceMultiMap.java rename to src/game/java/net/minecraft/util/ClassInheritanceMultiMap.java diff --git a/src/main/java/net/minecraft/util/CombatEntry.java b/src/game/java/net/minecraft/util/CombatEntry.java similarity index 100% rename from src/main/java/net/minecraft/util/CombatEntry.java rename to src/game/java/net/minecraft/util/CombatEntry.java diff --git a/src/main/java/net/minecraft/util/CombatRules.java b/src/game/java/net/minecraft/util/CombatRules.java similarity index 100% rename from src/main/java/net/minecraft/util/CombatRules.java rename to src/game/java/net/minecraft/util/CombatRules.java diff --git a/src/main/java/net/minecraft/util/CombatTracker.java b/src/game/java/net/minecraft/util/CombatTracker.java similarity index 100% rename from src/main/java/net/minecraft/util/CombatTracker.java rename to src/game/java/net/minecraft/util/CombatTracker.java diff --git a/src/main/java/net/minecraft/util/CooldownTracker.java b/src/game/java/net/minecraft/util/CooldownTracker.java similarity index 100% rename from src/main/java/net/minecraft/util/CooldownTracker.java rename to src/game/java/net/minecraft/util/CooldownTracker.java diff --git a/src/main/java/net/minecraft/util/CooldownTrackerServer.java b/src/game/java/net/minecraft/util/CooldownTrackerServer.java similarity index 100% rename from src/main/java/net/minecraft/util/CooldownTrackerServer.java rename to src/game/java/net/minecraft/util/CooldownTrackerServer.java diff --git a/src/main/java/net/minecraft/util/DamageSource.java b/src/game/java/net/minecraft/util/DamageSource.java similarity index 100% rename from src/main/java/net/minecraft/util/DamageSource.java rename to src/game/java/net/minecraft/util/DamageSource.java diff --git a/src/main/java/net/minecraft/util/EnchantmentNameParts.java b/src/game/java/net/minecraft/util/EnchantmentNameParts.java similarity index 100% rename from src/main/java/net/minecraft/util/EnchantmentNameParts.java rename to src/game/java/net/minecraft/util/EnchantmentNameParts.java diff --git a/src/main/java/net/minecraft/util/EntityDamageSource.java b/src/game/java/net/minecraft/util/EntityDamageSource.java similarity index 100% rename from src/main/java/net/minecraft/util/EntityDamageSource.java rename to src/game/java/net/minecraft/util/EntityDamageSource.java diff --git a/src/main/java/net/minecraft/util/EntityDamageSourceIndirect.java b/src/game/java/net/minecraft/util/EntityDamageSourceIndirect.java similarity index 100% rename from src/main/java/net/minecraft/util/EntityDamageSourceIndirect.java rename to src/game/java/net/minecraft/util/EntityDamageSourceIndirect.java diff --git a/src/main/java/net/minecraft/util/EntitySelectors.java b/src/game/java/net/minecraft/util/EntitySelectors.java similarity index 100% rename from src/main/java/net/minecraft/util/EntitySelectors.java rename to src/game/java/net/minecraft/util/EntitySelectors.java diff --git a/src/main/java/net/minecraft/util/EnumChatFormatting.java b/src/game/java/net/minecraft/util/EnumChatFormatting.java similarity index 100% rename from src/main/java/net/minecraft/util/EnumChatFormatting.java rename to src/game/java/net/minecraft/util/EnumChatFormatting.java diff --git a/src/main/java/net/minecraft/util/EnumFacing.java b/src/game/java/net/minecraft/util/EnumFacing.java similarity index 100% rename from src/main/java/net/minecraft/util/EnumFacing.java rename to src/game/java/net/minecraft/util/EnumFacing.java diff --git a/src/main/java/net/minecraft/util/EnumParticleTypes.java b/src/game/java/net/minecraft/util/EnumParticleTypes.java similarity index 100% rename from src/main/java/net/minecraft/util/EnumParticleTypes.java rename to src/game/java/net/minecraft/util/EnumParticleTypes.java diff --git a/src/main/java/net/minecraft/util/EnumWorldBlockLayer.java b/src/game/java/net/minecraft/util/EnumWorldBlockLayer.java similarity index 100% rename from src/main/java/net/minecraft/util/EnumWorldBlockLayer.java rename to src/game/java/net/minecraft/util/EnumWorldBlockLayer.java diff --git a/src/main/java/net/minecraft/util/FoodStats.java b/src/game/java/net/minecraft/util/FoodStats.java similarity index 100% rename from src/main/java/net/minecraft/util/FoodStats.java rename to src/game/java/net/minecraft/util/FoodStats.java diff --git a/src/main/java/net/minecraft/util/FrameTimer.java b/src/game/java/net/minecraft/util/FrameTimer.java similarity index 100% rename from src/main/java/net/minecraft/util/FrameTimer.java rename to src/game/java/net/minecraft/util/FrameTimer.java diff --git a/src/main/java/net/minecraft/util/IChatComponent.java b/src/game/java/net/minecraft/util/IChatComponent.java similarity index 100% rename from src/main/java/net/minecraft/util/IChatComponent.java rename to src/game/java/net/minecraft/util/IChatComponent.java diff --git a/src/main/java/net/minecraft/util/IJsonSerializable.java b/src/game/java/net/minecraft/util/IJsonSerializable.java similarity index 100% rename from src/main/java/net/minecraft/util/IJsonSerializable.java rename to src/game/java/net/minecraft/util/IJsonSerializable.java diff --git a/src/main/java/net/minecraft/util/IObjectIntIterable.java b/src/game/java/net/minecraft/util/IObjectIntIterable.java similarity index 100% rename from src/main/java/net/minecraft/util/IObjectIntIterable.java rename to src/game/java/net/minecraft/util/IObjectIntIterable.java diff --git a/src/main/java/net/minecraft/util/IProgressUpdate.java b/src/game/java/net/minecraft/util/IProgressUpdate.java similarity index 100% rename from src/main/java/net/minecraft/util/IProgressUpdate.java rename to src/game/java/net/minecraft/util/IProgressUpdate.java diff --git a/src/main/java/net/minecraft/util/IRegistry.java b/src/game/java/net/minecraft/util/IRegistry.java similarity index 100% rename from src/main/java/net/minecraft/util/IRegistry.java rename to src/game/java/net/minecraft/util/IRegistry.java diff --git a/src/main/java/net/minecraft/util/IStringSerializable.java b/src/game/java/net/minecraft/util/IStringSerializable.java similarity index 100% rename from src/main/java/net/minecraft/util/IStringSerializable.java rename to src/game/java/net/minecraft/util/IStringSerializable.java diff --git a/src/main/java/net/minecraft/util/IThreadListener.java b/src/game/java/net/minecraft/util/IThreadListener.java similarity index 100% rename from src/main/java/net/minecraft/util/IThreadListener.java rename to src/game/java/net/minecraft/util/IThreadListener.java diff --git a/src/main/java/net/minecraft/util/ITickable.java b/src/game/java/net/minecraft/util/ITickable.java similarity index 100% rename from src/main/java/net/minecraft/util/ITickable.java rename to src/game/java/net/minecraft/util/ITickable.java diff --git a/src/main/java/net/minecraft/util/IntHashMap.java b/src/game/java/net/minecraft/util/IntHashMap.java similarity index 100% rename from src/main/java/net/minecraft/util/IntHashMap.java rename to src/game/java/net/minecraft/util/IntHashMap.java diff --git a/src/main/java/net/minecraft/util/IntegerCache.java b/src/game/java/net/minecraft/util/IntegerCache.java similarity index 100% rename from src/main/java/net/minecraft/util/IntegerCache.java rename to src/game/java/net/minecraft/util/IntegerCache.java diff --git a/src/main/java/net/minecraft/util/JsonSerializableSet.java b/src/game/java/net/minecraft/util/JsonSerializableSet.java similarity index 100% rename from src/main/java/net/minecraft/util/JsonSerializableSet.java rename to src/game/java/net/minecraft/util/JsonSerializableSet.java diff --git a/src/main/java/net/minecraft/util/LazyLoadBase.java b/src/game/java/net/minecraft/util/LazyLoadBase.java similarity index 100% rename from src/main/java/net/minecraft/util/LazyLoadBase.java rename to src/game/java/net/minecraft/util/LazyLoadBase.java diff --git a/src/main/java/net/minecraft/util/LoggingPrintStream.java b/src/game/java/net/minecraft/util/LoggingPrintStream.java similarity index 100% rename from src/main/java/net/minecraft/util/LoggingPrintStream.java rename to src/game/java/net/minecraft/util/LoggingPrintStream.java diff --git a/src/main/java/net/minecraft/util/LongHashMap.java b/src/game/java/net/minecraft/util/LongHashMap.java similarity index 100% rename from src/main/java/net/minecraft/util/LongHashMap.java rename to src/game/java/net/minecraft/util/LongHashMap.java diff --git a/src/main/java/net/minecraft/util/MapPopulator.java b/src/game/java/net/minecraft/util/MapPopulator.java similarity index 100% rename from src/main/java/net/minecraft/util/MapPopulator.java rename to src/game/java/net/minecraft/util/MapPopulator.java diff --git a/src/main/java/net/minecraft/util/MathHelper.java b/src/game/java/net/minecraft/util/MathHelper.java similarity index 100% rename from src/main/java/net/minecraft/util/MathHelper.java rename to src/game/java/net/minecraft/util/MathHelper.java diff --git a/src/main/java/net/minecraft/util/Matrix4f.java b/src/game/java/net/minecraft/util/Matrix4f.java similarity index 100% rename from src/main/java/net/minecraft/util/Matrix4f.java rename to src/game/java/net/minecraft/util/Matrix4f.java diff --git a/src/main/java/net/minecraft/util/MinecraftError.java b/src/game/java/net/minecraft/util/MinecraftError.java similarity index 100% rename from src/main/java/net/minecraft/util/MinecraftError.java rename to src/game/java/net/minecraft/util/MinecraftError.java diff --git a/src/main/java/net/minecraft/util/MouseFilter.java b/src/game/java/net/minecraft/util/MouseFilter.java similarity index 100% rename from src/main/java/net/minecraft/util/MouseFilter.java rename to src/game/java/net/minecraft/util/MouseFilter.java diff --git a/src/main/java/net/minecraft/util/MouseHelper.java b/src/game/java/net/minecraft/util/MouseHelper.java similarity index 91% rename from src/main/java/net/minecraft/util/MouseHelper.java rename to src/game/java/net/minecraft/util/MouseHelper.java index 98788bf..777f572 100644 --- a/src/main/java/net/minecraft/util/MouseHelper.java +++ b/src/game/java/net/minecraft/util/MouseHelper.java @@ -2,6 +2,7 @@ package net.minecraft.util; import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; /** * + @@ -57,7 +58,7 @@ public class MouseHelper { } public void mouseXYChange() { - this.deltaX = Mouse.getDX(); - this.deltaY = Mouse.getDY(); + this.deltaX = PointerInputAbstraction.getVCursorDX(); + this.deltaY = PointerInputAbstraction.getVCursorDY(); } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/util/MovementInput.java b/src/game/java/net/minecraft/util/MovementInput.java similarity index 100% rename from src/main/java/net/minecraft/util/MovementInput.java rename to src/game/java/net/minecraft/util/MovementInput.java diff --git a/src/main/java/net/minecraft/util/MovementInputFromOptions.java b/src/game/java/net/minecraft/util/MovementInputFromOptions.java similarity index 62% rename from src/main/java/net/minecraft/util/MovementInputFromOptions.java rename to src/game/java/net/minecraft/util/MovementInputFromOptions.java index a976922..59dcfc6 100644 --- a/src/main/java/net/minecraft/util/MovementInputFromOptions.java +++ b/src/game/java/net/minecraft/util/MovementInputFromOptions.java @@ -1,5 +1,7 @@ package net.minecraft.util; +import net.lax1dude.eaglercraft.v1_8.touch_gui.EnumTouchControl; +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchControls; import net.minecraft.client.settings.GameSettings; /** @@ -41,24 +43,30 @@ public class MovementInputFromOptions extends MovementInput { public void updatePlayerMoveState() { this.moveStrafe = 0.0F; this.moveForward = 0.0F; - if (this.gameSettings.keyBindForward.isKeyDown()) { + if (this.gameSettings.keyBindForward.isKeyDown() || TouchControls.isPressed(EnumTouchControl.DPAD_UP) + || TouchControls.isPressed(EnumTouchControl.DPAD_UP_LEFT) + || TouchControls.isPressed(EnumTouchControl.DPAD_UP_RIGHT)) { ++this.moveForward; } - if (this.gameSettings.keyBindBack.isKeyDown()) { + if (this.gameSettings.keyBindBack.isKeyDown() || TouchControls.isPressed(EnumTouchControl.DPAD_DOWN)) { --this.moveForward; } - if (this.gameSettings.keyBindLeft.isKeyDown()) { + if (this.gameSettings.keyBindLeft.isKeyDown() || TouchControls.isPressed(EnumTouchControl.DPAD_LEFT) + || TouchControls.isPressed(EnumTouchControl.DPAD_UP_LEFT)) { ++this.moveStrafe; } - if (this.gameSettings.keyBindRight.isKeyDown()) { + if (this.gameSettings.keyBindRight.isKeyDown() || TouchControls.isPressed(EnumTouchControl.DPAD_RIGHT) + || TouchControls.isPressed(EnumTouchControl.DPAD_UP_RIGHT)) { --this.moveStrafe; } - this.jump = this.gameSettings.keyBindJump.isKeyDown(); - this.sneak = this.gameSettings.keyBindSneak.isKeyDown(); + this.jump = this.gameSettings.keyBindJump.isKeyDown() || TouchControls.isPressed(EnumTouchControl.JUMP) + || TouchControls.isPressed(EnumTouchControl.FLY_UP); + this.sneak = this.gameSettings.keyBindSneak.isKeyDown() || TouchControls.getSneakToggled() + || TouchControls.isPressed(EnumTouchControl.FLY_DOWN); if (this.sneak) { this.moveStrafe = (float) ((double) this.moveStrafe * 0.3D); this.moveForward = (float) ((double) this.moveForward * 0.3D); diff --git a/src/main/java/net/minecraft/util/MovingObjectPosition.java b/src/game/java/net/minecraft/util/MovingObjectPosition.java similarity index 100% rename from src/main/java/net/minecraft/util/MovingObjectPosition.java rename to src/game/java/net/minecraft/util/MovingObjectPosition.java diff --git a/src/main/java/net/minecraft/util/ObjectIntIdentityMap.java b/src/game/java/net/minecraft/util/ObjectIntIdentityMap.java similarity index 100% rename from src/main/java/net/minecraft/util/ObjectIntIdentityMap.java rename to src/game/java/net/minecraft/util/ObjectIntIdentityMap.java diff --git a/src/main/java/net/minecraft/util/RegistryDefaulted.java b/src/game/java/net/minecraft/util/RegistryDefaulted.java similarity index 100% rename from src/main/java/net/minecraft/util/RegistryDefaulted.java rename to src/game/java/net/minecraft/util/RegistryDefaulted.java diff --git a/src/main/java/net/minecraft/util/RegistryNamespaced.java b/src/game/java/net/minecraft/util/RegistryNamespaced.java similarity index 100% rename from src/main/java/net/minecraft/util/RegistryNamespaced.java rename to src/game/java/net/minecraft/util/RegistryNamespaced.java diff --git a/src/main/java/net/minecraft/util/RegistryNamespacedDefaultedByKey.java b/src/game/java/net/minecraft/util/RegistryNamespacedDefaultedByKey.java similarity index 100% rename from src/main/java/net/minecraft/util/RegistryNamespacedDefaultedByKey.java rename to src/game/java/net/minecraft/util/RegistryNamespacedDefaultedByKey.java diff --git a/src/main/java/net/minecraft/util/RegistrySimple.java b/src/game/java/net/minecraft/util/RegistrySimple.java similarity index 100% rename from src/main/java/net/minecraft/util/RegistrySimple.java rename to src/game/java/net/minecraft/util/RegistrySimple.java diff --git a/src/main/java/net/minecraft/util/ReportedException.java b/src/game/java/net/minecraft/util/ReportedException.java similarity index 100% rename from src/main/java/net/minecraft/util/ReportedException.java rename to src/game/java/net/minecraft/util/ReportedException.java diff --git a/src/main/java/net/minecraft/util/ResourceLocation.java b/src/game/java/net/minecraft/util/ResourceLocation.java similarity index 100% rename from src/main/java/net/minecraft/util/ResourceLocation.java rename to src/game/java/net/minecraft/util/ResourceLocation.java diff --git a/src/main/java/net/minecraft/util/Rotations.java b/src/game/java/net/minecraft/util/Rotations.java similarity index 100% rename from src/main/java/net/minecraft/util/Rotations.java rename to src/game/java/net/minecraft/util/Rotations.java diff --git a/src/main/java/net/minecraft/util/ScreenShotHelper.java b/src/game/java/net/minecraft/util/ScreenShotHelper.java similarity index 100% rename from src/main/java/net/minecraft/util/ScreenShotHelper.java rename to src/game/java/net/minecraft/util/ScreenShotHelper.java diff --git a/src/main/java/net/minecraft/util/Session.java b/src/game/java/net/minecraft/util/Session.java similarity index 93% rename from src/main/java/net/minecraft/util/Session.java rename to src/game/java/net/minecraft/util/Session.java index 3096688..e4967f9 100644 --- a/src/main/java/net/minecraft/util/Session.java +++ b/src/game/java/net/minecraft/util/Session.java @@ -38,7 +38,7 @@ import net.minecraft.entity.player.EntityPlayer; public class Session { private GameProfile profile; - private static final EaglercraftUUID offlineUUID; + private static final EaglercraftUUID outOfGameUUID; public Session() { reset(); @@ -53,7 +53,7 @@ public class Session { } public void reset() { - update(EaglerProfile.getName(), offlineUUID); + update(EaglerProfile.getName(), outOfGameUUID); } public void setLAN() { @@ -63,7 +63,7 @@ public class Session { static { byte[] bytes = new byte[16]; (new EaglercraftRandom()).nextBytes(bytes); - offlineUUID = new EaglercraftUUID(bytes); + outOfGameUUID = new EaglercraftUUID(bytes); } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/util/StatCollector.java b/src/game/java/net/minecraft/util/StatCollector.java similarity index 100% rename from src/main/java/net/minecraft/util/StatCollector.java rename to src/game/java/net/minecraft/util/StatCollector.java diff --git a/src/main/java/net/minecraft/util/StringTranslate.java b/src/game/java/net/minecraft/util/StringTranslate.java similarity index 95% rename from src/main/java/net/minecraft/util/StringTranslate.java rename to src/game/java/net/minecraft/util/StringTranslate.java index 749fb81..61d17d1 100644 --- a/src/main/java/net/minecraft/util/StringTranslate.java +++ b/src/game/java/net/minecraft/util/StringTranslate.java @@ -74,7 +74,7 @@ public class StringTranslate { } public static void initClient() { - try (InputStream inputstream = EagRuntime.getResourceStream("/assets/minecraft/lang/en_US.lang")) { + try (InputStream inputstream = EagRuntime.getRequiredResourceStream("/assets/minecraft/lang/en_US.lang")) { initServer(IOUtils.readLines(inputstream, StandardCharsets.UTF_8)); fallbackInstance = new StringTranslate(); fallbackInstance.replaceWith(instance.languageList); @@ -101,7 +101,7 @@ public class StringTranslate { } } - instance.lastUpdateTimeInMilliseconds = System.currentTimeMillis(); + instance.lastUpdateTimeInMilliseconds = EagRuntime.steadyTimeMillis(); } /** @@ -120,7 +120,7 @@ public class StringTranslate { public static void replaceWith(Map parMap) { instance.languageList.clear(); instance.languageList.putAll(parMap); - instance.lastUpdateTimeInMilliseconds = System.currentTimeMillis(); + instance.lastUpdateTimeInMilliseconds = EagRuntime.steadyTimeMillis(); } /** diff --git a/src/main/java/net/minecraft/util/StringUtils.java b/src/game/java/net/minecraft/util/StringUtils.java similarity index 97% rename from src/main/java/net/minecraft/util/StringUtils.java rename to src/game/java/net/minecraft/util/StringUtils.java index 3955143..e27e2cc 100644 --- a/src/main/java/net/minecraft/util/StringUtils.java +++ b/src/game/java/net/minecraft/util/StringUtils.java @@ -62,6 +62,6 @@ public class StringUtils { private static final Pattern patternControlCodeAlternate = Pattern.compile("(?i)&([0-9A-FK-OR])"); public static String translateControlCodesAlternate(String parString1) { - return patternControlCodeAlternate.matcher(parString1).replaceAll("\u00A7$1"); + return patternControlCodeAlternate.matcher(parString1).replaceAll(new String(new char[] { 0xA7, '$', '1' })); } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/util/Timer.java b/src/game/java/net/minecraft/util/Timer.java similarity index 95% rename from src/main/java/net/minecraft/util/Timer.java rename to src/game/java/net/minecraft/util/Timer.java index 4ad0d9d..e9601f0 100644 --- a/src/main/java/net/minecraft/util/Timer.java +++ b/src/game/java/net/minecraft/util/Timer.java @@ -1,5 +1,6 @@ package net.minecraft.util; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.minecraft.client.Minecraft; /** @@ -56,7 +57,7 @@ public class Timer { public Timer(float parFloat1) { this.ticksPerSecond = parFloat1; this.lastSyncSysClock = Minecraft.getSystemTime(); - this.lastSyncHRClock = System.nanoTime() / 1000000L; + this.lastSyncHRClock = EagRuntime.nanoTime() / 1000000L; } /** @@ -66,7 +67,7 @@ public class Timer { public void updateTimer() { long i = Minecraft.getSystemTime(); long j = i - this.lastSyncSysClock; - long k = System.nanoTime() / 1000000L; + long k = EagRuntime.nanoTime() / 1000000L; double d0 = (double) k / 1000.0D; if (j <= 1000L && j >= 0L) { this.field_74285_i += j; diff --git a/src/main/java/net/minecraft/util/Tuple.java b/src/game/java/net/minecraft/util/Tuple.java similarity index 100% rename from src/main/java/net/minecraft/util/Tuple.java rename to src/game/java/net/minecraft/util/Tuple.java diff --git a/src/main/java/net/minecraft/util/TupleIntJsonSerializable.java b/src/game/java/net/minecraft/util/TupleIntJsonSerializable.java similarity index 100% rename from src/main/java/net/minecraft/util/TupleIntJsonSerializable.java rename to src/game/java/net/minecraft/util/TupleIntJsonSerializable.java diff --git a/src/main/java/net/minecraft/util/Util.java b/src/game/java/net/minecraft/util/Util.java similarity index 100% rename from src/main/java/net/minecraft/util/Util.java rename to src/game/java/net/minecraft/util/Util.java diff --git a/src/main/java/net/minecraft/util/Vec3.java b/src/game/java/net/minecraft/util/Vec3.java similarity index 100% rename from src/main/java/net/minecraft/util/Vec3.java rename to src/game/java/net/minecraft/util/Vec3.java diff --git a/src/main/java/net/minecraft/util/Vec3i.java b/src/game/java/net/minecraft/util/Vec3i.java similarity index 100% rename from src/main/java/net/minecraft/util/Vec3i.java rename to src/game/java/net/minecraft/util/Vec3i.java diff --git a/src/main/java/net/minecraft/util/Vec4b.java b/src/game/java/net/minecraft/util/Vec4b.java similarity index 100% rename from src/main/java/net/minecraft/util/Vec4b.java rename to src/game/java/net/minecraft/util/Vec4b.java diff --git a/src/main/java/net/minecraft/util/Vector3d.java b/src/game/java/net/minecraft/util/Vector3d.java similarity index 100% rename from src/main/java/net/minecraft/util/Vector3d.java rename to src/game/java/net/minecraft/util/Vector3d.java diff --git a/src/main/java/net/minecraft/util/WeightedRandom.java b/src/game/java/net/minecraft/util/WeightedRandom.java similarity index 100% rename from src/main/java/net/minecraft/util/WeightedRandom.java rename to src/game/java/net/minecraft/util/WeightedRandom.java diff --git a/src/main/java/net/minecraft/util/WeightedRandomChestContent.java b/src/game/java/net/minecraft/util/WeightedRandomChestContent.java similarity index 100% rename from src/main/java/net/minecraft/util/WeightedRandomChestContent.java rename to src/game/java/net/minecraft/util/WeightedRandomChestContent.java diff --git a/src/main/java/net/minecraft/util/WeightedRandomFishable.java b/src/game/java/net/minecraft/util/WeightedRandomFishable.java similarity index 100% rename from src/main/java/net/minecraft/util/WeightedRandomFishable.java rename to src/game/java/net/minecraft/util/WeightedRandomFishable.java diff --git a/src/main/java/net/minecraft/village/MerchantRecipe.java b/src/game/java/net/minecraft/village/MerchantRecipe.java similarity index 100% rename from src/main/java/net/minecraft/village/MerchantRecipe.java rename to src/game/java/net/minecraft/village/MerchantRecipe.java diff --git a/src/main/java/net/minecraft/village/MerchantRecipeList.java b/src/game/java/net/minecraft/village/MerchantRecipeList.java similarity index 100% rename from src/main/java/net/minecraft/village/MerchantRecipeList.java rename to src/game/java/net/minecraft/village/MerchantRecipeList.java diff --git a/src/main/java/net/minecraft/village/Village.java b/src/game/java/net/minecraft/village/Village.java similarity index 100% rename from src/main/java/net/minecraft/village/Village.java rename to src/game/java/net/minecraft/village/Village.java diff --git a/src/main/java/net/minecraft/village/VillageCollection.java b/src/game/java/net/minecraft/village/VillageCollection.java similarity index 100% rename from src/main/java/net/minecraft/village/VillageCollection.java rename to src/game/java/net/minecraft/village/VillageCollection.java diff --git a/src/main/java/net/minecraft/village/VillageDoorInfo.java b/src/game/java/net/minecraft/village/VillageDoorInfo.java similarity index 100% rename from src/main/java/net/minecraft/village/VillageDoorInfo.java rename to src/game/java/net/minecraft/village/VillageDoorInfo.java diff --git a/src/main/java/net/minecraft/village/VillageSiege.java b/src/game/java/net/minecraft/village/VillageSiege.java similarity index 100% rename from src/main/java/net/minecraft/village/VillageSiege.java rename to src/game/java/net/minecraft/village/VillageSiege.java diff --git a/src/main/java/net/minecraft/world/ChunkCache.java b/src/game/java/net/minecraft/world/ChunkCache.java similarity index 100% rename from src/main/java/net/minecraft/world/ChunkCache.java rename to src/game/java/net/minecraft/world/ChunkCache.java diff --git a/src/main/java/net/minecraft/world/ChunkCoordIntPair.java b/src/game/java/net/minecraft/world/ChunkCoordIntPair.java similarity index 100% rename from src/main/java/net/minecraft/world/ChunkCoordIntPair.java rename to src/game/java/net/minecraft/world/ChunkCoordIntPair.java diff --git a/src/main/java/net/minecraft/world/ColorizerFoliage.java b/src/game/java/net/minecraft/world/ColorizerFoliage.java similarity index 100% rename from src/main/java/net/minecraft/world/ColorizerFoliage.java rename to src/game/java/net/minecraft/world/ColorizerFoliage.java diff --git a/src/main/java/net/minecraft/world/ColorizerGrass.java b/src/game/java/net/minecraft/world/ColorizerGrass.java similarity index 100% rename from src/main/java/net/minecraft/world/ColorizerGrass.java rename to src/game/java/net/minecraft/world/ColorizerGrass.java diff --git a/src/main/java/net/minecraft/world/DifficultyInstance.java b/src/game/java/net/minecraft/world/DifficultyInstance.java similarity index 100% rename from src/main/java/net/minecraft/world/DifficultyInstance.java rename to src/game/java/net/minecraft/world/DifficultyInstance.java diff --git a/src/main/java/net/minecraft/world/EnumDifficulty.java b/src/game/java/net/minecraft/world/EnumDifficulty.java similarity index 100% rename from src/main/java/net/minecraft/world/EnumDifficulty.java rename to src/game/java/net/minecraft/world/EnumDifficulty.java diff --git a/src/main/java/net/minecraft/world/EnumSkyBlock.java b/src/game/java/net/minecraft/world/EnumSkyBlock.java similarity index 100% rename from src/main/java/net/minecraft/world/EnumSkyBlock.java rename to src/game/java/net/minecraft/world/EnumSkyBlock.java diff --git a/src/main/java/net/minecraft/world/Explosion.java b/src/game/java/net/minecraft/world/Explosion.java similarity index 100% rename from src/main/java/net/minecraft/world/Explosion.java rename to src/game/java/net/minecraft/world/Explosion.java diff --git a/src/main/java/net/minecraft/world/GameRules.java b/src/game/java/net/minecraft/world/GameRules.java similarity index 98% rename from src/main/java/net/minecraft/world/GameRules.java rename to src/game/java/net/minecraft/world/GameRules.java index 50611cd..7f3b55b 100644 --- a/src/main/java/net/minecraft/world/GameRules.java +++ b/src/game/java/net/minecraft/world/GameRules.java @@ -60,6 +60,7 @@ public class GameRules { this.addGameRule("clickToSit", "true", GameRules.ValueType.BOOLEAN_VALUE); this.addGameRule("colorCodes", "true", GameRules.ValueType.BOOLEAN_VALUE); this.addGameRule("doSignEditing", "true", GameRules.ValueType.BOOLEAN_VALUE); + this.addGameRule("doWeatherCycle", "true", GameRules.ValueType.BOOLEAN_VALUE); } public void addGameRule(String key, String value, GameRules.ValueType type) { diff --git a/src/main/java/net/minecraft/world/IBlockAccess.java b/src/game/java/net/minecraft/world/IBlockAccess.java similarity index 100% rename from src/main/java/net/minecraft/world/IBlockAccess.java rename to src/game/java/net/minecraft/world/IBlockAccess.java diff --git a/src/main/java/net/minecraft/world/IInteractionObject.java b/src/game/java/net/minecraft/world/IInteractionObject.java similarity index 100% rename from src/main/java/net/minecraft/world/IInteractionObject.java rename to src/game/java/net/minecraft/world/IInteractionObject.java diff --git a/src/main/java/net/minecraft/world/ILockableContainer.java b/src/game/java/net/minecraft/world/ILockableContainer.java similarity index 100% rename from src/main/java/net/minecraft/world/ILockableContainer.java rename to src/game/java/net/minecraft/world/ILockableContainer.java diff --git a/src/main/java/net/minecraft/world/IWorldAccess.java b/src/game/java/net/minecraft/world/IWorldAccess.java similarity index 100% rename from src/main/java/net/minecraft/world/IWorldAccess.java rename to src/game/java/net/minecraft/world/IWorldAccess.java diff --git a/src/main/java/net/minecraft/world/IWorldNameable.java b/src/game/java/net/minecraft/world/IWorldNameable.java similarity index 100% rename from src/main/java/net/minecraft/world/IWorldNameable.java rename to src/game/java/net/minecraft/world/IWorldNameable.java diff --git a/src/main/java/net/minecraft/world/LockCode.java b/src/game/java/net/minecraft/world/LockCode.java similarity index 100% rename from src/main/java/net/minecraft/world/LockCode.java rename to src/game/java/net/minecraft/world/LockCode.java diff --git a/src/main/java/net/minecraft/world/MinecraftException.java b/src/game/java/net/minecraft/world/MinecraftException.java similarity index 100% rename from src/main/java/net/minecraft/world/MinecraftException.java rename to src/game/java/net/minecraft/world/MinecraftException.java diff --git a/src/main/java/net/minecraft/world/NextTickListEntry.java b/src/game/java/net/minecraft/world/NextTickListEntry.java similarity index 100% rename from src/main/java/net/minecraft/world/NextTickListEntry.java rename to src/game/java/net/minecraft/world/NextTickListEntry.java diff --git a/src/main/java/net/minecraft/world/SpawnerAnimals.java b/src/game/java/net/minecraft/world/SpawnerAnimals.java similarity index 100% rename from src/main/java/net/minecraft/world/SpawnerAnimals.java rename to src/game/java/net/minecraft/world/SpawnerAnimals.java diff --git a/src/main/java/net/minecraft/world/Teleporter.java b/src/game/java/net/minecraft/world/Teleporter.java similarity index 100% rename from src/main/java/net/minecraft/world/Teleporter.java rename to src/game/java/net/minecraft/world/Teleporter.java diff --git a/src/main/java/net/minecraft/world/World.java b/src/game/java/net/minecraft/world/World.java similarity index 98% rename from src/main/java/net/minecraft/world/World.java rename to src/game/java/net/minecraft/world/World.java index ca8607f..314b693 100644 --- a/src/main/java/net/minecraft/world/World.java +++ b/src/game/java/net/minecraft/world/World.java @@ -15,7 +15,6 @@ import net.hoosiertransfer.Alfheim.ILightInfoProvider; import net.hoosiertransfer.Alfheim.ILightLevelProvider; import net.hoosiertransfer.Alfheim.ILightingEngineProvider; import net.hoosiertransfer.Alfheim.lighting.LightingEngine; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import java.util.Set; @@ -43,7 +42,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.profiler.Profiler; import net.minecraft.scoreboard.Scoreboard; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; @@ -161,8 +159,7 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL protected boolean findingSpawnPoint; protected MapStorage mapStorage; protected VillageCollection villageCollectionObj; - public final Profiler theProfiler; - private final Calendar theCalendar = EagRuntime.getLocaleCalendar(); + private final Calendar theCalendar = Calendar.getInstance(); protected Scoreboard worldScoreboard = new Scoreboard(); /** * + @@ -186,7 +183,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL this.spawnPeacefulMobs = true; this.lightUpdateBlockList = new int['\u8000']; this.saveHandler = saveHandlerIn; - this.theProfiler = profilerIn; this.worldInfo = info; this.provider = providerIn; this.worldBorder = providerIn.getWorldBorder(); @@ -361,9 +357,7 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL Block block1 = iblockstate.getBlock(); if (block.getLightOpacity() != block1.getLightOpacity() || block.getLightValue() != block1.getLightValue()) { - this.theProfiler.startSection("checkLight"); this.checkLight(pos); - this.theProfiler.endSection(); } if ((flags & 2) != 0 && (!this.isRemote || (flags & 4) == 0) && chunk.isPopulated()) { @@ -1366,9 +1360,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL * Updates (and cleans up) entities and tile entities */ public void updateEntities() { - this.theProfiler.startSection("entities"); - this.theProfiler.startSection("global"); - for (int i = 0; i < this.weatherEffects.size(); ++i) { Entity entity = (Entity) this.weatherEffects.get(i); @@ -1392,7 +1383,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } } - this.theProfiler.endStartSection("remove"); this.loadedEntityList.removeAll(this.unloadedEntityList); for (int k = 0; k < this.unloadedEntityList.size(); ++k) { @@ -1409,7 +1399,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } this.unloadedEntityList.clear(); - this.theProfiler.endStartSection("regular"); for (int i1 = 0; i1 < this.loadedEntityList.size(); ++i1) { Entity entity2 = (Entity) this.loadedEntityList.get(i1); @@ -1422,7 +1411,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL entity2.ridingEntity = null; } - this.theProfiler.startSection("tick"); if (!entity2.isDead) { try { this.updateEntity(entity2); @@ -1434,8 +1422,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } } - this.theProfiler.endSection(); - this.theProfiler.startSection("remove"); if (entity2.isDead) { int k1 = entity2.chunkCoordX; int i2 = entity2.chunkCoordZ; @@ -1447,10 +1433,8 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL this.onEntityRemoved(entity2); } - this.theProfiler.endSection(); } - this.theProfiler.endStartSection("blockEntities"); this.processingLoadedTiles = true; Iterator iterator = this.tickableTileEntities.iterator(); @@ -1487,7 +1471,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL this.tileEntitiesToBeRemoved.clear(); } - this.theProfiler.endStartSection("pendingBlockEntities"); if (!this.addedTileEntityList.isEmpty()) { for (int j1 = 0; j1 < this.addedTileEntityList.size(); ++j1) { TileEntity tileentity1 = (TileEntity) this.addedTileEntityList.get(j1); @@ -1508,8 +1491,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL this.addedTileEntityList.clear(); } - this.theProfiler.endSection(); - this.theProfiler.endSection(); } public boolean addTileEntity(TileEntity tile) { @@ -1569,7 +1550,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } } - this.theProfiler.startSection("chunkCheck"); if (Double.isNaN(entityIn.posX) || Double.isInfinite(entityIn.posX)) { entityIn.posX = entityIn.lastTickPosX; } @@ -1608,7 +1588,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } } - this.theProfiler.endSection(); if (forceUpdate && entityIn.addedToChunk && entityIn.riddenByEntity != null) { if (!entityIn.riddenByEntity.isDead && entityIn.riddenByEntity.ridingEntity == entityIn) { this.updateEntity(entityIn.riddenByEntity); @@ -2129,7 +2108,7 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL * Updates all weather states. */ protected void updateWeather() { - if (!this.provider.getHasNoSky()) { + if (!this.provider.getHasNoSky() && this.getGameRules().getBoolean("doWeatherCycle")) { if (!this.isRemote) { int i = this.worldInfo.getCleanWeatherTime(); if (i > 0) { @@ -2191,7 +2170,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL protected void setActivePlayerChunksAndCheckLight() { this.activeChunkSet.clear(); - this.theProfiler.startSection("buildList"); for (int i = 0; i < this.playerEntities.size(); ++i) { EntityPlayer entityplayer = (EntityPlayer) this.playerEntities.get(i); @@ -2206,12 +2184,10 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } } - this.theProfiler.endSection(); if (this.ambientTickCountdown > 0) { --this.ambientTickCountdown; } - this.theProfiler.startSection("playerCheckLight"); if (!this.playerEntities.isEmpty()) { int k1 = this.rand.nextInt(this.playerEntities.size()); EntityPlayer entityplayer1 = (EntityPlayer) this.playerEntities.get(k1); @@ -2221,13 +2197,11 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL this.checkLight(new BlockPos(l1, i2, j2)); } - this.theProfiler.endSection(); } protected abstract int getRenderDistanceChunks(); protected void playMoodSoundAndCheckLight(int chunkIn, int parInt2, Chunk parChunk) { - this.theProfiler.endStartSection("moodSound"); if (this.ambientTickCountdown == 0 && !this.isRemote) { this.updateLCG = this.updateLCG * 3 + 1013904223; int i = this.updateLCG >> 2; @@ -2251,7 +2225,6 @@ public abstract class World implements IBlockAccess, ILightingEngineProvider, IL } } - this.theProfiler.endStartSection("checkLight"); parChunk.enqueueRelightChecks(); } diff --git a/src/main/java/net/minecraft/world/WorldManager.java b/src/game/java/net/minecraft/world/WorldManager.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldManager.java rename to src/game/java/net/minecraft/world/WorldManager.java diff --git a/src/main/java/net/minecraft/world/WorldProvider.java b/src/game/java/net/minecraft/world/WorldProvider.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldProvider.java rename to src/game/java/net/minecraft/world/WorldProvider.java diff --git a/src/main/java/net/minecraft/world/WorldProviderEnd.java b/src/game/java/net/minecraft/world/WorldProviderEnd.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldProviderEnd.java rename to src/game/java/net/minecraft/world/WorldProviderEnd.java diff --git a/src/main/java/net/minecraft/world/WorldProviderHell.java b/src/game/java/net/minecraft/world/WorldProviderHell.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldProviderHell.java rename to src/game/java/net/minecraft/world/WorldProviderHell.java diff --git a/src/main/java/net/minecraft/world/WorldProviderSurface.java b/src/game/java/net/minecraft/world/WorldProviderSurface.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldProviderSurface.java rename to src/game/java/net/minecraft/world/WorldProviderSurface.java diff --git a/src/main/java/net/minecraft/world/WorldSavedData.java b/src/game/java/net/minecraft/world/WorldSavedData.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldSavedData.java rename to src/game/java/net/minecraft/world/WorldSavedData.java diff --git a/src/main/java/net/minecraft/world/WorldServer.java b/src/game/java/net/minecraft/world/WorldServer.java similarity index 97% rename from src/main/java/net/minecraft/world/WorldServer.java rename to src/game/java/net/minecraft/world/WorldServer.java index b6e3134..2b8fe49 100644 --- a/src/main/java/net/minecraft/world/WorldServer.java +++ b/src/game/java/net/minecraft/world/WorldServer.java @@ -38,7 +38,6 @@ import net.minecraft.network.play.server.S27PacketExplosion; import net.minecraft.network.play.server.S2APacketParticles; import net.minecraft.network.play.server.S2BPacketChangeGameState; import net.minecraft.network.play.server.S2CPacketSpawnGlobalEntity; -import net.minecraft.profiler.Profiler; import net.minecraft.scoreboard.ScoreboardSaveData; import net.minecraft.scoreboard.ServerScoreboard; import net.minecraft.server.MinecraftServer; @@ -134,9 +133,8 @@ public class WorldServer extends World implements IThreadListener { new WeightedRandomChestContent(Item.getItemFromBlock(Blocks.log2), 0, 1, 3, 10) }); private List pendingTickListEntriesThisTick = Lists.newArrayList(); - public WorldServer(MinecraftServer server, ISaveHandler saveHandlerIn, WorldInfo info, int dimensionId, - Profiler profilerIn) { - super(saveHandlerIn, info, WorldProvider.getProviderForDimension(dimensionId), profilerIn, false); + public WorldServer(MinecraftServer server, ISaveHandler saveHandlerIn, WorldInfo info, int dimensionId) { + super(saveHandlerIn, info, WorldProvider.getProviderForDimension(dimensionId), false); this.mcServer = server; this.theEntityTracker = new EntityTracker(this); this.thePlayerManager = new PlayerManager(this); @@ -205,14 +203,12 @@ public class WorldServer extends World implements IThreadListener { this.wakeAllPlayers(); } - this.theProfiler.startSection("mobSpawner"); if (this.getGameRules().getBoolean("doMobSpawning") && this.worldInfo.getTerrainType() != WorldType.DEBUG_WORLD) { this.mobSpawner.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs, this.worldInfo.getWorldTotalTime() % 400L == 0L); } - this.theProfiler.endStartSection("chunkSource"); this.chunkProvider.unloadQueuedChunks(); int j = this.calculateSkylightSubtracted(1.0F); if (j != this.getSkylightSubtracted()) { @@ -224,18 +220,12 @@ public class WorldServer extends World implements IThreadListener { this.worldInfo.setWorldTime(this.worldInfo.getWorldTime() + 1L); } - this.theProfiler.endStartSection("tickPending"); this.tickUpdates(false); - this.theProfiler.endStartSection("tickBlocks"); this.updateBlocks(); - this.theProfiler.endStartSection("chunkMap"); this.thePlayerManager.updatePlayerInstances(); - this.theProfiler.endStartSection("village"); this.villageCollectionObj.tick(); this.villageSiege.tick(); - this.theProfiler.endStartSection("portalForcer"); this.worldTeleporter.removeStalePortalLocations(this.getTotalWorldTime()); - this.theProfiler.endSection(); this.sendQueuedBlockEvents(); } @@ -354,12 +344,9 @@ public class WorldServer extends World implements IThreadListener { for (ChunkCoordIntPair chunkcoordintpair : this.activeChunkSet) { int k = chunkcoordintpair.chunkXPos * 16; int l = chunkcoordintpair.chunkZPos * 16; - this.theProfiler.startSection("getChunk"); Chunk chunk = this.getChunkFromChunkCoords(chunkcoordintpair.chunkXPos, chunkcoordintpair.chunkZPos); this.playMoodSoundAndCheckLight(k, l, chunk); - this.theProfiler.endStartSection("tickChunk"); chunk.func_150804_b(false); - this.theProfiler.endStartSection("thunder"); if (this.rand.nextInt(100000) == 0 && this.isRaining() && this.isThundering()) { this.updateLCG = this.updateLCG * 3 + 1013904223; int i1 = this.updateLCG >> 2; @@ -371,7 +358,6 @@ public class WorldServer extends World implements IThreadListener { } } - this.theProfiler.endStartSection("iceandsnow"); if (this.rand.nextInt(16) == 0) { this.updateLCG = this.updateLCG * 3 + 1013904223; int k2 = this.updateLCG >> 2; @@ -391,7 +377,6 @@ public class WorldServer extends World implements IThreadListener { } } - this.theProfiler.endStartSection("tickBlocks"); int l2 = this.getGameRules().getInt("randomTickSpeed"); if (l2 > 0) { ExtendedBlockStorage[] vigg = chunk.getBlockStorageArray(); @@ -418,10 +403,7 @@ public class WorldServer extends World implements IThreadListener { } } } - - this.theProfiler.endSection(); } - } } @@ -538,8 +520,6 @@ public class WorldServer extends World implements IThreadListener { i = 1000; } - this.theProfiler.startSection("cleaning"); - for (int j = 0; j < i; ++j) { NextTickListEntry nextticklistentry = (NextTickListEntry) this.pendingTickListEntriesTreeSet .first(); @@ -552,8 +532,6 @@ public class WorldServer extends World implements IThreadListener { this.pendingTickListEntriesThisTick.add(nextticklistentry); } - this.theProfiler.endSection(); - this.theProfiler.startSection("ticking"); Iterator iterator = this.pendingTickListEntriesThisTick.iterator(); while (iterator.hasNext()) { @@ -584,7 +562,6 @@ public class WorldServer extends World implements IThreadListener { } } - this.theProfiler.endSection(); this.pendingTickListEntriesThisTick.clear(); return !this.pendingTickListEntriesTreeSet.isEmpty(); } diff --git a/src/main/java/net/minecraft/world/WorldServerMulti.java b/src/game/java/net/minecraft/world/WorldServerMulti.java similarity index 95% rename from src/main/java/net/minecraft/world/WorldServerMulti.java rename to src/game/java/net/minecraft/world/WorldServerMulti.java index b8b299c..d97365a 100644 --- a/src/main/java/net/minecraft/world/WorldServerMulti.java +++ b/src/game/java/net/minecraft/world/WorldServerMulti.java @@ -1,6 +1,5 @@ package net.minecraft.world; -import net.minecraft.profiler.Profiler; import net.minecraft.server.MinecraftServer; import net.minecraft.village.VillageCollection; import net.minecraft.world.border.IBorderListener; @@ -40,9 +39,8 @@ import net.minecraft.world.storage.ISaveHandler; public class WorldServerMulti extends WorldServer { private WorldServer delegate; - public WorldServerMulti(MinecraftServer server, ISaveHandler saveHandlerIn, int dimensionId, WorldServer delegate, - Profiler profilerIn) { - super(server, saveHandlerIn, new DerivedWorldInfo(delegate.getWorldInfo()), dimensionId, profilerIn); + public WorldServerMulti(MinecraftServer server, ISaveHandler saveHandlerIn, int dimensionId, WorldServer delegate) { + super(server, saveHandlerIn, new DerivedWorldInfo(delegate.getWorldInfo()), dimensionId); this.delegate = delegate; delegate.getWorldBorder().addListener(new IBorderListener() { public void onSizeChanged(WorldBorder var1, double d0) { diff --git a/src/main/java/net/minecraft/world/WorldSettings.java b/src/game/java/net/minecraft/world/WorldSettings.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldSettings.java rename to src/game/java/net/minecraft/world/WorldSettings.java diff --git a/src/main/java/net/minecraft/world/WorldType.java b/src/game/java/net/minecraft/world/WorldType.java similarity index 100% rename from src/main/java/net/minecraft/world/WorldType.java rename to src/game/java/net/minecraft/world/WorldType.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeCache.java b/src/game/java/net/minecraft/world/biome/BiomeCache.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeCache.java rename to src/game/java/net/minecraft/world/biome/BiomeCache.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeColorHelper.java b/src/game/java/net/minecraft/world/biome/BiomeColorHelper.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeColorHelper.java rename to src/game/java/net/minecraft/world/biome/BiomeColorHelper.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeDecorator.java b/src/game/java/net/minecraft/world/biome/BiomeDecorator.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeDecorator.java rename to src/game/java/net/minecraft/world/biome/BiomeDecorator.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeEndDecorator.java b/src/game/java/net/minecraft/world/biome/BiomeEndDecorator.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeEndDecorator.java rename to src/game/java/net/minecraft/world/biome/BiomeEndDecorator.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenBase.java b/src/game/java/net/minecraft/world/biome/BiomeGenBase.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenBase.java rename to src/game/java/net/minecraft/world/biome/BiomeGenBase.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenBeach.java b/src/game/java/net/minecraft/world/biome/BiomeGenBeach.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenBeach.java rename to src/game/java/net/minecraft/world/biome/BiomeGenBeach.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenDesert.java b/src/game/java/net/minecraft/world/biome/BiomeGenDesert.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenDesert.java rename to src/game/java/net/minecraft/world/biome/BiomeGenDesert.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenEnd.java b/src/game/java/net/minecraft/world/biome/BiomeGenEnd.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenEnd.java rename to src/game/java/net/minecraft/world/biome/BiomeGenEnd.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenForest.java b/src/game/java/net/minecraft/world/biome/BiomeGenForest.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenForest.java rename to src/game/java/net/minecraft/world/biome/BiomeGenForest.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenHell.java b/src/game/java/net/minecraft/world/biome/BiomeGenHell.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenHell.java rename to src/game/java/net/minecraft/world/biome/BiomeGenHell.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenHills.java b/src/game/java/net/minecraft/world/biome/BiomeGenHills.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenHills.java rename to src/game/java/net/minecraft/world/biome/BiomeGenHills.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenJungle.java b/src/game/java/net/minecraft/world/biome/BiomeGenJungle.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenJungle.java rename to src/game/java/net/minecraft/world/biome/BiomeGenJungle.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenMesa.java b/src/game/java/net/minecraft/world/biome/BiomeGenMesa.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenMesa.java rename to src/game/java/net/minecraft/world/biome/BiomeGenMesa.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenMushroomIsland.java b/src/game/java/net/minecraft/world/biome/BiomeGenMushroomIsland.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenMushroomIsland.java rename to src/game/java/net/minecraft/world/biome/BiomeGenMushroomIsland.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenMutated.java b/src/game/java/net/minecraft/world/biome/BiomeGenMutated.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenMutated.java rename to src/game/java/net/minecraft/world/biome/BiomeGenMutated.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenOcean.java b/src/game/java/net/minecraft/world/biome/BiomeGenOcean.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenOcean.java rename to src/game/java/net/minecraft/world/biome/BiomeGenOcean.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenPlains.java b/src/game/java/net/minecraft/world/biome/BiomeGenPlains.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenPlains.java rename to src/game/java/net/minecraft/world/biome/BiomeGenPlains.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenRiver.java b/src/game/java/net/minecraft/world/biome/BiomeGenRiver.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenRiver.java rename to src/game/java/net/minecraft/world/biome/BiomeGenRiver.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenSavanna.java b/src/game/java/net/minecraft/world/biome/BiomeGenSavanna.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenSavanna.java rename to src/game/java/net/minecraft/world/biome/BiomeGenSavanna.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenSnow.java b/src/game/java/net/minecraft/world/biome/BiomeGenSnow.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenSnow.java rename to src/game/java/net/minecraft/world/biome/BiomeGenSnow.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenStoneBeach.java b/src/game/java/net/minecraft/world/biome/BiomeGenStoneBeach.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenStoneBeach.java rename to src/game/java/net/minecraft/world/biome/BiomeGenStoneBeach.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenSwamp.java b/src/game/java/net/minecraft/world/biome/BiomeGenSwamp.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenSwamp.java rename to src/game/java/net/minecraft/world/biome/BiomeGenSwamp.java diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenTaiga.java b/src/game/java/net/minecraft/world/biome/BiomeGenTaiga.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/BiomeGenTaiga.java rename to src/game/java/net/minecraft/world/biome/BiomeGenTaiga.java diff --git a/src/main/java/net/minecraft/world/biome/WorldChunkManager.java b/src/game/java/net/minecraft/world/biome/WorldChunkManager.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/WorldChunkManager.java rename to src/game/java/net/minecraft/world/biome/WorldChunkManager.java diff --git a/src/main/java/net/minecraft/world/biome/WorldChunkManagerHell.java b/src/game/java/net/minecraft/world/biome/WorldChunkManagerHell.java similarity index 100% rename from src/main/java/net/minecraft/world/biome/WorldChunkManagerHell.java rename to src/game/java/net/minecraft/world/biome/WorldChunkManagerHell.java diff --git a/src/main/java/net/minecraft/world/border/EnumBorderStatus.java b/src/game/java/net/minecraft/world/border/EnumBorderStatus.java similarity index 100% rename from src/main/java/net/minecraft/world/border/EnumBorderStatus.java rename to src/game/java/net/minecraft/world/border/EnumBorderStatus.java diff --git a/src/main/java/net/minecraft/world/border/IBorderListener.java b/src/game/java/net/minecraft/world/border/IBorderListener.java similarity index 100% rename from src/main/java/net/minecraft/world/border/IBorderListener.java rename to src/game/java/net/minecraft/world/border/IBorderListener.java diff --git a/src/main/java/net/minecraft/world/border/WorldBorder.java b/src/game/java/net/minecraft/world/border/WorldBorder.java similarity index 96% rename from src/main/java/net/minecraft/world/border/WorldBorder.java rename to src/game/java/net/minecraft/world/border/WorldBorder.java index c79fa96..26e5274 100644 --- a/src/main/java/net/minecraft/world/border/WorldBorder.java +++ b/src/game/java/net/minecraft/world/border/WorldBorder.java @@ -4,6 +4,7 @@ import java.util.List; import com.google.common.collect.Lists; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.minecraft.entity.Entity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.BlockPos; @@ -151,7 +152,7 @@ public class WorldBorder { public double getDiameter() { if (this.getStatus() != EnumBorderStatus.STATIONARY) { - double d0 = (double) ((float) (System.currentTimeMillis() - this.startTime) + double d0 = (double) ((float) (EagRuntime.steadyTimeMillis() - this.startTime) / (float) (this.endTime - this.startTime)); if (d0 < 1.0D) { return this.startDiameter + (this.endDiameter - this.startDiameter) * d0; @@ -164,7 +165,7 @@ public class WorldBorder { } public long getTimeUntilTarget() { - return this.getStatus() != EnumBorderStatus.STATIONARY ? this.endTime - System.currentTimeMillis() : 0L; + return this.getStatus() != EnumBorderStatus.STATIONARY ? this.endTime - EagRuntime.steadyTimeMillis() : 0L; } public double getTargetSize() { @@ -174,7 +175,7 @@ public class WorldBorder { public void setTransition(double newSize) { this.startDiameter = newSize; this.endDiameter = newSize; - this.endTime = System.currentTimeMillis(); + this.endTime = EagRuntime.steadyTimeMillis(); this.startTime = this.endTime; List lst = this.getListeners(); @@ -187,7 +188,7 @@ public class WorldBorder { public void setTransition(double oldSize, double newSize, long time) { this.startDiameter = oldSize; this.endDiameter = newSize; - this.startTime = System.currentTimeMillis(); + this.startTime = EagRuntime.steadyTimeMillis(); this.endTime = this.startTime + time; List lst = this.getListeners(); diff --git a/src/main/java/net/minecraft/world/chunk/Chunk.java b/src/game/java/net/minecraft/world/chunk/Chunk.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/Chunk.java rename to src/game/java/net/minecraft/world/chunk/Chunk.java diff --git a/src/main/java/net/minecraft/world/chunk/ChunkPrimer.java b/src/game/java/net/minecraft/world/chunk/ChunkPrimer.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/ChunkPrimer.java rename to src/game/java/net/minecraft/world/chunk/ChunkPrimer.java diff --git a/src/main/java/net/minecraft/world/chunk/EmptyChunk.java b/src/game/java/net/minecraft/world/chunk/EmptyChunk.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/EmptyChunk.java rename to src/game/java/net/minecraft/world/chunk/EmptyChunk.java diff --git a/src/main/java/net/minecraft/world/chunk/IChunkProvider.java b/src/game/java/net/minecraft/world/chunk/IChunkProvider.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/IChunkProvider.java rename to src/game/java/net/minecraft/world/chunk/IChunkProvider.java diff --git a/src/main/java/net/minecraft/world/chunk/NibbleArray.java b/src/game/java/net/minecraft/world/chunk/NibbleArray.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/NibbleArray.java rename to src/game/java/net/minecraft/world/chunk/NibbleArray.java diff --git a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java b/src/game/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java rename to src/game/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java diff --git a/src/main/java/net/minecraft/world/chunk/storage/ChunkLoader.java b/src/game/java/net/minecraft/world/chunk/storage/ChunkLoader.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/storage/ChunkLoader.java rename to src/game/java/net/minecraft/world/chunk/storage/ChunkLoader.java diff --git a/src/main/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java b/src/game/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java rename to src/game/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java diff --git a/src/main/java/net/minecraft/world/chunk/storage/IChunkLoader.java b/src/game/java/net/minecraft/world/chunk/storage/IChunkLoader.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/storage/IChunkLoader.java rename to src/game/java/net/minecraft/world/chunk/storage/IChunkLoader.java diff --git a/src/main/java/net/minecraft/world/chunk/storage/NibbleArrayReader.java b/src/game/java/net/minecraft/world/chunk/storage/NibbleArrayReader.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/storage/NibbleArrayReader.java rename to src/game/java/net/minecraft/world/chunk/storage/NibbleArrayReader.java diff --git a/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java b/src/game/java/net/minecraft/world/chunk/storage/RegionFile.java similarity index 100% rename from src/main/java/net/minecraft/world/chunk/storage/RegionFile.java rename to src/game/java/net/minecraft/world/chunk/storage/RegionFile.java diff --git a/src/main/java/net/minecraft/world/demo/DemoWorldManager.java b/src/game/java/net/minecraft/world/demo/DemoWorldManager.java similarity index 100% rename from src/main/java/net/minecraft/world/demo/DemoWorldManager.java rename to src/game/java/net/minecraft/world/demo/DemoWorldManager.java diff --git a/src/main/java/net/minecraft/world/demo/DemoWorldServer.java b/src/game/java/net/minecraft/world/demo/DemoWorldServer.java similarity index 91% rename from src/main/java/net/minecraft/world/demo/DemoWorldServer.java rename to src/game/java/net/minecraft/world/demo/DemoWorldServer.java index 56e631e..4da1611 100644 --- a/src/main/java/net/minecraft/world/demo/DemoWorldServer.java +++ b/src/game/java/net/minecraft/world/demo/DemoWorldServer.java @@ -1,6 +1,5 @@ package net.minecraft.world.demo; -import net.minecraft.profiler.Profiler; import net.minecraft.server.MinecraftServer; import net.minecraft.world.WorldServer; import net.minecraft.world.WorldSettings; @@ -44,9 +43,8 @@ public class DemoWorldServer extends WorldServer { public static final WorldSettings demoWorldSettings = (new WorldSettings(demoWorldSeed, WorldSettings.GameType.SURVIVAL, true, false, WorldType.DEFAULT)).enableBonusChest(); - public DemoWorldServer(MinecraftServer server, ISaveHandler saveHandlerIn, WorldInfo worldInfoIn, int dimensionId, - Profiler profilerIn) { - super(server, saveHandlerIn, worldInfoIn, dimensionId, profilerIn); + public DemoWorldServer(MinecraftServer server, ISaveHandler saveHandlerIn, WorldInfo worldInfoIn, int dimensionId) { + super(server, saveHandlerIn, worldInfoIn, dimensionId); this.worldInfo.populateFromWorldSettings(demoWorldSettings); } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderDebug.java b/src/game/java/net/minecraft/world/gen/ChunkProviderDebug.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderDebug.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderDebug.java diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderEnd.java b/src/game/java/net/minecraft/world/gen/ChunkProviderEnd.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderEnd.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderEnd.java diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderFlat.java b/src/game/java/net/minecraft/world/gen/ChunkProviderFlat.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderFlat.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderFlat.java diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderGenerate.java b/src/game/java/net/minecraft/world/gen/ChunkProviderGenerate.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderGenerate.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderGenerate.java diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderHell.java b/src/game/java/net/minecraft/world/gen/ChunkProviderHell.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderHell.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderHell.java diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/game/java/net/minecraft/world/gen/ChunkProviderServer.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderServer.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderServer.java diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderSettings.java b/src/game/java/net/minecraft/world/gen/ChunkProviderSettings.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/ChunkProviderSettings.java rename to src/game/java/net/minecraft/world/gen/ChunkProviderSettings.java diff --git a/src/main/java/net/minecraft/world/gen/FlatGeneratorInfo.java b/src/game/java/net/minecraft/world/gen/FlatGeneratorInfo.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/FlatGeneratorInfo.java rename to src/game/java/net/minecraft/world/gen/FlatGeneratorInfo.java diff --git a/src/main/java/net/minecraft/world/gen/FlatLayerInfo.java b/src/game/java/net/minecraft/world/gen/FlatLayerInfo.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/FlatLayerInfo.java rename to src/game/java/net/minecraft/world/gen/FlatLayerInfo.java diff --git a/src/main/java/net/minecraft/world/gen/GeneratorBushFeature.java b/src/game/java/net/minecraft/world/gen/GeneratorBushFeature.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/GeneratorBushFeature.java rename to src/game/java/net/minecraft/world/gen/GeneratorBushFeature.java diff --git a/src/main/java/net/minecraft/world/gen/MapGenBase.java b/src/game/java/net/minecraft/world/gen/MapGenBase.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/MapGenBase.java rename to src/game/java/net/minecraft/world/gen/MapGenBase.java diff --git a/src/main/java/net/minecraft/world/gen/MapGenCaves.java b/src/game/java/net/minecraft/world/gen/MapGenCaves.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/MapGenCaves.java rename to src/game/java/net/minecraft/world/gen/MapGenCaves.java diff --git a/src/main/java/net/minecraft/world/gen/MapGenCavesHell.java b/src/game/java/net/minecraft/world/gen/MapGenCavesHell.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/MapGenCavesHell.java rename to src/game/java/net/minecraft/world/gen/MapGenCavesHell.java diff --git a/src/main/java/net/minecraft/world/gen/MapGenRavine.java b/src/game/java/net/minecraft/world/gen/MapGenRavine.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/MapGenRavine.java rename to src/game/java/net/minecraft/world/gen/MapGenRavine.java diff --git a/src/main/java/net/minecraft/world/gen/NoiseGenerator.java b/src/game/java/net/minecraft/world/gen/NoiseGenerator.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/NoiseGenerator.java rename to src/game/java/net/minecraft/world/gen/NoiseGenerator.java diff --git a/src/main/java/net/minecraft/world/gen/NoiseGeneratorImproved.java b/src/game/java/net/minecraft/world/gen/NoiseGeneratorImproved.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/NoiseGeneratorImproved.java rename to src/game/java/net/minecraft/world/gen/NoiseGeneratorImproved.java diff --git a/src/main/java/net/minecraft/world/gen/NoiseGeneratorOctaves.java b/src/game/java/net/minecraft/world/gen/NoiseGeneratorOctaves.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/NoiseGeneratorOctaves.java rename to src/game/java/net/minecraft/world/gen/NoiseGeneratorOctaves.java diff --git a/src/main/java/net/minecraft/world/gen/NoiseGeneratorPerlin.java b/src/game/java/net/minecraft/world/gen/NoiseGeneratorPerlin.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/NoiseGeneratorPerlin.java rename to src/game/java/net/minecraft/world/gen/NoiseGeneratorPerlin.java diff --git a/src/main/java/net/minecraft/world/gen/NoiseGeneratorSimplex.java b/src/game/java/net/minecraft/world/gen/NoiseGeneratorSimplex.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/NoiseGeneratorSimplex.java rename to src/game/java/net/minecraft/world/gen/NoiseGeneratorSimplex.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenAbstractTree.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenAbstractTree.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenAbstractTree.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenAbstractTree.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenBigMushroom.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenBigMushroom.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenBigMushroom.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenBigMushroom.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenBigTree.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenBigTree.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenBigTree.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenBigTree.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenBlockBlob.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenBlockBlob.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenBlockBlob.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenBlockBlob.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenCactus.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenCactus.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenCactus.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenCactus.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenCanopyTree.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenCanopyTree.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenCanopyTree.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenCanopyTree.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenClay.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenClay.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenClay.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenClay.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenDeadBush.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenDeadBush.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenDeadBush.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenDeadBush.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenDesertWells.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenDesertWells.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenDesertWells.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenDesertWells.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenDoublePlant.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenDoublePlant.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenDoublePlant.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenDoublePlant.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenDungeons.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenDungeons.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenDungeons.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenDungeons.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenFire.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenFire.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenFire.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenFire.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenFlowers.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenFlowers.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenFlowers.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenFlowers.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenForest.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenForest.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenForest.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenForest.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenGlowStone1.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenGlowStone1.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenGlowStone1.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenGlowStone1.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenGlowStone2.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenGlowStone2.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenGlowStone2.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenGlowStone2.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenHellLava.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenHellLava.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenHellLava.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenHellLava.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenHugeTrees.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenHugeTrees.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenHugeTrees.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenHugeTrees.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenIcePath.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenIcePath.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenIcePath.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenIcePath.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenIceSpike.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenIceSpike.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenIceSpike.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenIceSpike.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenLakes.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenLakes.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenLakes.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenLakes.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenLiquids.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenLiquids.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenLiquids.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenLiquids.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenMegaJungle.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenMegaJungle.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenMegaJungle.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenMegaJungle.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenMegaPineTree.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenMegaPineTree.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenMegaPineTree.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenMegaPineTree.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenMelon.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenMelon.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenMelon.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenMelon.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenMinable.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenMinable.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenMinable.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenMinable.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenPumpkin.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenPumpkin.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenPumpkin.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenPumpkin.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenReed.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenReed.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenReed.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenReed.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenSand.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenSand.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenSand.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenSand.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenSavannaTree.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenSavannaTree.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenSavannaTree.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenSavannaTree.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenShrub.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenShrub.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenShrub.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenShrub.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenSpikes.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenSpikes.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenSpikes.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenSpikes.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenSwamp.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenSwamp.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenSwamp.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenSwamp.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenTaiga1.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenTaiga1.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenTaiga1.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenTaiga1.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenTaiga2.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenTaiga2.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenTaiga2.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenTaiga2.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenTallGrass.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenTallGrass.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenTallGrass.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenTallGrass.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenTrees.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenTrees.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenTrees.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenTrees.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenVines.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenVines.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenVines.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenVines.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenWaterlily.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenWaterlily.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenWaterlily.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenWaterlily.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenerator.java b/src/game/java/net/minecraft/world/gen/feature/WorldGenerator.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGenerator.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGenerator.java diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.java b/src/game/java/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.java rename to src/game/java/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayer.java b/src/game/java/net/minecraft/world/gen/layer/GenLayer.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayer.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayer.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerAddIsland.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerAddIsland.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerAddIsland.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerAddIsland.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerAddSnow.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerAddSnow.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerAddSnow.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerAddSnow.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerBiome.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerBiome.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerBiome.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerBiome.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerBiomeEdge.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerBiomeEdge.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerBiomeEdge.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerBiomeEdge.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerDeepOcean.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerDeepOcean.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerDeepOcean.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerDeepOcean.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerEdge.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerEdge.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerEdge.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerEdge.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerHills.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerHills.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerHills.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerHills.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerIsland.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerIsland.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerIsland.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerIsland.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerRareBiome.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerRareBiome.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerRareBiome.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerRareBiome.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerRiver.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerRiver.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerRiver.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerRiver.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerRiverInit.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerRiverInit.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerRiverInit.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerRiverInit.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerRiverMix.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerRiverMix.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerRiverMix.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerRiverMix.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerShore.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerShore.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerShore.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerShore.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerSmooth.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerSmooth.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerSmooth.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerSmooth.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.java diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerZoom.java b/src/game/java/net/minecraft/world/gen/layer/GenLayerZoom.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/GenLayerZoom.java rename to src/game/java/net/minecraft/world/gen/layer/GenLayerZoom.java diff --git a/src/main/java/net/minecraft/world/gen/layer/IntCache.java b/src/game/java/net/minecraft/world/gen/layer/IntCache.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/layer/IntCache.java rename to src/game/java/net/minecraft/world/gen/layer/IntCache.java diff --git a/src/main/java/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.java b/src/game/java/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.java rename to src/game/java/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenMineshaft.java b/src/game/java/net/minecraft/world/gen/structure/MapGenMineshaft.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenMineshaft.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenMineshaft.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenNetherBridge.java b/src/game/java/net/minecraft/world/gen/structure/MapGenNetherBridge.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenNetherBridge.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenNetherBridge.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenScatteredFeature.java b/src/game/java/net/minecraft/world/gen/structure/MapGenScatteredFeature.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenScatteredFeature.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenScatteredFeature.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenStronghold.java b/src/game/java/net/minecraft/world/gen/structure/MapGenStronghold.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenStronghold.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenStronghold.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenStructure.java b/src/game/java/net/minecraft/world/gen/structure/MapGenStructure.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenStructure.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenStructure.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenStructureData.java b/src/game/java/net/minecraft/world/gen/structure/MapGenStructureData.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenStructureData.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenStructureData.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenStructureIO.java b/src/game/java/net/minecraft/world/gen/structure/MapGenStructureIO.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenStructureIO.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenStructureIO.java diff --git a/src/main/java/net/minecraft/world/gen/structure/MapGenVillage.java b/src/game/java/net/minecraft/world/gen/structure/MapGenVillage.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/MapGenVillage.java rename to src/game/java/net/minecraft/world/gen/structure/MapGenVillage.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureBoundingBox.java b/src/game/java/net/minecraft/world/gen/structure/StructureBoundingBox.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureBoundingBox.java rename to src/game/java/net/minecraft/world/gen/structure/StructureBoundingBox.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureComponent.java b/src/game/java/net/minecraft/world/gen/structure/StructureComponent.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureComponent.java rename to src/game/java/net/minecraft/world/gen/structure/StructureComponent.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureMineshaftPieces.java b/src/game/java/net/minecraft/world/gen/structure/StructureMineshaftPieces.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureMineshaftPieces.java rename to src/game/java/net/minecraft/world/gen/structure/StructureMineshaftPieces.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureMineshaftStart.java b/src/game/java/net/minecraft/world/gen/structure/StructureMineshaftStart.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureMineshaftStart.java rename to src/game/java/net/minecraft/world/gen/structure/StructureMineshaftStart.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureNetherBridgePieces.java b/src/game/java/net/minecraft/world/gen/structure/StructureNetherBridgePieces.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureNetherBridgePieces.java rename to src/game/java/net/minecraft/world/gen/structure/StructureNetherBridgePieces.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureOceanMonument.java b/src/game/java/net/minecraft/world/gen/structure/StructureOceanMonument.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureOceanMonument.java rename to src/game/java/net/minecraft/world/gen/structure/StructureOceanMonument.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.java b/src/game/java/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.java rename to src/game/java/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureStart.java b/src/game/java/net/minecraft/world/gen/structure/StructureStart.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureStart.java rename to src/game/java/net/minecraft/world/gen/structure/StructureStart.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureStrongholdPieces.java b/src/game/java/net/minecraft/world/gen/structure/StructureStrongholdPieces.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureStrongholdPieces.java rename to src/game/java/net/minecraft/world/gen/structure/StructureStrongholdPieces.java diff --git a/src/main/java/net/minecraft/world/gen/structure/StructureVillagePieces.java b/src/game/java/net/minecraft/world/gen/structure/StructureVillagePieces.java similarity index 100% rename from src/main/java/net/minecraft/world/gen/structure/StructureVillagePieces.java rename to src/game/java/net/minecraft/world/gen/structure/StructureVillagePieces.java diff --git a/src/main/java/net/minecraft/world/pathfinder/NodeProcessor.java b/src/game/java/net/minecraft/world/pathfinder/NodeProcessor.java similarity index 100% rename from src/main/java/net/minecraft/world/pathfinder/NodeProcessor.java rename to src/game/java/net/minecraft/world/pathfinder/NodeProcessor.java diff --git a/src/main/java/net/minecraft/world/pathfinder/SwimNodeProcessor.java b/src/game/java/net/minecraft/world/pathfinder/SwimNodeProcessor.java similarity index 100% rename from src/main/java/net/minecraft/world/pathfinder/SwimNodeProcessor.java rename to src/game/java/net/minecraft/world/pathfinder/SwimNodeProcessor.java diff --git a/src/main/java/net/minecraft/world/pathfinder/WalkNodeProcessor.java b/src/game/java/net/minecraft/world/pathfinder/WalkNodeProcessor.java similarity index 100% rename from src/main/java/net/minecraft/world/pathfinder/WalkNodeProcessor.java rename to src/game/java/net/minecraft/world/pathfinder/WalkNodeProcessor.java diff --git a/src/main/java/net/minecraft/world/storage/DerivedWorldInfo.java b/src/game/java/net/minecraft/world/storage/DerivedWorldInfo.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/DerivedWorldInfo.java rename to src/game/java/net/minecraft/world/storage/DerivedWorldInfo.java diff --git a/src/main/java/net/minecraft/world/storage/IPlayerFileData.java b/src/game/java/net/minecraft/world/storage/IPlayerFileData.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/IPlayerFileData.java rename to src/game/java/net/minecraft/world/storage/IPlayerFileData.java diff --git a/src/main/java/net/minecraft/world/storage/ISaveFormat.java b/src/game/java/net/minecraft/world/storage/ISaveFormat.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/ISaveFormat.java rename to src/game/java/net/minecraft/world/storage/ISaveFormat.java diff --git a/src/main/java/net/minecraft/world/storage/ISaveHandler.java b/src/game/java/net/minecraft/world/storage/ISaveHandler.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/ISaveHandler.java rename to src/game/java/net/minecraft/world/storage/ISaveHandler.java diff --git a/src/main/java/net/minecraft/world/storage/MapData.java b/src/game/java/net/minecraft/world/storage/MapData.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/MapData.java rename to src/game/java/net/minecraft/world/storage/MapData.java diff --git a/src/main/java/net/minecraft/world/storage/MapStorage.java b/src/game/java/net/minecraft/world/storage/MapStorage.java similarity index 99% rename from src/main/java/net/minecraft/world/storage/MapStorage.java rename to src/game/java/net/minecraft/world/storage/MapStorage.java index bbeeceb..6ac5e9a 100644 --- a/src/main/java/net/minecraft/world/storage/MapStorage.java +++ b/src/game/java/net/minecraft/world/storage/MapStorage.java @@ -64,7 +64,7 @@ public class MapStorage { WorldSavedData createInstance(String mapFileName); } - public static final Map, MapStorageProvider> storageProviders = new HashMap(); + public static final Map, MapStorageProvider> storageProviders = new HashMap<>(); static { storageProviders.put(MapData.class, MapData::new); diff --git a/src/main/java/net/minecraft/world/storage/SaveDataMemoryStorage.java b/src/game/java/net/minecraft/world/storage/SaveDataMemoryStorage.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/SaveDataMemoryStorage.java rename to src/game/java/net/minecraft/world/storage/SaveDataMemoryStorage.java diff --git a/src/main/java/net/minecraft/world/storage/SaveFormatComparator.java b/src/game/java/net/minecraft/world/storage/SaveFormatComparator.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/SaveFormatComparator.java rename to src/game/java/net/minecraft/world/storage/SaveFormatComparator.java diff --git a/src/main/java/net/minecraft/world/storage/SaveFormatOld.java b/src/game/java/net/minecraft/world/storage/SaveFormatOld.java similarity index 91% rename from src/main/java/net/minecraft/world/storage/SaveFormatOld.java rename to src/game/java/net/minecraft/world/storage/SaveFormatOld.java index 0789c7e..74f86a0 100644 --- a/src/main/java/net/minecraft/world/storage/SaveFormatOld.java +++ b/src/game/java/net/minecraft/world/storage/SaveFormatOld.java @@ -9,6 +9,7 @@ import java.util.List; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; import net.minecraft.util.IProgressUpdate; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -83,11 +84,11 @@ public class SaveFormatOld implements ISaveFormat { * Returns the world's WorldInfo object */ public WorldInfo getWorldInfo(String saveName) { - VFile2 file1 = new VFile2(this.savesDirectory, saveName); + VFile2 file1 = WorldsDB.newVFile(this.savesDirectory, saveName); if (!file1.exists()) { return null; } else { - VFile2 file2 = new VFile2(file1, "level.dat"); + VFile2 file2 = WorldsDB.newVFile(file1, "level.dat"); if (file2.exists()) { try { NBTTagCompound nbttagcompound2; @@ -101,7 +102,7 @@ public class SaveFormatOld implements ISaveFormat { } } - file2 = new VFile2(file1, "level.dat_old"); + file2 = WorldsDB.newVFile(file1, "level.dat_old"); if (file2.exists()) { try { NBTTagCompound nbttagcompound; @@ -125,8 +126,8 @@ public class SaveFormatOld implements ISaveFormat { * does *not* rename the directory containing the world data. */ public boolean renameWorld(String dirName, String newName) { - VFile2 file1 = new VFile2(this.savesDirectory, dirName); - VFile2 file2 = new VFile2(file1, "level.dat"); + VFile2 file1 = WorldsDB.newVFile(this.savesDirectory, dirName); + VFile2 file2 = WorldsDB.newVFile(file1, "level.dat"); { if (file2.exists()) { try { @@ -162,7 +163,7 @@ public class SaveFormatOld implements ISaveFormat { * associated directory recursively. */ public boolean deleteWorldDirectory(String parString1) { - VFile2 file1 = new VFile2(this.savesDirectory, parString1); + VFile2 file1 = WorldsDB.newVFile(this.savesDirectory, parString1); logger.info("Deleting level " + parString1); for (int i = 1; i <= 5; ++i) { @@ -246,7 +247,7 @@ public class SaveFormatOld implements ISaveFormat { * Return whether the given world can be loaded. */ public boolean canLoadWorld(String parString1) { - return (new VFile2(this.savesDirectory, parString1, "level.dat")).exists() - || (new VFile2(this.savesDirectory, parString1, "level.dat_old")).exists(); + return (WorldsDB.newVFile(this.savesDirectory, parString1, "level.dat")).exists() + || (WorldsDB.newVFile(this.savesDirectory, parString1, "level.dat_old")).exists(); } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/world/storage/SaveHandler.java b/src/game/java/net/minecraft/world/storage/SaveHandler.java similarity index 86% rename from src/main/java/net/minecraft/world/storage/SaveHandler.java rename to src/game/java/net/minecraft/world/storage/SaveHandler.java index 0de7cfe..51752e1 100644 --- a/src/main/java/net/minecraft/world/storage/SaveHandler.java +++ b/src/game/java/net/minecraft/world/storage/SaveHandler.java @@ -14,6 +14,7 @@ import net.minecraft.world.chunk.storage.IChunkLoader; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; /** * + @@ -58,9 +59,9 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { private final String saveDirectoryName; public SaveHandler(VFile2 savesDirectory, String directoryName) { - this.worldDirectory = new VFile2(savesDirectory, directoryName); - this.playersDirectory = new VFile2(this.worldDirectory, "player"); - this.mapDataDir = new VFile2(this.worldDirectory, "data"); + this.worldDirectory = WorldsDB.newVFile(savesDirectory, directoryName); + this.playersDirectory = WorldsDB.newVFile(this.worldDirectory, "player"); + this.mapDataDir = WorldsDB.newVFile(this.worldDirectory, "data"); this.saveDirectoryName = directoryName; } @@ -95,7 +96,7 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { * Loads and returns the world info */ public WorldInfo loadWorldInfo() { - VFile2 file1 = new VFile2(this.worldDirectory, "level.dat"); + VFile2 file1 = WorldsDB.newVFile(this.worldDirectory, "level.dat"); if (file1.exists()) { try (InputStream is = file1.getInputStream()) { NBTTagCompound nbttagcompound2 = CompressedStreamTools.readCompressed(is); @@ -107,7 +108,7 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { } } - file1 = new VFile2(this.worldDirectory, "level.dat_old"); + file1 = WorldsDB.newVFile(this.worldDirectory, "level.dat_old"); if (file1.exists()) { try (InputStream is = file1.getInputStream()) { NBTTagCompound nbttagcompound = CompressedStreamTools.readCompressed(is); @@ -133,9 +134,9 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { nbttagcompound2.setTag("Data", nbttagcompound1); try { - VFile2 file1 = new VFile2(this.worldDirectory, "level.dat_new"); - VFile2 file2 = new VFile2(this.worldDirectory, "level.dat_old"); - VFile2 file3 = new VFile2(this.worldDirectory, "level.dat"); + VFile2 file1 = WorldsDB.newVFile(this.worldDirectory, "level.dat_new"); + VFile2 file2 = WorldsDB.newVFile(this.worldDirectory, "level.dat_old"); + VFile2 file3 = WorldsDB.newVFile(this.worldDirectory, "level.dat"); try (OutputStream os = file1.getOutputStream()) { CompressedStreamTools.writeCompressed(nbttagcompound2, os); } @@ -169,9 +170,9 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { nbttagcompound1.setTag("Data", nbttagcompound); try { - VFile2 file1 = new VFile2(this.worldDirectory, "level.dat_new"); - VFile2 file2 = new VFile2(this.worldDirectory, "level.dat_old"); - VFile2 file3 = new VFile2(this.worldDirectory, "level.dat"); + VFile2 file1 = WorldsDB.newVFile(this.worldDirectory, "level.dat_new"); + VFile2 file2 = WorldsDB.newVFile(this.worldDirectory, "level.dat_old"); + VFile2 file3 = WorldsDB.newVFile(this.worldDirectory, "level.dat"); try (OutputStream os = file1.getOutputStream()) { CompressedStreamTools.writeCompressed(nbttagcompound1, os); } @@ -205,8 +206,8 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { NBTTagCompound nbttagcompound = new NBTTagCompound(); player.writeToNBT(nbttagcompound); String s = player.getName().toLowerCase(); - VFile2 file1 = new VFile2(this.playersDirectory, s + ".dat.tmp"); - VFile2 file2 = new VFile2(this.playersDirectory, s + ".dat"); + VFile2 file1 = WorldsDB.newVFile(this.playersDirectory, s + ".dat.tmp"); + VFile2 file2 = WorldsDB.newVFile(this.playersDirectory, s + ".dat"); try (OutputStream os = file1.getOutputStream()) { CompressedStreamTools.writeCompressed(nbttagcompound, os); } @@ -231,7 +232,7 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { NBTTagCompound nbttagcompound = null; try { - VFile2 file1 = new VFile2(this.playersDirectory, player.getName().toLowerCase() + ".dat"); + VFile2 file1 = WorldsDB.newVFile(this.playersDirectory, player.getName().toLowerCase() + ".dat"); if (file1.exists()) { try (InputStream is = file1.getInputStream()) { nbttagcompound = CompressedStreamTools.readCompressed(is); @@ -284,7 +285,7 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData { * Gets the file location of the given map */ public VFile2 getMapFileFromName(String mapName) { - return new VFile2(this.mapDataDir, mapName + ".dat"); + return WorldsDB.newVFile(this.mapDataDir, mapName + ".dat"); } /** diff --git a/src/main/java/net/minecraft/world/storage/SaveHandlerMP.java b/src/game/java/net/minecraft/world/storage/SaveHandlerMP.java similarity index 100% rename from src/main/java/net/minecraft/world/storage/SaveHandlerMP.java rename to src/game/java/net/minecraft/world/storage/SaveHandlerMP.java diff --git a/src/main/java/net/minecraft/world/storage/WorldInfo.java b/src/game/java/net/minecraft/world/storage/WorldInfo.java similarity index 99% rename from src/main/java/net/minecraft/world/storage/WorldInfo.java rename to src/game/java/net/minecraft/world/storage/WorldInfo.java index 44959fd..bb6fe2b 100644 --- a/src/main/java/net/minecraft/world/storage/WorldInfo.java +++ b/src/game/java/net/minecraft/world/storage/WorldInfo.java @@ -3,7 +3,6 @@ package net.minecraft.world.storage; import java.util.concurrent.Callable; import net.minecraft.crash.CrashReportCategory; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.server.MinecraftServer; import net.minecraft.util.BlockPos; import net.minecraft.world.EnumDifficulty; import net.minecraft.world.GameRules; @@ -291,7 +290,7 @@ public class WorldInfo { nbt.setLong("Time", this.totalTime); nbt.setLong("DayTime", this.worldTime); nbt.setLong("SizeOnDisk", this.sizeOnDisk); - nbt.setLong("LastPlayed", MinecraftServer.getCurrentTimeMillis()); + nbt.setLong("LastPlayed", System.currentTimeMillis()); nbt.setString("LevelName", this.levelName); nbt.setInteger("version", this.saveVersion); nbt.setInteger("clearWeatherTime", this.cleanWeatherTime); diff --git a/src/lwjgl/java/fi/iki/elonen/NanoHTTPD.java b/src/lwjgl/java/fi/iki/elonen/NanoHTTPD.java new file mode 100755 index 0000000..d0d6727 --- /dev/null +++ b/src/lwjgl/java/fi/iki/elonen/NanoHTTPD.java @@ -0,0 +1,2333 @@ +package fi.iki.elonen; + +/* + * #%L + * NanoHttpd-Core + * %% + * Copyright (C) 2012 - 2015 nanohttpd + * %% + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the nanohttpd nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.security.KeyStore; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPOutputStream; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.TrustManagerFactory; + +import fi.iki.elonen.NanoHTTPD.Response.IStatus; +import fi.iki.elonen.NanoHTTPD.Response.Status; + +/** + * A simple, tiny, nicely embeddable HTTP server in Java + *

+ *

+ * NanoHTTPD + *

+ * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, + * 2010 by Konstantinos Togias + *

+ *

+ *

+ * Features + limitations: + *

    + *

    + *

  • Only one Java file
  • + *
  • Java 5 compatible
  • + *
  • Released as open source, Modified BSD licence
  • + *
  • No fixed config files, logging, authorization etc. (Implement yourself if + * you need them.)
  • + *
  • Supports parameter parsing of GET and POST methods (+ rudimentary PUT + * support in 1.25)
  • + *
  • Supports both dynamic content and file serving
  • + *
  • Supports file upload (since version 1.2, 2010)
  • + *
  • Supports partial content (streaming)
  • + *
  • Supports ETags
  • + *
  • Never caches anything
  • + *
  • Doesn't limit bandwidth, request time or simultaneous connections
  • + *
  • Default code serves files and shows all HTTP parameters and headers
  • + *
  • File server supports directory listing, index.html and index.htm
  • + *
  • File server supports partial content (streaming)
  • + *
  • File server supports ETags
  • + *
  • File server does the 301 redirection trick for directories without + * '/'
  • + *
  • File server supports simple skipping for files (continue download)
  • + *
  • File server serves also very long files without memory overhead
  • + *
  • Contains a built-in list of most common MIME types
  • + *
  • All header names are converted to lower case so they don't vary between + * browsers/clients
  • + *

    + *

+ *

+ *

+ * How to use: + *

    + *

    + *

  • Subclass and implement serve() and embed to your own program
  • + *

    + *

+ *

+ * See the separate "LICENSE.md" file for the distribution license (Modified BSD + * licence) + */ +public abstract class NanoHTTPD { + + /** + * Pluggable strategy for asynchronously executing requests. + */ + public interface AsyncRunner { + + void closeAll(); + + void closed(ClientHandler clientHandler); + + void exec(ClientHandler code); + } + + /** + * The runnable that will be used for every new client connection. + */ + public class ClientHandler implements Runnable { + + private final InputStream inputStream; + + private final Socket acceptSocket; + + public ClientHandler(InputStream inputStream, Socket acceptSocket) { + this.inputStream = inputStream; + this.acceptSocket = acceptSocket; + } + + public void close() { + safeClose(this.inputStream); + safeClose(this.acceptSocket); + } + + @Override + public void run() { + OutputStream outputStream = null; + try { + outputStream = this.acceptSocket.getOutputStream(); + TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create(); + HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, + this.acceptSocket.getInetAddress()); + while (!this.acceptSocket.isClosed()) { + session.execute(); + } + } catch (Exception e) { + // When the socket is closed by the client, + // we throw our own SocketException + // to break the "keep alive" loop above. If + // the exception was anything other + // than the expected SocketException OR a + // SocketTimeoutException, print the + // stacktrace + if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) + && !(e instanceof SocketTimeoutException)) { + NanoHTTPD.LOG.log(Level.SEVERE, + "Communication with the client broken, or an bug in the handler code", e); + } + } finally { + safeClose(outputStream); + safeClose(this.inputStream); + safeClose(this.acceptSocket); + NanoHTTPD.this.asyncRunner.closed(this); + } + } + } + + public static class Cookie { + + public static String getHTTPTime(int days) { + Calendar calendar = Calendar.getInstance(); + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + calendar.add(Calendar.DAY_OF_MONTH, days); + return dateFormat.format(calendar.getTime()); + } + + private final String n, v, e; + + public Cookie(String name, String value) { + this(name, value, 30); + } + + public Cookie(String name, String value, int numDays) { + this.n = name; + this.v = value; + this.e = getHTTPTime(numDays); + } + + public Cookie(String name, String value, String expires) { + this.n = name; + this.v = value; + this.e = expires; + } + + public String getHTTPHeader() { + String fmt = "%s=%s; expires=%s"; + return String.format(fmt, this.n, this.v, this.e); + } + } + + /** + * Provides rudimentary support for cookies. Doesn't support 'path', 'secure' + * nor 'httpOnly'. Feel free to improve it and/or add unsupported features. + * + * @author LordFokas + */ + public class CookieHandler implements Iterable { + + private final HashMap cookies = new HashMap(); + + private final ArrayList queue = new ArrayList(); + + public CookieHandler(Map httpHeaders) { + String raw = httpHeaders.get("cookie"); + if (raw != null) { + String[] tokens = raw.split(";"); + for (String token : tokens) { + String[] data = token.trim().split("="); + if (data.length == 2) { + this.cookies.put(data[0], data[1]); + } + } + } + } + + /** + * Set a cookie with an expiration date from a month ago, effectively deleting + * it on the client side. + * + * @param name The cookie name. + */ + public void delete(String name) { + set(name, "-delete-", -30); + } + + @Override + public Iterator iterator() { + return this.cookies.keySet().iterator(); + } + + /** + * Read a cookie from the HTTP Headers. + * + * @param name The cookie's name. + * @return The cookie's value if it exists, null otherwise. + */ + public String read(String name) { + return this.cookies.get(name); + } + + public void set(Cookie cookie) { + this.queue.add(cookie); + } + + /** + * Sets a cookie. + * + * @param name The cookie's name. + * @param value The cookie's value. + * @param expires How many days until the cookie expires. + */ + public void set(String name, String value, int expires) { + this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); + } + + /** + * Internally used by the webserver to add all queued cookies into the + * Response's HTTP Headers. + * + * @param response The Response object to which headers the queued cookies will + * be added. + */ + public void unloadQueue(Response response) { + for (Cookie cookie : this.queue) { + response.addHeader("Set-Cookie", cookie.getHTTPHeader()); + } + } + } + + /** + * Default threading strategy for NanoHTTPD. + *

+ *

+ * By default, the server spawns a new Thread for every incoming request. These + * are set to daemon status, and named according to the request number. + * The name is useful when profiling the application. + *

+ */ + public static class DefaultAsyncRunner implements AsyncRunner { + + private long requestCount; + + private final List running = Collections + .synchronizedList(new ArrayList()); + + /** + * @return a list with currently running clients. + */ + public List getRunning() { + return running; + } + + @Override + public void closeAll() { + // copy of the list for concurrency + for (ClientHandler clientHandler : new ArrayList(this.running)) { + clientHandler.close(); + } + } + + @Override + public void closed(ClientHandler clientHandler) { + this.running.remove(clientHandler); + } + + @Override + public void exec(ClientHandler clientHandler) { + ++this.requestCount; + Thread t = new Thread(clientHandler); + t.setDaemon(true); + t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")"); + this.running.add(clientHandler); + t.start(); + } + } + + /** + * Default strategy for creating and cleaning up temporary files. + *

+ *

+ * By default, files are created by File.createTempFile() in the + * directory specified. + *

+ */ + public static class DefaultTempFile implements TempFile { + + private final File file; + + private final OutputStream fstream; + + public DefaultTempFile(File tempdir) throws IOException { + this.file = File.createTempFile("NanoHTTPD-", "", tempdir); + this.fstream = new FileOutputStream(this.file); + } + + @Override + public void delete() throws Exception { + safeClose(this.fstream); + if (!this.file.delete()) { + throw new Exception("could not delete temporary file: " + this.file.getAbsolutePath()); + } + } + + @Override + public String getName() { + return this.file.getAbsolutePath(); + } + + @Override + public OutputStream open() throws Exception { + return this.fstream; + } + } + + /** + * Default strategy for creating and cleaning up temporary files. + *

+ *

+ * This class stores its files in the standard location (that is, wherever + * java.io.tmpdir points to). Files are added to an internal list, + * and deleted when no longer needed (that is, when clear() is + * invoked at the end of processing a request). + *

+ */ + public static class DefaultTempFileManager implements TempFileManager { + + private final File tmpdir; + + private final List tempFiles; + + public DefaultTempFileManager() { + this.tmpdir = new File(System.getProperty("java.io.tmpdir")); + if (!tmpdir.exists()) { + tmpdir.mkdirs(); + } + this.tempFiles = new ArrayList(); + } + + @Override + public void clear() { + for (TempFile file : this.tempFiles) { + try { + file.delete(); + } catch (Exception ignored) { + NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); + } + } + this.tempFiles.clear(); + } + + @Override + public TempFile createTempFile(String filename_hint) throws Exception { + DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); + this.tempFiles.add(tempFile); + return tempFile; + } + } + + /** + * Default strategy for creating and cleaning up temporary files. + */ + private class DefaultTempFileManagerFactory implements TempFileManagerFactory { + + @Override + public TempFileManager create() { + return new DefaultTempFileManager(); + } + } + + /** + * Creates a normal ServerSocket for TCP connections + */ + public static class DefaultServerSocketFactory implements ServerSocketFactory { + + @Override + public ServerSocket create() throws IOException { + return new ServerSocket(); + } + + } + + /** + * Creates a new SSLServerSocket + */ + public static class SecureServerSocketFactory implements ServerSocketFactory { + + private SSLServerSocketFactory sslServerSocketFactory; + + private String[] sslProtocols; + + public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { + this.sslServerSocketFactory = sslServerSocketFactory; + this.sslProtocols = sslProtocols; + } + + @Override + public ServerSocket create() throws IOException { + SSLServerSocket ss = null; + ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); + if (this.sslProtocols != null) { + ss.setEnabledProtocols(this.sslProtocols); + } else { + ss.setEnabledProtocols(ss.getSupportedProtocols()); + } + ss.setUseClientMode(false); + ss.setWantClientAuth(false); + ss.setNeedClientAuth(false); + return ss; + } + + } + + private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)"; + + private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, + Pattern.CASE_INSENSITIVE); + + private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)"; + + private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE); + + private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]"; + + private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern + .compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX); + + protected static class ContentType { + + private static final String ASCII_ENCODING = "US-ASCII"; + + private static final String MULTIPART_FORM_DATA_HEADER = "multipart/form-data"; + + private static final String CONTENT_REGEX = "[ |\t]*([^/^ ^;^,]+/[^ ^;^,]+)"; + + private static final Pattern MIME_PATTERN = Pattern.compile(CONTENT_REGEX, Pattern.CASE_INSENSITIVE); + + private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?"; + + private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE); + + private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?"; + + private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE); + + private final String contentTypeHeader; + + private final String contentType; + + private final String encoding; + + private final String boundary; + + public ContentType(String contentTypeHeader) { + this.contentTypeHeader = contentTypeHeader; + if (contentTypeHeader != null) { + contentType = getDetailFromContentHeader(contentTypeHeader, MIME_PATTERN, "", 1); + encoding = getDetailFromContentHeader(contentTypeHeader, CHARSET_PATTERN, null, 2); + } else { + contentType = ""; + encoding = "UTF-8"; + } + if (MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType)) { + boundary = getDetailFromContentHeader(contentTypeHeader, BOUNDARY_PATTERN, null, 2); + } else { + boundary = null; + } + } + + private String getDetailFromContentHeader(String contentTypeHeader, Pattern pattern, String defaultValue, + int group) { + Matcher matcher = pattern.matcher(contentTypeHeader); + return matcher.find() ? matcher.group(group) : defaultValue; + } + + public String getContentTypeHeader() { + return contentTypeHeader; + } + + public String getContentType() { + return contentType; + } + + public String getEncoding() { + return encoding == null ? ASCII_ENCODING : encoding; + } + + public String getBoundary() { + return boundary; + } + + public boolean isMultipart() { + return MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType); + } + + public ContentType tryUTF8() { + if (encoding == null) { + return new ContentType(this.contentTypeHeader + "; charset=UTF-8"); + } + return this; + } + } + + protected class HTTPSession implements IHTTPSession { + + private static final int REQUEST_BUFFER_LEN = 512; + + private static final int MEMORY_STORE_LIMIT = 1024; + + public static final int BUFSIZE = 8192; + + public static final int MAX_HEADER_SIZE = 1024; + + private final TempFileManager tempFileManager; + + private final OutputStream outputStream; + + private final BufferedInputStream inputStream; + + private int splitbyte; + + private int rlen; + + private String uri; + + private Method method; + + private Map> parms; + + private Map headers; + + private CookieHandler cookies; + + private String queryParameterString; + + private String remoteIp; + + private String remoteHostname; + + private String protocolVersion; + + public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { + this.tempFileManager = tempFileManager; + this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE); + this.outputStream = outputStream; + } + + public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, + InetAddress inetAddress) { + this.tempFileManager = tempFileManager; + this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE); + this.outputStream = outputStream; + this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" + : inetAddress.getHostAddress().toString(); + this.remoteHostname = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "localhost" + : inetAddress.getHostName().toString(); + this.headers = new HashMap(); + } + + /** + * Decodes the sent headers and loads the data into Key/value pairs + */ + private void decodeHeader(BufferedReader in, Map pre, Map> parms, + Map headers) throws ResponseException { + try { + // Read the request line + String inLine = in.readLine(); + if (inLine == null) { + return; + } + + StringTokenizer st = new StringTokenizer(inLine); + if (!st.hasMoreTokens()) { + throw new ResponseException(Response.Status.BAD_REQUEST, + "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); + } + + pre.put("method", st.nextToken()); + + if (!st.hasMoreTokens()) { + throw new ResponseException(Response.Status.BAD_REQUEST, + "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); + } + + String uri = st.nextToken(); + + // Decode parameters from the URI + int qmi = uri.indexOf('?'); + if (qmi >= 0) { + decodeParms(uri.substring(qmi + 1), parms); + uri = decodePercent(uri.substring(0, qmi)); + } else { + uri = decodePercent(uri); + } + + // If there's another token, its protocol version, + // followed by HTTP headers. + // NOTE: this now forces header names lower case since they are + // case insensitive and vary by client. + if (st.hasMoreTokens()) { + protocolVersion = st.nextToken(); + } else { + protocolVersion = "HTTP/1.1"; + NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1."); + } + String line = in.readLine(); + while (line != null && !line.trim().isEmpty()) { + int p = line.indexOf(':'); + if (p >= 0) { + headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim()); + } + line = in.readLine(); + } + + pre.put("uri", uri); + } catch (IOException ioe) { + throw new ResponseException(Response.Status.INTERNAL_ERROR, + "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); + } + } + + /** + * Decodes the Multipart Body data and put it into Key/Value pairs. + */ + private void decodeMultipartFormData(ContentType contentType, ByteBuffer fbuf, Map> parms, + Map files) throws ResponseException { + int pcount = 0; + try { + int[] boundaryIdxs = getBoundaryPositions(fbuf, contentType.getBoundary().getBytes()); + if (boundaryIdxs.length < 2) { + throw new ResponseException(Response.Status.BAD_REQUEST, + "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings."); + } + + byte[] partHeaderBuff = new byte[MAX_HEADER_SIZE]; + for (int boundaryIdx = 0; boundaryIdx < boundaryIdxs.length - 1; boundaryIdx++) { + fbuf.position(boundaryIdxs[boundaryIdx]); + int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE; + fbuf.get(partHeaderBuff, 0, len); + BufferedReader in = new BufferedReader( + new InputStreamReader(new ByteArrayInputStream(partHeaderBuff, 0, len), + Charset.forName(contentType.getEncoding())), + len); + + int headerLines = 0; + // First line is boundary string + String mpline = in.readLine(); + headerLines++; + if (mpline == null || !mpline.contains(contentType.getBoundary())) { + throw new ResponseException(Response.Status.BAD_REQUEST, + "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary."); + } + + String partName = null, fileName = null, partContentType = null; + // Parse the reset of the header lines + mpline = in.readLine(); + headerLines++; + while (mpline != null && mpline.trim().length() > 0) { + Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline); + if (matcher.matches()) { + String attributeString = matcher.group(2); + matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString); + while (matcher.find()) { + String key = matcher.group(1); + if ("name".equalsIgnoreCase(key)) { + partName = matcher.group(2); + } else if ("filename".equalsIgnoreCase(key)) { + fileName = matcher.group(2); + // add these two line to support multiple + // files uploaded using the same field Id + if (!fileName.isEmpty()) { + if (pcount > 0) + partName = partName + String.valueOf(pcount++); + else + pcount++; + } + } + } + } + matcher = CONTENT_TYPE_PATTERN.matcher(mpline); + if (matcher.matches()) { + partContentType = matcher.group(2).trim(); + } + mpline = in.readLine(); + headerLines++; + } + int partHeaderLength = 0; + while (headerLines-- > 0) { + partHeaderLength = scipOverNewLine(partHeaderBuff, partHeaderLength); + } + // Read the part data + if (partHeaderLength >= len - 4) { + throw new ResponseException(Response.Status.INTERNAL_ERROR, + "Multipart header size exceeds MAX_HEADER_SIZE."); + } + int partDataStart = boundaryIdxs[boundaryIdx] + partHeaderLength; + int partDataEnd = boundaryIdxs[boundaryIdx + 1] - 4; + + fbuf.position(partDataStart); + + List values = parms.get(partName); + if (values == null) { + values = new ArrayList(); + parms.put(partName, values); + } + + if (partContentType == null) { + // Read the part into a string + byte[] data_bytes = new byte[partDataEnd - partDataStart]; + fbuf.get(data_bytes); + + values.add(new String(data_bytes, contentType.getEncoding())); + } else { + // Read it into a file + String path = saveTmpFile(fbuf, partDataStart, partDataEnd - partDataStart, fileName); + if (!files.containsKey(partName)) { + files.put(partName, path); + } else { + int count = 2; + while (files.containsKey(partName + count)) { + count++; + } + files.put(partName + count, path); + } + values.add(fileName); + } + } + } catch (ResponseException re) { + throw re; + } catch (Exception e) { + throw new ResponseException(Response.Status.INTERNAL_ERROR, e.toString()); + } + } + + private int scipOverNewLine(byte[] partHeaderBuff, int index) { + while (partHeaderBuff[index] != '\n') { + index++; + } + return ++index; + } + + /** + * Decodes parameters in percent-encoded URI-format ( e.g. + * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given Map. + */ + private void decodeParms(String parms, Map> p) { + if (parms == null) { + this.queryParameterString = ""; + return; + } + + this.queryParameterString = parms; + StringTokenizer st = new StringTokenizer(parms, "&"); + while (st.hasMoreTokens()) { + String e = st.nextToken(); + int sep = e.indexOf('='); + String key = null; + String value = null; + + if (sep >= 0) { + key = decodePercent(e.substring(0, sep)).trim(); + value = decodePercent(e.substring(sep + 1)); + } else { + key = decodePercent(e).trim(); + value = ""; + } + + List values = p.get(key); + if (values == null) { + values = new ArrayList(); + p.put(key, values); + } + + values.add(value); + } + } + + @Override + public void execute() throws IOException { + Response r = null; + try { + // Read the first 8192 bytes. + // The full header should fit in here. + // Apache's default header limit is 8KB. + // Do NOT assume that a single read will get the entire header + // at once! + byte[] buf = new byte[HTTPSession.BUFSIZE]; + this.splitbyte = 0; + this.rlen = 0; + + int read = -1; + this.inputStream.mark(HTTPSession.BUFSIZE); + try { + read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); + } catch (SSLException e) { + throw e; + } catch (IOException e) { + safeClose(this.inputStream); + safeClose(this.outputStream); + throw new SocketException("NanoHttpd Shutdown"); + } + if (read == -1) { + // socket was been closed + safeClose(this.inputStream); + safeClose(this.outputStream); + throw new SocketException("NanoHttpd Shutdown"); + } + while (read > 0) { + this.rlen += read; + this.splitbyte = findHeaderEnd(buf, this.rlen); + if (this.splitbyte > 0) { + break; + } + read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); + } + + if (this.splitbyte < this.rlen) { + this.inputStream.reset(); + this.inputStream.skip(this.splitbyte); + } + + this.parms = new HashMap>(); + if (null == this.headers) { + this.headers = new HashMap(); + } else { + this.headers.clear(); + } + + // Create a BufferedReader for parsing the header. + BufferedReader hin = new BufferedReader( + new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen))); + + // Decode the header into parms and header java properties + Map pre = new HashMap(); + decodeHeader(hin, pre, this.parms, this.headers); + + if (null != this.remoteIp) { + this.headers.put("remote-addr", this.remoteIp); + this.headers.put("http-client-ip", this.remoteIp); + } + + this.method = Method.lookup(pre.get("method")); + if (this.method == null) { + throw new ResponseException(Response.Status.BAD_REQUEST, + "BAD REQUEST: Syntax error. HTTP verb " + pre.get("method") + " unhandled."); + } + + this.uri = pre.get("uri"); + + this.cookies = new CookieHandler(this.headers); + + String connection = this.headers.get("connection"); + boolean keepAlive = "HTTP/1.1".equals(protocolVersion) + && (connection == null || !connection.matches("(?i).*close.*")); + + // Ok, now do the serve() + + // TODO: long body_size = getBodySize(); + // TODO: long pos_before_serve = this.inputStream.totalRead() + // (requires implementation for totalRead()) + r = serve(this); + // TODO: this.inputStream.skip(body_size - + // (this.inputStream.totalRead() - pos_before_serve)) + + if (r == null) { + throw new ResponseException(Response.Status.INTERNAL_ERROR, + "SERVER INTERNAL ERROR: Serve() returned a null response."); + } else { + String acceptEncoding = this.headers.get("accept-encoding"); + this.cookies.unloadQueue(r); + r.setRequestMethod(this.method); + r.setGzipEncoding( + useGzipWhenAccepted(r) && acceptEncoding != null && acceptEncoding.contains("gzip")); + r.setKeepAlive(keepAlive); + r.send(this.outputStream); + } + if (!keepAlive || r.isCloseConnection()) { + throw new SocketException("NanoHttpd Shutdown"); + } + } catch (SocketException e) { + // throw it out to close socket object (finalAccept) + throw e; + } catch (SocketTimeoutException ste) { + // treat socket timeouts the same way we treat socket exceptions + // i.e. close the stream & finalAccept object by throwing the + // exception up the call stack. + throw ste; + } catch (SSLException ssle) { + Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, + "SSL PROTOCOL FAILURE: " + ssle.getMessage()); + resp.send(this.outputStream); + safeClose(this.outputStream); + } catch (IOException ioe) { + Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, + "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); + resp.send(this.outputStream); + safeClose(this.outputStream); + } catch (ResponseException re) { + Response resp = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); + resp.send(this.outputStream); + safeClose(this.outputStream); + } finally { + safeClose(r); + this.tempFileManager.clear(); + } + } + + /** + * Find byte index separating header from body. It must be the last byte of the + * first two sequential new lines. + */ + private int findHeaderEnd(final byte[] buf, int rlen) { + int splitbyte = 0; + while (splitbyte + 1 < rlen) { + + // RFC2616 + if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 3 < rlen + && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { + return splitbyte + 4; + } + + // tolerance + if (buf[splitbyte] == '\n' && buf[splitbyte + 1] == '\n') { + return splitbyte + 2; + } + splitbyte++; + } + return 0; + } + + /** + * Find the byte positions where multipart boundaries start. This reads a large + * block at a time and uses a temporary buffer to optimize (memory mapped) file + * access. + */ + private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) { + int[] res = new int[0]; + if (b.remaining() < boundary.length) { + return res; + } + + int search_window_pos = 0; + byte[] search_window = new byte[4 * 1024 + boundary.length]; + + int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length; + b.get(search_window, 0, first_fill); + int new_bytes = first_fill - boundary.length; + + do { + // Search the search_window + for (int j = 0; j < new_bytes; j++) { + for (int i = 0; i < boundary.length; i++) { + if (search_window[j + i] != boundary[i]) + break; + if (i == boundary.length - 1) { + // Match found, add it to results + int[] new_res = new int[res.length + 1]; + System.arraycopy(res, 0, new_res, 0, res.length); + new_res[res.length] = search_window_pos + j; + res = new_res; + } + } + } + search_window_pos += new_bytes; + + // Copy the end of the buffer to the start + System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, + boundary.length); + + // Refill search_window + new_bytes = search_window.length - boundary.length; + new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes; + b.get(search_window, boundary.length, new_bytes); + } while (new_bytes > 0); + return res; + } + + @Override + public CookieHandler getCookies() { + return this.cookies; + } + + @Override + public final Map getHeaders() { + return this.headers; + } + + @Override + public final InputStream getInputStream() { + return this.inputStream; + } + + @Override + public final Method getMethod() { + return this.method; + } + + /** + * @deprecated use {@link #getParameters()} instead. + */ + @Override + @Deprecated + public final Map getParms() { + Map result = new HashMap(); + for (String key : this.parms.keySet()) { + result.put(key, this.parms.get(key).get(0)); + } + + return result; + } + + @Override + public final Map> getParameters() { + return this.parms; + } + + @Override + public String getQueryParameterString() { + return this.queryParameterString; + } + + private RandomAccessFile getTmpBucket() { + try { + TempFile tempFile = this.tempFileManager.createTempFile(null); + return new RandomAccessFile(tempFile.getName(), "rw"); + } catch (Exception e) { + throw new Error(e); // we won't recover, so throw an error + } + } + + @Override + public final String getUri() { + return this.uri; + } + + /** + * Deduce body length in bytes. Either from "content-length" header or read + * bytes. + */ + public long getBodySize() { + if (this.headers.containsKey("content-length")) { + return Long.parseLong(this.headers.get("content-length")); + } else if (this.splitbyte < this.rlen) { + return this.rlen - this.splitbyte; + } + return 0; + } + + @Override + public void parseBody(Map files) throws IOException, ResponseException { + RandomAccessFile randomAccessFile = null; + try { + long size = getBodySize(); + ByteArrayOutputStream baos = null; + DataOutput requestDataOutput = null; + + // Store the request in memory or a file, depending on size + if (size < MEMORY_STORE_LIMIT) { + baos = new ByteArrayOutputStream(); + requestDataOutput = new DataOutputStream(baos); + } else { + randomAccessFile = getTmpBucket(); + requestDataOutput = randomAccessFile; + } + + // Read all the body and write it to request_data_output + byte[] buf = new byte[REQUEST_BUFFER_LEN]; + while (this.rlen >= 0 && size > 0) { + this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN)); + size -= this.rlen; + if (this.rlen > 0) { + requestDataOutput.write(buf, 0, this.rlen); + } + } + + ByteBuffer fbuf = null; + if (baos != null) { + fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size()); + } else { + fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, + randomAccessFile.length()); + randomAccessFile.seek(0); + } + + // If the method is POST, there may be parameters + // in data section, too, read it: + if (Method.POST.equals(this.method)) { + ContentType contentType = new ContentType(this.headers.get("content-type")); + if (contentType.isMultipart()) { + String boundary = contentType.getBoundary(); + if (boundary == null) { + throw new ResponseException(Response.Status.BAD_REQUEST, + "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); + } + decodeMultipartFormData(contentType, fbuf, this.parms, files); + } else { + byte[] postBytes = new byte[fbuf.remaining()]; + fbuf.get(postBytes); + String postLine = new String(postBytes, contentType.getEncoding()).trim(); + // Handle application/x-www-form-urlencoded + if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType.getContentType())) { + decodeParms(postLine, this.parms); + } else if (postLine.length() != 0) { + // Special case for raw POST data => create a + // special files entry "postData" with raw content + // data + files.put("postData", postLine); + } + } + } else if (Method.PUT.equals(this.method)) { + files.put("content", saveTmpFile(fbuf, 0, fbuf.limit(), null)); + } + } finally { + safeClose(randomAccessFile); + } + } + + /** + * Retrieves the content of a sent file and saves it to a temporary file. The + * full path to the saved file is returned. + */ + private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) { + String path = ""; + if (len > 0) { + FileOutputStream fileOutputStream = null; + try { + TempFile tempFile = this.tempFileManager.createTempFile(filename_hint); + ByteBuffer src = b.duplicate(); + fileOutputStream = new FileOutputStream(tempFile.getName()); + FileChannel dest = fileOutputStream.getChannel(); + src.position(offset).limit(offset + len); + dest.write(src.slice()); + path = tempFile.getName(); + } catch (Exception e) { // Catch exception if any + throw new Error(e); // we won't recover, so throw an error + } finally { + safeClose(fileOutputStream); + } + } + return path; + } + + @Override + public String getRemoteIpAddress() { + return this.remoteIp; + } + + @Override + public String getRemoteHostName() { + return this.remoteHostname; + } + } + + /** + * Handles one session, i.e. parses the HTTP request and returns the response. + */ + public interface IHTTPSession { + + void execute() throws IOException; + + CookieHandler getCookies(); + + Map getHeaders(); + + InputStream getInputStream(); + + Method getMethod(); + + /** + * This method will only return the first value for a given parameter. You will + * want to use getParameters if you expect multiple values for a given key. + * + * @deprecated use {@link #getParameters()} instead. + */ + @Deprecated + Map getParms(); + + Map> getParameters(); + + String getQueryParameterString(); + + /** + * @return the path part of the URL. + */ + String getUri(); + + /** + * Adds the files in the request body to the files map. + * + * @param files map to modify + */ + void parseBody(Map files) throws IOException, ResponseException; + + /** + * Get the remote ip address of the requester. + * + * @return the IP address. + */ + String getRemoteIpAddress(); + + /** + * Get the remote hostname of the requester. + * + * @return the hostname. + */ + String getRemoteHostName(); + } + + /** + * HTTP Request methods, with the ability to decode a String back + * to its enum value. + */ + public enum Method { + GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE, CONNECT, PATCH, PROPFIND, PROPPATCH, MKCOL, MOVE, COPY, LOCK, + UNLOCK; + + static Method lookup(String method) { + if (method == null) + return null; + + try { + return valueOf(method); + } catch (IllegalArgumentException e) { + // TODO: Log it? + return null; + } + } + } + + /** + * HTTP response. Return one of these from serve(). + */ + public static class Response implements Closeable { + + public interface IStatus { + + String getDescription(); + + int getRequestStatus(); + } + + /** + * Some HTTP response status codes + */ + public enum Status implements IStatus { + SWITCH_PROTOCOL(101, "Switching Protocols"), + + OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), NO_CONTENT(204, "No Content"), + PARTIAL_CONTENT(206, "Partial Content"), MULTI_STATUS(207, "Multi-Status"), + + REDIRECT(301, "Moved Permanently"), + /** + * Many user agents mishandle 302 in ways that violate the RFC1945 spec (i.e., + * redirect a POST to a GET). 303 and 307 were added in RFC2616 to address this. + * You should prefer 303 and 307 unless the calling user agent does not support + * 303 and 307 functionality + */ + @Deprecated + FOUND(302, "Found"), REDIRECT_SEE_OTHER(303, "See Other"), NOT_MODIFIED(304, "Not Modified"), + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + + BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, "Unauthorized"), FORBIDDEN(403, "Forbidden"), + NOT_FOUND(404, "Not Found"), METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + NOT_ACCEPTABLE(406, "Not Acceptable"), REQUEST_TIMEOUT(408, "Request Timeout"), CONFLICT(409, "Conflict"), + GONE(410, "Gone"), LENGTH_REQUIRED(411, "Length Required"), PRECONDITION_FAILED(412, "Precondition Failed"), + PAYLOAD_TOO_LARGE(413, "Payload Too Large"), UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), + EXPECTATION_FAILED(417, "Expectation Failed"), TOO_MANY_REQUESTS(429, "Too Many Requests"), + + INTERNAL_ERROR(500, "Internal Server Error"), NOT_IMPLEMENTED(501, "Not Implemented"), + SERVICE_UNAVAILABLE(503, "Service Unavailable"), + UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported"); + + private final int requestStatus; + + private final String description; + + Status(int requestStatus, String description) { + this.requestStatus = requestStatus; + this.description = description; + } + + public static Status lookup(int requestStatus) { + for (Status status : Status.values()) { + if (status.getRequestStatus() == requestStatus) { + return status; + } + } + return null; + } + + @Override + public String getDescription() { + return "" + this.requestStatus + " " + this.description; + } + + @Override + public int getRequestStatus() { + return this.requestStatus; + } + + } + + /** + * Output stream that will automatically send every write to the wrapped + * OutputStream according to chunked transfer: + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 + */ + private static class ChunkedOutputStream extends FilterOutputStream { + + public ChunkedOutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(int b) throws IOException { + byte[] data = { (byte) b }; + write(data, 0, 1); + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (len == 0) + return; + out.write(String.format("%x\r\n", len).getBytes()); + out.write(b, off, len); + out.write("\r\n".getBytes()); + } + + public void finish() throws IOException { + out.write("0\r\n\r\n".getBytes()); + } + + } + + /** + * HTTP status code after processing, e.g. "200 OK", Status.OK + */ + private IStatus status; + + /** + * MIME type of content, e.g. "text/html" + */ + private String mimeType; + + /** + * Data of the response, may be null. + */ + private InputStream data; + + private long contentLength; + + /** + * Headers for the HTTP response. Use addHeader() to add lines. the lowercase + * map is automatically kept up to date. + */ + @SuppressWarnings("serial") + private final Map header = new HashMap() { + + public String put(String key, String value) { + lowerCaseHeader.put(key == null ? key : key.toLowerCase(), value); + return super.put(key, value); + }; + }; + + /** + * copy of the header map with all the keys lowercase for faster searching. + */ + private final Map lowerCaseHeader = new HashMap(); + + /** + * The request method that spawned this response. + */ + private Method requestMethod; + + /** + * Use chunkedTransfer + */ + private boolean chunkedTransfer; + + private boolean encodeAsGzip; + + private boolean keepAlive; + + /** + * Creates a fixed length response if totalBytes>=0, otherwise chunked. + */ + protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) { + this.status = status; + this.mimeType = mimeType; + if (data == null) { + this.data = new ByteArrayInputStream(new byte[0]); + this.contentLength = 0L; + } else { + this.data = data; + this.contentLength = totalBytes; + } + this.chunkedTransfer = this.contentLength < 0; + keepAlive = true; + } + + @Override + public void close() throws IOException { + if (this.data != null) { + this.data.close(); + } + } + + /** + * Adds given line to the header. + */ + public void addHeader(String name, String value) { + this.header.put(name, value); + } + + /** + * Indicate to close the connection after the Response has been sent. + * + * @param close {@code true} to hint connection closing, {@code false} to let + * connection be closed by client. + */ + public void closeConnection(boolean close) { + if (close) + this.header.put("connection", "close"); + else + this.header.remove("connection"); + } + + /** + * @return {@code true} if connection is to be closed after this Response has + * been sent. + */ + public boolean isCloseConnection() { + return "close".equals(getHeader("connection")); + } + + public InputStream getData() { + return this.data; + } + + public String getHeader(String name) { + return this.lowerCaseHeader.get(name.toLowerCase()); + } + + public String getMimeType() { + return this.mimeType; + } + + public Method getRequestMethod() { + return this.requestMethod; + } + + public IStatus getStatus() { + return this.status; + } + + public void setGzipEncoding(boolean encodeAsGzip) { + this.encodeAsGzip = encodeAsGzip; + } + + public void setKeepAlive(boolean useKeepAlive) { + this.keepAlive = useKeepAlive; + } + + /** + * Sends given response to the socket. + */ + protected void send(OutputStream outputStream) { + SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); + gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); + + try { + if (this.status == null) { + throw new Error("sendResponse(): Status can't be null."); + } + PrintWriter pw = new PrintWriter( + new BufferedWriter( + new OutputStreamWriter(outputStream, new ContentType(this.mimeType).getEncoding())), + false); + pw.append("HTTP/1.1 ").append(this.status.getDescription()).append(" \r\n"); + if (this.mimeType != null) { + printHeader(pw, "Content-Type", this.mimeType); + } + if (getHeader("date") == null) { + printHeader(pw, "Date", gmtFrmt.format(new Date())); + } + for (Entry entry : this.header.entrySet()) { + printHeader(pw, entry.getKey(), entry.getValue()); + } + if (getHeader("connection") == null) { + printHeader(pw, "Connection", (this.keepAlive ? "keep-alive" : "close")); + } + if (getHeader("content-length") != null) { + encodeAsGzip = false; + } + if (encodeAsGzip) { + printHeader(pw, "Content-Encoding", "gzip"); + setChunkedTransfer(true); + } + long pending = this.data != null ? this.contentLength : 0; + if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { + printHeader(pw, "Transfer-Encoding", "chunked"); + } else if (!encodeAsGzip) { + pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, pending); + } + pw.append("\r\n"); + pw.flush(); + sendBodyWithCorrectTransferAndEncoding(outputStream, pending); + outputStream.flush(); + safeClose(this.data); + } catch (IOException ioe) { + NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe); + } + } + + @SuppressWarnings("static-method") + protected void printHeader(PrintWriter pw, String key, String value) { + pw.append(key).append(": ").append(value).append("\r\n"); + } + + protected long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, long defaultSize) { + String contentLengthString = getHeader("content-length"); + long size = defaultSize; + if (contentLengthString != null) { + try { + size = Long.parseLong(contentLengthString); + } catch (NumberFormatException ex) { + LOG.severe("content-length was no number " + contentLengthString); + } + } + pw.print("Content-Length: " + size + "\r\n"); + return size; + } + + private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) + throws IOException { + if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { + ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream); + sendBodyWithCorrectEncoding(chunkedOutputStream, -1); + chunkedOutputStream.finish(); + } else { + sendBodyWithCorrectEncoding(outputStream, pending); + } + } + + private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException { + if (encodeAsGzip) { + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); + sendBody(gzipOutputStream, -1); + gzipOutputStream.finish(); + } else { + sendBody(outputStream, pending); + } + } + + /** + * Sends the body to the specified OutputStream. The pending parameter limits + * the maximum amounts of bytes sent unless it is -1, in which case everything + * is sent. + * + * @param outputStream the OutputStream to send data to + * @param pending -1 to send everything, otherwise sets a max limit to the + * number of bytes sent + * @throws IOException if something goes wrong while sending the data. + */ + private void sendBody(OutputStream outputStream, long pending) throws IOException { + long BUFFER_SIZE = 16 * 1024; + byte[] buff = new byte[(int) BUFFER_SIZE]; + boolean sendEverything = pending == -1; + while (pending > 0 || sendEverything) { + long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE); + int read = this.data.read(buff, 0, (int) bytesToRead); + if (read <= 0) { + break; + } + outputStream.write(buff, 0, read); + if (!sendEverything) { + pending -= read; + } + } + } + + public void setChunkedTransfer(boolean chunkedTransfer) { + this.chunkedTransfer = chunkedTransfer; + } + + public void setData(InputStream data) { + this.data = data; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public void setRequestMethod(Method requestMethod) { + this.requestMethod = requestMethod; + } + + public void setStatus(IStatus status) { + this.status = status; + } + } + + public static final class ResponseException extends Exception { + + private static final long serialVersionUID = 6569838532917408380L; + + private final Response.Status status; + + public ResponseException(Response.Status status, String message) { + super(message); + this.status = status; + } + + public ResponseException(Response.Status status, String message, Exception e) { + super(message, e); + this.status = status; + } + + public Response.Status getStatus() { + return this.status; + } + } + + /** + * The runnable that will be used for the main listening thread. + */ + public class ServerRunnable implements Runnable { + + private final int timeout; + + private IOException bindException; + + private boolean hasBinded = false; + + public ServerRunnable(int timeout) { + this.timeout = timeout; + } + + @Override + public void run() { + try { + myServerSocket.bind( + hostname != null ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); + hasBinded = true; + } catch (IOException e) { + this.bindException = e; + return; + } + do { + try { + final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept(); + if (this.timeout > 0) { + finalAccept.setSoTimeout(this.timeout); + } + final InputStream inputStream = finalAccept.getInputStream(); + NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream)); + } catch (IOException e) { + NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); + } + } while (!NanoHTTPD.this.myServerSocket.isClosed()); + } + } + + /** + * A temp file. + *

+ *

+ * Temp files are responsible for managing the actual temporary storage and + * cleaning themselves up when no longer needed. + *

+ */ + public interface TempFile { + + public void delete() throws Exception; + + public String getName(); + + public OutputStream open() throws Exception; + } + + /** + * Temp file manager. + *

+ *

+ * Temp file managers are created 1-to-1 with incoming requests, to create and + * cleanup temporary files created as a result of handling the request. + *

+ */ + public interface TempFileManager { + + void clear(); + + public TempFile createTempFile(String filename_hint) throws Exception; + } + + /** + * Factory to create temp file managers. + */ + public interface TempFileManagerFactory { + + public TempFileManager create(); + } + + /** + * Factory to create ServerSocketFactories. + */ + public interface ServerSocketFactory { + + public ServerSocket create() throws IOException; + + } + + /** + * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) This + * is required as the Keep-Alive HTTP connections would otherwise block the + * socket reading thread forever (or as long the browser is open). + */ + public static final int SOCKET_READ_TIMEOUT = 5000; + + /** + * Common MIME type for dynamic content: plain text + */ + public static final String MIME_PLAINTEXT = "text/plain"; + + /** + * Common MIME type for dynamic content: html + */ + public static final String MIME_HTML = "text/html"; + + /** + * Pseudo-Parameter to use to store the actual query string in the parameters + * map for later re-processing. + */ + private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; + + /** + * logger to log to. + */ + private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName()); + + /** + * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE + */ + protected static Map MIME_TYPES; + + public static Map mimeTypes() { + if (MIME_TYPES == null) { + MIME_TYPES = new HashMap(); + loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/default-mimetypes.properties"); + loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/mimetypes.properties"); + if (MIME_TYPES.isEmpty()) { + LOG.log(Level.WARNING, "no mime types found in the classpath! please provide mimetypes.properties"); + } + } + return MIME_TYPES; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void loadMimeTypes(Map result, String resourceName) { + try { + Enumeration resources = NanoHTTPD.class.getClassLoader().getResources(resourceName); + while (resources.hasMoreElements()) { + URL url = (URL) resources.nextElement(); + Properties properties = new Properties(); + InputStream stream = null; + try { + stream = url.openStream(); + properties.load(stream); + } catch (IOException e) { + LOG.log(Level.SEVERE, "could not load mimetypes from " + url, e); + } finally { + safeClose(stream); + } + result.putAll((Map) properties); + } + } catch (IOException e) { + LOG.log(Level.INFO, "no mime types available at " + resourceName); + } + }; + + /** + * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an array of + * loaded KeyManagers. These objects must properly loaded/initialized by the + * caller. + */ + public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) + throws IOException { + SSLServerSocketFactory res = null; + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(loadedKeyStore); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); + res = ctx.getServerSocketFactory(); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + return res; + } + + /** + * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a loaded + * KeyManagerFactory. These objects must properly loaded/initialized by the + * caller. + */ + public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, + KeyManagerFactory loadedKeyFactory) throws IOException { + try { + return makeSSLSocketFactory(loadedKeyStore, loadedKeyFactory.getKeyManagers()); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + } + + /** + * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your + * certificate and passphrase + */ + public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) + throws IOException { + try { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); + + if (keystoreStream == null) { + throw new IOException("Unable to load keystore from classpath: " + keyAndTrustStoreClasspathPath); + } + + keystore.load(keystoreStream, passphrase); + KeyManagerFactory keyManagerFactory = KeyManagerFactory + .getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keystore, passphrase); + return makeSSLSocketFactory(keystore, keyManagerFactory); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + } + + /** + * Get MIME type from file name extension, if possible + * + * @param uri the string representing a file + * @return the connected mime/type + */ + public static String getMimeTypeForFile(String uri) { + int dot = uri.lastIndexOf('.'); + String mime = null; + if (dot >= 0) { + mime = mimeTypes().get(uri.substring(dot + 1).toLowerCase()); + } + return mime == null ? "application/octet-stream" : mime; + } + + private static final void safeClose(Object closeable) { + try { + if (closeable != null) { + if (closeable instanceof Closeable) { + ((Closeable) closeable).close(); + } else if (closeable instanceof Socket) { + ((Socket) closeable).close(); + } else if (closeable instanceof ServerSocket) { + ((ServerSocket) closeable).close(); + } else { + throw new IllegalArgumentException("Unknown object to close"); + } + } + } catch (IOException e) { + NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e); + } + } + + private final String hostname; + + private final int myPort; + + private volatile ServerSocket myServerSocket; + + private ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory(); + + private Thread myThread; + + /** + * Pluggable strategy for asynchronously executing requests. + */ + protected AsyncRunner asyncRunner; + + /** + * Pluggable strategy for creating and cleaning up temporary files. + */ + private TempFileManagerFactory tempFileManagerFactory; + + /** + * Constructs an HTTP server on given port. + */ + public NanoHTTPD(int port) { + this(null, port); + } + + // ------------------------------------------------------------------------------- + // // + // + // Threading Strategy. + // + // ------------------------------------------------------------------------------- + // // + + /** + * Constructs an HTTP server on given hostname and port. + */ + public NanoHTTPD(String hostname, int port) { + this.hostname = hostname; + this.myPort = port; + setTempFileManagerFactory(new DefaultTempFileManagerFactory()); + setAsyncRunner(new DefaultAsyncRunner()); + } + + /** + * Forcibly closes all connections that are open. + */ + public synchronized void closeAllConnections() { + stop(); + } + + /** + * create a instance of the client handler, subclasses can return a subclass of + * the ClientHandler. + * + * @param finalAccept the socket the cleint is connected to + * @param inputStream the input stream + * @return the client handler + */ + protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) { + return new ClientHandler(inputStream, finalAccept); + } + + /** + * Instantiate the server runnable, can be overwritten by subclasses to provide + * a subclass of the ServerRunnable. + * + * @param timeout the socet timeout to use. + * @return the server runnable. + */ + protected ServerRunnable createServerRunnable(final int timeout) { + return new ServerRunnable(timeout); + } + + /** + * Decode parameters from a URL, handing the case where a single parameter name + * might have been supplied several times, by return lists of values. In general + * these lists will contain a single element. + * + * @param parms original NanoHTTPD parameters values, as passed to the + * serve() method. + * @return a map of String (parameter name) to + * List<String> (a list of the values supplied). + */ + protected static Map> decodeParameters(Map parms) { + return decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER)); + } + + // ------------------------------------------------------------------------------- + // // + + /** + * Decode parameters from a URL, handing the case where a single parameter name + * might have been supplied several times, by return lists of values. In general + * these lists will contain a single element. + * + * @param queryString a query string pulled from the URL. + * @return a map of String (parameter name) to + * List<String> (a list of the values supplied). + */ + protected static Map> decodeParameters(String queryString) { + Map> parms = new HashMap>(); + if (queryString != null) { + StringTokenizer st = new StringTokenizer(queryString, "&"); + while (st.hasMoreTokens()) { + String e = st.nextToken(); + int sep = e.indexOf('='); + String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); + if (!parms.containsKey(propertyName)) { + parms.put(propertyName, new ArrayList()); + } + String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null; + if (propertyValue != null) { + parms.get(propertyName).add(propertyValue); + } + } + } + return parms; + } + + /** + * Decode percent encoded String values. + * + * @param str the percent encoded String + * @return expanded form of the input, for example "foo%20bar" becomes "foo bar" + */ + protected static String decodePercent(String str) { + String decoded = null; + try { + decoded = URLDecoder.decode(str, "UTF8"); + } catch (UnsupportedEncodingException ignored) { + NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored); + } + return decoded; + } + + /** + * @return true if the gzip compression should be used if the client accespts + * it. Default this option is on for text content and off for + * everything. Override this for custom semantics. + */ + @SuppressWarnings("static-method") + protected boolean useGzipWhenAccepted(Response r) { + return r.getMimeType() != null + && (r.getMimeType().toLowerCase().contains("text/") || r.getMimeType().toLowerCase().contains("/json")); + } + + public final int getListeningPort() { + return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort(); + } + + public final boolean isAlive() { + return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); + } + + public ServerSocketFactory getServerSocketFactory() { + return serverSocketFactory; + } + + public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) { + this.serverSocketFactory = serverSocketFactory; + } + + public String getHostname() { + return hostname; + } + + public TempFileManagerFactory getTempFileManagerFactory() { + return tempFileManagerFactory; + } + + /** + * Call before start() to serve over HTTPS instead of HTTP + */ + public void makeSecure(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { + this.serverSocketFactory = new SecureServerSocketFactory(sslServerSocketFactory, sslProtocols); + } + + /** + * Create a response with unknown length (using HTTP 1.1 chunking). + */ + public static Response newChunkedResponse(IStatus status, String mimeType, InputStream data) { + return new Response(status, mimeType, data, -1); + } + + /** + * Create a response with known length. + */ + public static Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) { + return new Response(status, mimeType, data, totalBytes); + } + + /** + * Create a text response with known length. + */ + public static Response newFixedLengthResponse(IStatus status, String mimeType, String txt) { + ContentType contentType = new ContentType(mimeType); + if (txt == null) { + return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0); + } else { + byte[] bytes; + try { + CharsetEncoder newEncoder = Charset.forName(contentType.getEncoding()).newEncoder(); + if (!newEncoder.canEncode(txt)) { + contentType = contentType.tryUTF8(); + } + bytes = txt.getBytes(contentType.getEncoding()); + } catch (UnsupportedEncodingException e) { + NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e); + bytes = new byte[0]; + } + return newFixedLengthResponse(status, contentType.getContentTypeHeader(), new ByteArrayInputStream(bytes), + bytes.length); + } + } + + /** + * Create a text response with known length. + */ + public static Response newFixedLengthResponse(String msg) { + return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg); + } + + /** + * Override this to customize the server. + *

+ *

+ * (By default, this returns a 404 "Not Found" plain text error response.) + * + * @param session The HTTP session + * @return HTTP response, see class Response for details + */ + public Response serve(IHTTPSession session) { + Map files = new HashMap(); + Method method = session.getMethod(); + if (Method.PUT.equals(method) || Method.POST.equals(method)) { + try { + session.parseBody(files); + } catch (IOException ioe) { + return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, + "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); + } catch (ResponseException re) { + return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); + } + } + + Map parms = session.getParms(); + parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString()); + return serve(session.getUri(), method, session.getHeaders(), parms, files); + } + + /** + * Override this to customize the server. + *

+ *

+ * (By default, this returns a 404 "Not Found" plain text error response.) + * + * @param uri Percent-decoded URI without parameters, for example + * "/index.cgi" + * @param method "GET", "POST" etc. + * @param parms Parsed, percent decoded parameters from URI and, in case of + * POST, data. + * @param headers Header entries, percent decoded + * @return HTTP response, see class Response for details + */ + @Deprecated + public Response serve(String uri, Method method, Map headers, Map parms, + Map files) { + return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); + } + + /** + * Pluggable strategy for asynchronously executing requests. + * + * @param asyncRunner new strategy for handling threads. + */ + public void setAsyncRunner(AsyncRunner asyncRunner) { + this.asyncRunner = asyncRunner; + } + + /** + * Pluggable strategy for creating and cleaning up temporary files. + * + * @param tempFileManagerFactory new strategy for handling temp files. + */ + public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { + this.tempFileManagerFactory = tempFileManagerFactory; + } + + /** + * Start the server. + * + * @throws IOException if the socket is in use. + */ + public void start() throws IOException { + start(NanoHTTPD.SOCKET_READ_TIMEOUT); + } + + /** + * Starts the server (in setDaemon(true) mode). + */ + public void start(final int timeout) throws IOException { + start(timeout, true); + } + + /** + * Start the server. + * + * @param timeout timeout to use for socket connections. + * @param daemon start the thread daemon or not. + * @throws IOException if the socket is in use. + */ + public void start(final int timeout, boolean daemon) throws IOException { + this.myServerSocket = this.getServerSocketFactory().create(); + this.myServerSocket.setReuseAddress(true); + + ServerRunnable serverRunnable = createServerRunnable(timeout); + this.myThread = new Thread(serverRunnable); + this.myThread.setDaemon(daemon); + this.myThread.setName("NanoHttpd Main Listener"); + this.myThread.start(); + while (!serverRunnable.hasBinded && serverRunnable.bindException == null) { + try { + Thread.sleep(10L); + } catch (Throwable e) { + // on android this may not be allowed, that's why we + // catch throwable the wait should be very short because we are + // just waiting for the bind of the socket + } + } + if (serverRunnable.bindException != null) { + throw serverRunnable.bindException; + } + } + + /** + * Stop the server. + */ + public void stop() { + try { + safeClose(this.myServerSocket); + this.asyncRunner.closeAll(); + if (this.myThread != null) { + this.myThread.join(); + } + } catch (Exception e) { + NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e); + } + } + + public final boolean wasStarted() { + return this.myServerSocket != null && this.myThread != null; + } +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java index f1cfad6..e04a69a 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java @@ -1,154 +1,190 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -class OpenGLObjects { - - static class BufferGL implements IBufferGL { - - final int ptr; - - BufferGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteBuffers(this); - } - - } - - static class BufferArrayGL implements IBufferArrayGL { - - final int ptr; - - BufferArrayGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteVertexArrays(this); - } - - } - - static class TextureGL implements ITextureGL { - - final int ptr; - - TextureGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteTextures(this); - } - - } - - static class ProgramGL implements IProgramGL { - - final int ptr; - - ProgramGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteProgram(this); - } - - } - - static class UniformGL implements IUniformGL { - - final int ptr; - - UniformGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - } - - } - - static class ShaderGL implements IShaderGL { - - final int ptr; - - ShaderGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteShader(this); - } - - } - - static class FramebufferGL implements IFramebufferGL { - - final int ptr; - - FramebufferGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteFramebuffer(this); - } - - } - - static class RenderbufferGL implements IRenderbufferGL { - - final int ptr; - - RenderbufferGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteRenderbuffer(this); - } - - } - - static class QueryGL implements IQueryGL { - - final int ptr; - - QueryGL(int ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteQueries(this); - } - - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class OpenGLObjects { + + static class BufferGL implements IBufferGL { + + final int ptr; + + BufferGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteBuffers(this); + } + + } + + static class BufferArrayGL implements IBufferArrayGL { + + final int ptr; + + BufferArrayGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteVertexArrays(this); + } + + } + + static class TextureGL implements ITextureGL { + + final int ptr; + + TextureGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteTextures(this); + } + + } + + static class ProgramGL implements IProgramGL { + + final int ptr; + + ProgramGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteProgram(this); + } + + } + + static class UniformGL implements IUniformGL { + + final int ptr; + + UniformGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + } + + } + + static class ShaderGL implements IShaderGL { + + final int ptr; + + ShaderGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteShader(this); + } + + } + + static class FramebufferGL implements IFramebufferGL { + + final int ptr; + + FramebufferGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteFramebuffer(this); + } + + } + + static class RenderbufferGL implements IRenderbufferGL { + + final int ptr; + + RenderbufferGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteRenderbuffer(this); + } + + } + + static class QueryGL implements IQueryGL { + + final int ptr; + + QueryGL(int ptr) { + this.ptr = ptr; + } + + public int hashCode() { + return ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteQueries(this); + } + + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java index 8d11c54..9b386c8 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java @@ -1,289 +1,342 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import static org.lwjgl.glfw.GLFW.*; - -import java.awt.Component; -import java.awt.Desktop; -import java.awt.EventQueue; -import java.awt.HeadlessException; -import java.awt.Toolkit; -import java.awt.Dialog.ModalExclusionType; -import java.awt.Dialog.ModalityType; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URI; - -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; -import javax.swing.filechooser.FileFilter; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.MainMenuCreditsDialog; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformApplication { - - private static long win = 0l; - - static void initHooks(long glfwWindow) { - win = glfwWindow; - } - - public static void openLink(String url) { - try { - Desktop.getDesktop().browse(new URI(url)); - } catch (Throwable var5) { - EagRuntime.debugPrintStackTrace(var5); - } - } - - public static void setClipboard(String text) { - glfwSetClipboardString(win, text); - } - - public static String getClipboard() { - String str = glfwGetClipboardString(win); - return str == null ? "" : str; - } - - public static void setLocalStorage(String name, byte[] data) { - setLocalStorage(name, data, true); - } - - public static void setLocalStorage(String name, byte[] data, boolean hooks) { - if (data == null) { - (new File("_eagstorage." + name + ".dat")).delete(); - } else { - try (FileOutputStream f = new FileOutputStream(new File("_eagstorage." + name + ".dat"))) { - f.write(data); - } catch (IOException e) { - EagRuntime.debugPrintStackTrace(e); - } - } - } - - public static byte[] getLocalStorage(String data) { - return getLocalStorage(data, true); - } - - public static byte[] getLocalStorage(String data, boolean hooks) { - File f = new File("_eagstorage." + data + ".dat"); - if (!f.isFile()) { - return null; - } - byte[] b = new byte[(int) f.length()]; - try (FileInputStream s = new FileInputStream(f)) { - s.read(b); - return b; - } catch (IOException e) { - return null; - } - } - - public static String saveScreenshot() { - return "nothing"; - } - - public static void showPopup(String msg) { - JOptionPane pane = new JOptionPane(msg, JOptionPane.WARNING_MESSAGE, JOptionPane.DEFAULT_OPTION, null, - new Object[] { "OK" }, "OK"); - pane.setInitialValue("OK"); - JDialog dialog = pane.createDialog("EaglercraftX Runtime"); - pane.selectInitialValue(); - dialog.setIconImage(Toolkit.getDefaultToolkit().getImage("icon32.png")); - dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - dialog.setAlwaysOnTop(true); - dialog.setModal(true); - dialog.setLocationByPlatform(true); - dialog.setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE); - dialog.setModalityType(ModalityType.TOOLKIT_MODAL); - dialog.setLocationRelativeTo(null); - dialog.setVisible(true); - } - - private static volatile boolean fileChooserOpen = false; - private static volatile boolean fileChooserHasResult = false; - private static volatile FileChooserResult fileChooserResultObject = null; - - public static void displayFileChooser(final String mime, final String ext) { - if (!fileChooserOpen) { - fileChooserOpen = true; - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - runDisplayFileChooser(mime, ext); - } - }); - } - } - - private static void runDisplayFileChooser(String mime, String ext) { - try { - JFileChooser fc = new FileChooserAlwaysOnTop((new File(".")).getAbsoluteFile()); - fc.setDialogTitle("select a file"); - fc.setFileSelectionMode(JFileChooser.FILES_ONLY); - fc.setMultiSelectionEnabled(false); - fc.setFileFilter(new FileFilterExt(ext)); - if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { - File f = fc.getSelectedFile(); - if (f != null) { - String name = f.getName(); - byte[] bytes = new byte[(int) f.length()]; - try (FileInputStream is = new FileInputStream(f)) { - is.read(bytes); - } - fileChooserResultObject = new FileChooserResult(name, bytes); - } else { - fileChooserResultObject = null; - } - } - } catch (Throwable t) { - fileChooserResultObject = null; - } - fileChooserOpen = false; - fileChooserHasResult = true; - } - - private static class FileChooserAlwaysOnTop extends JFileChooser { - - private FileChooserAlwaysOnTop(File file) { - super(file); - } - - protected JDialog createDialog(Component parent) throws HeadlessException { - JDialog dialog = super.createDialog(parent); - dialog.setAlwaysOnTop(true); - return dialog; - } - - } - - private static class FileFilterExt extends FileFilter { - - private final String extension; - - private FileFilterExt(String ext) { - extension = ext; - } - - @Override - public boolean accept(File f) { - return f.isDirectory() || f.getName().endsWith("." + extension); - } - - @Override - public String getDescription() { - return extension + " files"; - } - - } - - public static boolean fileChooserHasResult() { - return fileChooserHasResult; - } - - public static FileChooserResult getFileChooserResult() { - fileChooserHasResult = false; - FileChooserResult res = fileChooserResultObject; - fileChooserResultObject = null; - return res; - } - - public static void clearFileChooserResult() { - fileChooserHasResult = false; - fileChooserResultObject = null; - } - - private static MainMenuCreditsDialog creditsDialog = null; - - public static void openCreditsPopup(String text) { - if (creditsDialog == null) { - creditsDialog = new MainMenuCreditsDialog(); - } - creditsDialog.setCreditsText(text); - creditsDialog.setLocationRelativeTo(null); - creditsDialog.setVisible(true); - } - - private static final File downloadsDirectory = new File("downloads"); - private static final Logger downloadsLogger = LogManager.getLogger("DownloadsFolder"); - - public static void downloadFileWithName(String fileName, byte[] fileContents) { - if (!downloadsDirectory.isDirectory() && !downloadsDirectory.mkdirs()) { - throw new RuntimeException("Could not create directory: " + downloadsDirectory.getAbsolutePath()); - } - - File f = new File(downloadsDirectory, fileName); - if (f.exists()) { - String name = fileName; - String ext = ""; - int i = fileName.lastIndexOf('.'); - if (i != -1) { - name = fileName.substring(0, i); - ext = fileName.substring(i); - } - - i = 0; - do { - f = new File(downloadsDirectory, name + " (" + (++i) + ")" + ext); - } while (f.exists()); - } - - try (FileOutputStream fos = new FileOutputStream(f)) { - fos.write(fileContents); - } catch (IOException ex) { - throw new RuntimeException("Could not save file: " + f.getAbsolutePath()); - } - - downloadsLogger.info("Saved {} byte file to: {}", fileContents.length, f.getAbsolutePath()); - - try { - Desktop.getDesktop().open(downloadsDirectory); - } catch (Throwable t) { - } - } - - public static void addLogMessage(String logMessage, boolean isError) { - - } - - public static boolean isShowingDebugConsole() { - return false; - } - - public static void showDebugConsole() { - - } - - public static void setMCServerWindowGlobal(String str) { - - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import static org.lwjgl.glfw.GLFW.*; + +import java.awt.Component; +import java.awt.Desktop; +import java.awt.EventQueue; +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.Dialog.ModalExclusionType; +import java.awt.Dialog.ModalityType; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.imageio.ImageIO; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.MainMenuCreditsDialog; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums; + +/** + * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformApplication { + + private static long win = 0l; + + static void initHooks(long glfwWindow) { + win = glfwWindow; + } + + public static void openLink(String url) { + URI safeURL; + try { + safeURL = new URI(url); + String proto = safeURL.getScheme(); + if(!proto.equalsIgnoreCase("http") && !proto.equalsIgnoreCase("https")) { + throw new IllegalArgumentException("Suspicious protocol: " + proto); + } + }catch(URISyntaxException | IllegalArgumentException ex) { + PlatformRuntime.logger.error("Refusing to open invalid URL: {}", url); + PlatformRuntime.logger.error(ex); + return; + } + try { + Desktop.getDesktop().browse(safeURL); + } catch (Throwable var5) { + PlatformRuntime.logger.error("Failed to browse to URL: {}", safeURL.toString()); + PlatformRuntime.logger.error(var5); + } + } + + public static void setClipboard(String text) { + glfwSetClipboardString(win, text); + } + + public static String getClipboard() { + String str = glfwGetClipboardString(win); + return str == null ? "" : str; + } + + public static void setLocalStorage(String name, byte[] data) { + setLocalStorage(name, data, true); + } + + public static void setLocalStorage(String name, byte[] data, boolean hooks) { + if (data == null) { + (new File("_eagstorage." + name + ".dat")).delete(); + } else { + try (FileOutputStream f = new FileOutputStream(new File("_eagstorage." + name + ".dat"))) { + f.write(data); + } catch (IOException e) { + EagRuntime.debugPrintStackTrace(e); + } + } + } + + public static byte[] getLocalStorage(String data) { + return getLocalStorage(data, true); + } + + public static byte[] getLocalStorage(String data, boolean hooks) { + File f = new File("_eagstorage." + data + ".dat"); + if (!f.isFile()) { + return null; + } + byte[] b = new byte[(int) f.length()]; + try (FileInputStream s = new FileInputStream(f)) { + s.read(b); + return b; + } catch (IOException e) { + return null; + } + } + + private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss"); + private static final File screeshotsDir = new File("screenshots"); + + public static String saveScreenshot() { + if(!screeshotsDir.isDirectory() && !screeshotsDir.mkdirs()) { + PlatformRuntime.logger.error("Failed to create screenshots directory: {}", screeshotsDir.getAbsolutePath()); + return "nothing"; + } + String name = "screenshot_" + dateFormatSS.format(new Date()).toString() + ".png"; + int w = PlatformInput.getWindowWidth(); + int h = PlatformInput.getWindowHeight(); + ByteBuffer screenshotBuffer = PlatformRuntime.allocateByteBuffer(w * h * 4); + PlatformOpenGL._wglReadPixels(0, 0, w, h, RealOpenGLEnums.GL_RGBA, RealOpenGLEnums.GL_UNSIGNED_BYTE, screenshotBuffer); + BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + int i; + for(int y = 0; y < h; ++y) { + for(int x = 0; x < w; ++x) { + i = (x + (h - y - 1) * w) << 2; + bufferedImage.setRGB(x, y, + ((screenshotBuffer.get(i) & 0xFF) << 16) | ((screenshotBuffer.get(i + 1) & 0xFF) << 8) + | (screenshotBuffer.get(i + 2) & 0xFF) | 0xFF000000); + } + } + PlatformRuntime.freeByteBuffer(screenshotBuffer); + File screenshotFile = new File(screeshotsDir, name); + try { + ImageIO.write(bufferedImage, "PNG", screenshotFile); + }catch(IOException ex) { + PlatformRuntime.logger.error("Failed to write screenshot: {}", screenshotFile.getAbsolutePath()); + return "nothing"; + } + PlatformRuntime.logger.info("Saved screenshot to: {}", screenshotFile.getAbsolutePath()); + return name; + } + + public static void showPopup(String msg) { + JOptionPane pane = new JOptionPane(msg, JOptionPane.WARNING_MESSAGE, JOptionPane.DEFAULT_OPTION, null, + new Object[] { "OK" }, "OK"); + pane.setInitialValue("OK"); + JDialog dialog = pane.createDialog("EaglercraftX Runtime"); + pane.selectInitialValue(); + dialog.setIconImage(Toolkit.getDefaultToolkit().getImage("icon32.png")); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setAlwaysOnTop(true); + dialog.setModal(true); + dialog.setLocationByPlatform(true); + dialog.setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE); + dialog.setModalityType(ModalityType.TOOLKIT_MODAL); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + private static volatile boolean fileChooserOpen = false; + private static volatile boolean fileChooserHasResult = false; + private static volatile FileChooserResult fileChooserResultObject = null; + + public static void displayFileChooser(final String mime, final String ext) { + if (!fileChooserOpen) { + fileChooserOpen = true; + clearFileChooserResult(); + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + runDisplayFileChooser(mime, ext); + } + }); + } + } + + private static void runDisplayFileChooser(String mime, String ext) { + try { + JFileChooser fc = new FileChooserAlwaysOnTop((new File(".")).getAbsoluteFile()); + fc.setDialogTitle("select a file"); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + fc.setFileFilter(new FileFilterExt(ext)); + if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { + File f = fc.getSelectedFile(); + if (f != null) { + String name = f.getName(); + byte[] bytes = new byte[(int) f.length()]; + try (FileInputStream is = new FileInputStream(f)) { + is.read(bytes); + } + fileChooserResultObject = new FileChooserResult(name, bytes); + } else { + fileChooserResultObject = null; + } + } + } catch (Throwable t) { + fileChooserResultObject = null; + } + fileChooserOpen = false; + fileChooserHasResult = true; + } + + private static class FileChooserAlwaysOnTop extends JFileChooser { + + private FileChooserAlwaysOnTop(File file) { + super(file); + } + + protected JDialog createDialog(Component parent) throws HeadlessException { + JDialog dialog = super.createDialog(parent); + dialog.setAlwaysOnTop(true); + return dialog; + } + + } + + private static class FileFilterExt extends FileFilter { + + private final String extension; + + private FileFilterExt(String ext) { + extension = ext; + } + + @Override + public boolean accept(File f) { + return f.isDirectory() || f.getName().endsWith("." + extension); + } + + @Override + public String getDescription() { + return extension + " files"; + } + + } + + public static boolean fileChooserHasResult() { + return fileChooserHasResult; + } + + public static FileChooserResult getFileChooserResult() { + fileChooserHasResult = false; + FileChooserResult res = fileChooserResultObject; + fileChooserResultObject = null; + return res; + } + + public static void clearFileChooserResult() { + fileChooserHasResult = false; + fileChooserResultObject = null; + } + + private static MainMenuCreditsDialog creditsDialog = null; + + public static void openCreditsPopup(String text) { + if (creditsDialog == null) { + creditsDialog = new MainMenuCreditsDialog(); + } + creditsDialog.setCreditsText(text); + creditsDialog.setLocationRelativeTo(null); + creditsDialog.setVisible(true); + } + + private static final File downloadsDirectory = new File("downloads"); + private static final Logger downloadsLogger = LogManager.getLogger("DownloadsFolder"); + + public static void downloadFileWithName(String fileName, byte[] fileContents) { + if (!downloadsDirectory.isDirectory() && !downloadsDirectory.mkdirs()) { + throw new RuntimeException("Could not create directory: " + downloadsDirectory.getAbsolutePath()); + } + + File f = new File(downloadsDirectory, fileName); + if (f.exists()) { + String name = fileName; + String ext = ""; + int i = fileName.lastIndexOf('.'); + if (i != -1) { + name = fileName.substring(0, i); + ext = fileName.substring(i); + } + + i = 0; + do { + f = new File(downloadsDirectory, name + " (" + (++i) + ")" + ext); + } while (f.exists()); + } + + try (FileOutputStream fos = new FileOutputStream(f)) { + fos.write(fileContents); + } catch (IOException ex) { + throw new RuntimeException("Could not save file: " + f.getAbsolutePath()); + } + + downloadsLogger.info("Saved {} byte file to: {}", fileContents.length, f.getAbsolutePath()); + + try { + Desktop.getDesktop().open(downloadsDirectory); + } catch (Throwable t) { + } + } + + public static void addLogMessage(String logMessage, boolean isError) { + + } + + public static boolean isShowingDebugConsole() { + return false; + } + + public static void showDebugConsole() { + + } + + public static void setMCServerWindowGlobal(String str) { + + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java index e1dced3..085de7e 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java @@ -1,97 +1,101 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.imageio.ImageIO; - -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformAssets { - - static URL getDesktopResourceURL(String path) { - File f = new File("resources", path); - if (f.isFile()) { - try { - return f.toURI().toURL(); - } catch (MalformedURLException e) { - return null; - } - } else { - return null; - } - } - - public static final byte[] getResourceBytes(String path) { - File loadFile = new File("resources", path); - byte[] ret = new byte[(int) loadFile.length()]; - try (FileInputStream is = new FileInputStream(loadFile)) { - int i, j = 0; - while (j < ret.length && (i = is.read(ret, j, ret.length - j)) != -1) { - j += i; - } - return ret; - } catch (IOException ex) { - return null; - } - } - - public static final ImageData loadImageFile(InputStream data) { - try { - BufferedImage img = ImageIO.read(data); - if (img == null) { - throw new IOException("Data is not a supported image format!"); - } - int w = img.getWidth(); - int h = img.getHeight(); - boolean a = img.getColorModel().hasAlpha(); - int[] pixels = new int[w * h]; - img.getRGB(0, 0, w, h, pixels, 0, w); - for (int i = 0; i < pixels.length; ++i) { - int j = pixels[i]; - if (!a) { - j = j | 0xFF000000; - } - pixels[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >>> 16) | - ((j & 0x000000FF) << 16); - } - return new ImageData(w, h, pixels, a); - } catch (IOException ex) { - return null; - } - } - - public static final ImageData loadImageFile(byte[] data) { - return loadImageFile(new EaglerInputStream(data)); - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.imageio.ImageIO; + +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +/** + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformAssets { + + static URL getDesktopResourceURL(String path) { + File f = new File("resources", path); + if(f.isFile()) { + try { + return f.toURI().toURL(); + } catch (MalformedURLException e) { + return null; + } + }else { + return null; + } + } + + public static boolean getResourceExists(String path) { + return (new File("resources", path)).isFile(); + } + + public static byte[] getResourceBytes(String path) { + File loadFile = new File("resources", path); + byte[] ret = new byte[(int) loadFile.length()]; + try(FileInputStream is = new FileInputStream(loadFile)) { + int i, j = 0; + while(j < ret.length && (i = is.read(ret, j, ret.length - j)) != -1) { + j += i; + } + return ret; + }catch(IOException ex) { + return null; + } + } + + public static ImageData loadImageFile(InputStream data) { + return loadImageFile(data, "image/png"); + } + + public static ImageData loadImageFile(InputStream data, String mime) { + try { + BufferedImage img = ImageIO.read(data); + if(img == null) { + throw new IOException("Data is not a supported image format!"); + } + int w = img.getWidth(); + int h = img.getHeight(); + boolean a = img.getColorModel().hasAlpha(); + int[] pixels = new int[w * h]; + img.getRGB(0, 0, w, h, pixels, 0, w); + for(int i = 0; i < pixels.length; ++i) { + int j = pixels[i]; + if(!a) { + j = j | 0xFF000000; + } + pixels[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >>> 16) | + ((j & 0x000000FF) << 16); + } + return new ImageData(w, h, pixels, a); + }catch(IOException ex) { + return null; + } + } + + public static ImageData loadImageFile(byte[] data) { + return loadImageFile(new EaglerInputStream(data), "image/png"); + } + + public static ImageData loadImageFile(byte[] data, String mime) { + return loadImageFile(new EaglerInputStream(data), mime); + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java index 2d4bb8e..0656d64 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java @@ -13,58 +13,50 @@ import paulscode.sound.codecs.CodecJOrbis; import paulscode.sound.codecs.CodecWav; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class PlatformAudio { - + protected static class PaulscodeAudioResource implements IAudioResource { - + protected final URL resourceLoc; - + protected PaulscodeAudioResource(URL resourceLoc) { this.resourceLoc = resourceLoc; } - + } - + protected static class PaulscodeAudioHandle implements IAudioHandle { - + protected final String sourceName; protected long stall; - + protected PaulscodeAudioHandle(String sourceName) { this.sourceName = sourceName; - this.stall = System.currentTimeMillis(); + this.stall = PlatformRuntime.steadyTimeMillis(); } @Override public void pause(boolean setPaused) { - if (setPaused) { - if (sndSystem.playing(sourceName)) { + if(setPaused) { + if(sndSystem.playing(sourceName)) { sndSystem.pause(sourceName); } - } else { - if (!sndSystem.playing(sourceName)) { + }else { + if(!sndSystem.playing(sourceName)) { sndSystem.play(sourceName); } } @@ -72,7 +64,7 @@ public class PlatformAudio { @Override public void restart() { - this.stall = System.currentTimeMillis(); + this.stall = PlatformRuntime.steadyTimeMillis(); sndSystem.rewind(sourceName); sndSystem.play(sourceName); } @@ -99,27 +91,26 @@ public class PlatformAudio { @Override public boolean shouldFree() { - return !sndSystem.playing(sourceName) && System.currentTimeMillis() - this.stall > 250l; // TODO: I hate - // this hack + return !sndSystem.playing(sourceName) && PlatformRuntime.steadyTimeMillis() - this.stall > 250l; //TODO: I hate this hack } - + } - + public static IAudioResource loadAudioData(String filename, boolean holdInCache) { URL ret = PlatformAssets.getDesktopResourceURL(filename); - if (ret != null) { + if(ret != null) { return new PaulscodeAudioResource(ret); - } else { + }else { return null; } } - + public static void clearAudioCache() { // browser only } public static void flushAudioCache() { - + } public static interface IAudioCacheLoader { @@ -129,12 +120,12 @@ public class PlatformAudio { public static IAudioResource loadAudioDataNew(String filename, boolean holdInCache, IAudioCacheLoader loader) { throw new UnsupportedOperationException("Browser only!"); } - + private static final Logger logger = LogManager.getLogger("EaglercraftPlatformAudio"); private static SoundSystem sndSystem = null; - + static void platformInitialize() { - logger.info("Eaglercraft still uses Paul Lamb's SoundSystem but with LWJGL3"); + logger.info("Eaglercraft uses Paul Lamb's SoundSystem (with LWJGL3)"); logger.info(" \"Author: Paul Lamb, www.paulscode.com\""); try { SoundSystemConfig.addLibrary(LibraryLWJGLOpenAL.class); @@ -146,13 +137,11 @@ public class PlatformAudio { logger.info(parString1); } } - public void importantMessage(String parString1, int parInt1) { if (!parString1.isEmpty()) { logger.warn(parString1); } } - public void errorMessage(String parString1, String parString2, int parInt1) { if (!parString2.isEmpty()) { logger.error("Error in class \"{}\"!", parString1); @@ -161,66 +150,66 @@ public class PlatformAudio { } }); sndSystem = new SoundSystem(); - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Could not initialize Paulscode SoundSystem! Is this system's OpenAL installed correctly?"); logger.error(t); sndSystem = null; } } - + static void platformShutdown() { - if (sndSystem != null) { + if(sndSystem != null) { sndSystem.cleanup(); sndSystem = null; } } - + public static boolean available() { return sndSystem != null; } - + private static int sourceCounter = 0; - + public static IAudioHandle beginPlayback(IAudioResource track, float x, float y, float z, float volume, float pitch) { - if (sndSystem == null) { + if(sndSystem == null) { return null; } - + float f1 = 16.0F; if (volume > 1.0F) { f1 *= volume; } - + String srcName = "src" + ++sourceCounter; - sndSystem.newSource(false, srcName, ((PaulscodeAudioResource) track).resourceLoc, - ((PaulscodeAudioResource) track).resourceLoc.getPath(), false, x, y, z, 2, f1); + sndSystem.newSource(false, srcName, ((PaulscodeAudioResource)track).resourceLoc, + ((PaulscodeAudioResource)track).resourceLoc.getPath(), false, x, y, z, 2, f1); sndSystem.setTemporary(srcName, true); sndSystem.setPitch(srcName, pitch); sndSystem.setVolume(srcName, volume); sndSystem.play(srcName); - + return new PaulscodeAudioHandle(srcName); } - + public static IAudioHandle beginPlaybackStatic(IAudioResource track, float volume, float pitch) { - if (sndSystem == null) { + if(sndSystem == null) { return null; } - + String srcName = "src" + ++sourceCounter; - sndSystem.newSource(false, srcName, ((PaulscodeAudioResource) track).resourceLoc, - ((PaulscodeAudioResource) track).resourceLoc.getPath(), false, 0.0f, 0.0f, 0.0f, 0, 0.0f); + sndSystem.newSource(false, srcName, ((PaulscodeAudioResource)track).resourceLoc, + ((PaulscodeAudioResource)track).resourceLoc.getPath(), false, 0.0f, 0.0f, 0.0f, 0, 0.0f); sndSystem.setTemporary(srcName, true); sndSystem.setPitch(srcName, pitch); sndSystem.setVolume(srcName, volume); sndSystem.play(srcName); - + return new PaulscodeAudioHandle(srcName); } - + public static void setListener(float x, float y, float z, float pitchDegrees, float yawDegrees) { - if (sndSystem == null) { + if(sndSystem == null) { return; } float f2 = MathHelper.cos((yawDegrees + 90.0F) * 0.017453292F); diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java deleted file mode 100644 index 2db9c9e..0000000 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformBufferFunctions { - - public static void put(ByteBuffer newBuffer, ByteBuffer flip) { - newBuffer.put(flip); - } - - public static void put(IntBuffer intBuffer, int index, int[] data) { - int p = intBuffer.position(); - intBuffer.position(index); - intBuffer.put(data); - intBuffer.position(p); - } - -} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java index e8ae9da..c8562c5 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java @@ -2,7 +2,6 @@ package net.lax1dude.eaglercraft.v1_8.internal; import java.io.File; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DebugFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.JDBCFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.JDBCFilesystemConverter; @@ -28,104 +27,50 @@ public class PlatformFilesystem { public static final Logger logger = LogManager.getLogger("PlatformFilesystem"); + @Deprecated public static final File debugFilesystemRoot = (new File("filesystem/sp")).getAbsoluteFile(); - private static IFilesystemProvider provider = null; + public static final File filesystemsRoot = (new File("filesystem")).getAbsoluteFile(); - public static String jdbcUri = null; - public static String jdbcDriver = null; + private static final boolean isLegacyFolder = checkLegacy(); - public static void initialize() { - if(provider == null) { - if(jdbcUri != null && jdbcDriver != null) { - provider = JDBCFilesystem.initialize(jdbcUri, jdbcDriver); + private static boolean checkLegacy() { + if(!debugFilesystemRoot.isDirectory()) return false; + String[] str = debugFilesystemRoot.list(); + return str != null && str.length > 0; + } + + public static IEaglerFilesystem initializePersist(String dbName) { + String jdbcUri = System.getProperty("eagler.jdbc." + dbName + ".uri"); + String jdbcDriver = System.getProperty("eagler.jdbc." + dbName + ".driver"); + if(jdbcUri != null && jdbcDriver != null) { + try { + IEaglerFilesystem provider = JDBCFilesystem.initialize(dbName, jdbcUri, jdbcDriver); if(((JDBCFilesystem)provider).isNewFilesystem() && debugFilesystemRoot.isDirectory() && debugFilesystemRoot.list().length > 0) { JDBCFilesystemConverter.convertFilesystem("Converting filesystem, please wait...", debugFilesystemRoot, provider, true); } + return provider; + }catch(Throwable t) { + logger.error("Could not open jdbc-based filesystem: {}", dbName); + logger.error(t); + return null; + } + }else { + File f; + if(isLegacyFolder && (dbName.equals("worlds") || dbName.equals("resourcePacks"))) { + f = debugFilesystemRoot; + logger.info("Note: filesystem \"{}\" will be stored in the legacy \"sp\" folder because it exists and is not empty", dbName); }else { - provider = DebugFilesystem.initialize(debugFilesystemRoot); + f = new File(filesystemsRoot, dbName); + } + try { + return DebugFilesystem.initialize(dbName, f); + }catch(Throwable t) { + logger.error("Could not open folder-based filesystem: {}", dbName); + logger.error(t); + return null; } } } - public static void setUseJDBC(String uri) { - jdbcUri = uri; - } - - public static void setJDBCDriverClass(String driver) { - jdbcDriver = driver; - } - - public static interface IFilesystemProvider { - - boolean eaglerDelete(String pathName); - - ByteBuffer eaglerRead(String pathName); - - void eaglerWrite(String pathName, ByteBuffer data); - - boolean eaglerExists(String pathName); - - boolean eaglerMove(String pathNameOld, String pathNameNew); - - int eaglerCopy(String pathNameOld, String pathNameNew); - - int eaglerSize(String pathName); - - void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive); - - } - - private static void throwNotInitialized() { - throw new UnsupportedOperationException("Filesystem has not been initialized!"); - } - - public static boolean eaglerDelete(String pathName) { - if(provider == null) throwNotInitialized(); - return provider.eaglerDelete(pathName); - } - - public static ByteBuffer eaglerRead(String pathName) { - if(provider == null) throwNotInitialized(); - return provider.eaglerRead(pathName); - } - - public static void eaglerWrite(String pathName, ByteBuffer data) { - if(provider == null) throwNotInitialized(); - provider.eaglerWrite(pathName, data); - } - - public static boolean eaglerExists(String pathName) { - if(provider == null) throwNotInitialized(); - return provider.eaglerExists(pathName); - } - - public static boolean eaglerMove(String pathNameOld, String pathNameNew) { - if(provider == null) throwNotInitialized(); - return provider.eaglerMove(pathNameOld, pathNameNew); - } - - public static int eaglerCopy(String pathNameOld, String pathNameNew) { - if(provider == null) throwNotInitialized(); - return provider.eaglerCopy(pathNameOld, pathNameNew); - } - - public static int eaglerSize(String pathName) { - if(provider == null) throwNotInitialized(); - return provider.eaglerSize(pathName); - } - - public static void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { - if(provider == null) throwNotInitialized(); - provider.eaglerIterate(pathName, itr, recursive); - } - - public static void platformShutdown() { - if(provider != null) { - if(provider instanceof JDBCFilesystem) { - ((JDBCFilesystem)provider).shutdown(); - } - provider = null; - } - } } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index 462f52c..fc5a43e 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -1,491 +1,884 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import static org.lwjgl.glfw.GLFW.*; - -import java.util.LinkedList; -import java.util.List; - -import org.lwjgl.PointerBuffer; -import org.lwjgl.glfw.GLFWVidMode; -import org.lwjgl.system.MemoryStack; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformInput { - - private static long win = 0l; - - private static long cursorDefault = 0l; - private static long cursorHand = 0l; - private static long cursorText = 0l; - - private static boolean windowFocused = true; - private static boolean windowResized = true; - - private static boolean windowCursorEntered = true; - private static boolean windowMouseGrabbed = false; - private static int cursorX = 0; - private static int cursorY = 0; - private static int cursorDX = 0; - private static int cursorDY = 0; - private static int DWheel = 0; - - private static int windowWidth = 640; - private static int windowHeight = 480; - - private static final List keyboardEventList = new LinkedList(); - private static KeyboardEvent currentKeyboardEvent = null; - - private static final char[] keyboardReleaseEventChars = new char[256]; - - private static boolean enableRepeatEvents = false; - private static int functionKeyModifier = GLFW_KEY_F; - - public static boolean lockKeys = false; - - private static final List keyboardCharList = new LinkedList(); - - private static boolean vsync = true; - private static boolean glfwVSyncState = false; - - private static class KeyboardEvent { - - protected final int key; - protected final boolean pressed; - protected final boolean repeating; - protected char resolvedCharacter = '\0'; - - protected KeyboardEvent(int key, boolean pressed, boolean repeating) { - this.key = key; - this.pressed = pressed; - this.repeating = repeating; - } - - } - - private static final List mouseEventList = new LinkedList(); - private static MouseEvent currentMouseEvent = null; - - private static class MouseEvent { - - protected final int button; - protected final boolean pressed; - protected final int posX; - protected final int posY; - protected final float wheel; - - protected MouseEvent(int button, boolean pressed, int posX, int posY, float wheel) { - this.button = button; - this.pressed = pressed; - this.posX = posX; - this.posY = posY; - this.wheel = wheel; - } - - } - - static void initHooks(long glfwWindow) { - win = glfwWindow; - - glfwSetErrorCallback((arg0, arg1) -> { - String errorString = ""; - if (arg1 != 0l) { - try (MemoryStack stack = MemoryStack.stackPush()) { - PointerBuffer pbuffer = stack.mallocPointer(1); - pbuffer.put(0, arg1); - errorString = pbuffer.getStringUTF8(0); - } - } - PlatformRuntime.logger.error("GLFW Error #{}: {}", arg0, errorString); - }); - - if (!glfwRawMouseMotionSupported()) { - throw new UnsupportedOperationException("Raw mouse movement (cursor lock) is not supported!"); - } - - int[] v1 = new int[1], v2 = new int[1]; - glfwGetFramebufferSize(glfwWindow, v1, v2); - - windowWidth = v1[0]; - windowHeight = v2[0]; - - glfwSetFramebufferSizeCallback(glfwWindow, (window, width, height) -> { - windowWidth = width; - windowHeight = height; - windowResized = true; - }); - - glfwSetWindowFocusCallback(glfwWindow, (window, focused) -> { - windowFocused = focused; - }); - - glfwSetKeyCallback(glfwWindow, (window, key, scancode, action, mods) -> { - if (key == GLFW_KEY_F11 && action == GLFW_PRESS) { - toggleFullscreen(); - } - if (glfwGetKey(glfwWindow, functionKeyModifier) == GLFW_PRESS) { - if (key >= GLFW_KEY_1 && key <= GLFW_KEY_9) { - key = key - GLFW_KEY_1 + GLFW_KEY_F1; - } - } - key = KeyboardConstants.getEaglerKeyFromGLFW(key); - keyboardEventList.add(new KeyboardEvent(key, action != GLFW_RELEASE, action == GLFW_REPEAT)); - if (keyboardEventList.size() > 64) { - keyboardEventList.remove(0); - } - }); - - glfwSetCharCallback(glfwWindow, (window, character) -> { - keyboardCharList.add(Character.valueOf((char) character)); - if (keyboardCharList.size() > 64) { - keyboardCharList.remove(0); - } - }); - - glfwSetCursorPosCallback(glfwWindow, (window, posX, posY) -> { - posY = windowHeight - posY; - if (windowMouseGrabbed) { - cursorDX -= (cursorX - (int) posX); - cursorDY -= (cursorY - (int) posY); - cursorX = (int) posX; - cursorY = (int) posY; - } else { - cursorX = (int) posX; - cursorY = (int) posY; - mouseEventList.add(new MouseEvent(-1, false, cursorX, cursorY, 0.0f)); - if (mouseEventList.size() > 64) { - mouseEventList.remove(0); - } - } - }); - - glfwSetMouseButtonCallback(glfwWindow, (window, button, action, mods) -> { - mouseEventList.add(new MouseEvent(button, action != GLFW_RELEASE, cursorX, cursorY, 0.0f)); - if (mouseEventList.size() > 64) { - mouseEventList.remove(0); - } - }); - - glfwSetCursorEnterCallback(glfwWindow, (window, enter) -> { - windowCursorEntered = enter; - }); - - glfwSetScrollCallback(glfwWindow, (window, scrollX, scrollY) -> { - DWheel += (int) scrollY; - mouseEventList.add(new MouseEvent(-1, false, cursorX, cursorY, (float) scrollY)); - if (mouseEventList.size() > 64) { - mouseEventList.remove(0); - } - }); - - cursorDefault = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR); - cursorText = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - glfwSetCursor(glfwWindow, cursorDefault); - - if (!fullscreen && startupFullscreen) { - toggleFullscreen(); - } - } - - public static int getWindowWidth() { - return windowWidth; - } - - public static int getWindowHeight() { - return windowHeight; - } - - public static boolean getWindowFocused() { - return windowFocused; - } - - public static boolean isCloseRequested() { - return glfwWindowShouldClose(win); - } - - public static void setVSync(boolean enable) { - vsync = enable; - } - - public static void update() { - glfwPollEvents(); - if (vsync != glfwVSyncState) { - glfwSwapInterval(vsync ? 1 : 0); - glfwVSyncState = vsync; - } - glfwSwapBuffers(win); - } - - public static boolean isVSyncSupported() { - return true; - } - - public static boolean wasResized() { - boolean b = windowResized; - windowResized = false; - return b; - } - - public static boolean keyboardNext() { - if (keyboardEventList.size() > 0) { - currentKeyboardEvent = keyboardEventList.remove(0); - if (currentKeyboardEvent.resolvedCharacter == '\0' && KeyboardConstants - .getKeyCharFromEagler(currentKeyboardEvent.key) != '\0') { - if (currentKeyboardEvent.pressed && keyboardCharList.size() > 0) { - currentKeyboardEvent.resolvedCharacter = keyboardCharList.remove(0); - keyboardReleaseEventChars[currentKeyboardEvent.key] = currentKeyboardEvent.resolvedCharacter; - } else if (!currentKeyboardEvent.pressed) { - currentKeyboardEvent.resolvedCharacter = keyboardReleaseEventChars[currentKeyboardEvent.key]; - keyboardReleaseEventChars[currentKeyboardEvent.key] = '\0'; - } - } - if (currentKeyboardEvent.repeating && !enableRepeatEvents) { - return keyboardNext(); - } else { - return true; - } - } else { - if (keyboardCharList.size() > 0) { - currentKeyboardEvent = new KeyboardEvent(KeyboardConstants.KEY_SPACE, true, false); - currentKeyboardEvent.resolvedCharacter = keyboardCharList.remove(0); - KeyboardEvent releaseEvent = new KeyboardEvent(KeyboardConstants.KEY_SPACE, false, false); - releaseEvent.resolvedCharacter = currentKeyboardEvent.resolvedCharacter; - keyboardEventList.add(releaseEvent); - return true; - } else { - return false; - } - } - } - - public static boolean keyboardGetEventKeyState() { - return currentKeyboardEvent.pressed; - } - - public static int keyboardGetEventKey() { - return currentKeyboardEvent.key; - } - - public static char keyboardGetEventCharacter() { - return currentKeyboardEvent.resolvedCharacter; - } - - public static boolean keyboardIsKeyDown(int key) { - if (glfwGetKey(win, functionKeyModifier) == GLFW_PRESS) { - if (key >= GLFW_KEY_1 && key <= GLFW_KEY_9) { - return false; - } - if (key >= GLFW_KEY_F1 && key <= GLFW_KEY_F9) { - key = key - GLFW_KEY_F1 + GLFW_KEY_1; - } - } - return glfwGetKey(win, KeyboardConstants.getGLFWKeyFromEagler(key)) == GLFW_PRESS; - } - - public static boolean keyboardIsRepeatEvent() { - return currentKeyboardEvent.repeating; - } - - public static void keyboardEnableRepeatEvents(boolean b) { - enableRepeatEvents = b; - } - - public static boolean mouseNext() { - if (mouseEventList.size() > 0) { - currentMouseEvent = mouseEventList.remove(0); - return true; - } else { - return false; - } - } - - public static boolean mouseGetEventButtonState() { - return currentMouseEvent.pressed; - } - - public static int mouseGetEventButton() { - return currentMouseEvent.button; - } - - public static int mouseGetEventX() { - return currentMouseEvent.posX; - } - - public static int mouseGetEventY() { - return currentMouseEvent.posY; - } - - public static int mouseGetEventDWheel() { - return (int) currentMouseEvent.wheel; - } - - public static int mouseGetX() { - return cursorX; - } - - public static int mouseGetY() { - return cursorY; - } - - public static boolean mouseIsButtonDown(int i) { - return glfwGetMouseButton(win, i) == GLFW_PRESS; - } - - public static int mouseGetDWheel() { - int i = DWheel; - DWheel = 0; - return i; - } - - public static void mouseSetGrabbed(boolean grab) { - if (grab != windowMouseGrabbed) { - cursorX = windowWidth / 2; - cursorY = windowHeight / 2; - glfwSetCursorPos(win, cursorX, cursorY); - windowMouseGrabbed = grab; - cursorDX = 0; - cursorDY = 0; - glfwSetInputMode(win, GLFW_CURSOR, grab ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); - glfwSetInputMode(win, GLFW_RAW_MOUSE_MOTION, grab ? GLFW_TRUE : GLFW_FALSE); - } - } - - public static boolean isPointerLocked() { - return windowMouseGrabbed; - } - - public static boolean isMouseGrabbed() { - return windowMouseGrabbed; - } - - public static int mouseGetDX() { - int i = cursorDX; - cursorDX = 0; - return i; - } - - public static int mouseGetDY() { - int i = cursorDY; - cursorDY = 0; - return i; - } - - public static void mouseSetCursorPosition(int x, int y) { - cursorX = x; - cursorY = y; - glfwSetCursorPos(win, x, y); - } - - public static boolean mouseIsInsideWindow() { - return windowCursorEntered; - } - - public static boolean contextLost() { - return glfwGetWindowAttrib(win, GLFW_ICONIFIED) == GLFW_TRUE; - } - - public static void setFunctionKeyModifier(int key) { - functionKeyModifier = KeyboardConstants.getGLFWKeyFromEagler(key); - } - - private static boolean fullscreen = false; - private static boolean startupFullscreen = false; - private static int[] lastPos = new int[4]; - - public static void toggleFullscreen() { - long win = PlatformRuntime.getWindowHandle(); - long mon = getCurrentMonitor(win); - GLFWVidMode mode = glfwGetVideoMode(mon); - if (fullscreen) { - glfwSetWindowMonitor(win, 0, lastPos[0], lastPos[1], lastPos[2], lastPos[3], mode.refreshRate()); - } else { - int[] x = new int[1], y = new int[1]; - glfwGetWindowPos(win, x, y); - lastPos[0] = x[0]; - lastPos[1] = y[0]; - glfwGetWindowSize(win, x, y); - lastPos[2] = x[0]; - lastPos[3] = y[0]; - glfwSetWindowMonitor(win, mon, 0, 0, mode.width(), mode.height(), mode.refreshRate()); - } - fullscreen = !fullscreen; - } - - public static void setStartupFullscreen(boolean bool) { - startupFullscreen = bool; - } - - // https://stackoverflow.com/a/31526753 - private static long getCurrentMonitor(long window) { - int nmonitors, i; - int[] wx = new int[1], wy = new int[1], ww = new int[1], wh = new int[1]; - int[] mx = new int[1], my = new int[1], mw = new int[1], mh = new int[1]; - int overlap, bestoverlap = 0; - long bestmonitor = 0; - PointerBuffer monitors; - GLFWVidMode mode; - - glfwGetWindowPos(window, wx, wy); - glfwGetWindowSize(window, ww, wh); - monitors = glfwGetMonitors(); - nmonitors = monitors.remaining(); - - for (i = 0; i < nmonitors; ++i) { - mode = glfwGetVideoMode(monitors.get(i)); - glfwGetMonitorPos(monitors.get(i), mx, my); - mw[0] = mode.width(); - mh[0] = mode.height(); - - overlap = Math.max(0, Math.min(wx[0] + ww[0], mx[0] + mw[0]) - Math.max(wx[0], mx[0])) * - Math.max(0, Math.min(wy[0] + wh[0], my[0] + mh[0]) - Math.max(wy[0], my[0])); - - if (bestoverlap < overlap) { - bestoverlap = overlap; - bestmonitor = monitors.get(i); - } - } - - return bestmonitor; - } - - public static boolean isFullscreen() { - return fullscreen; - } - - public static void showCursor(EnumCursorType cursor) { - switch (cursor) { - case DEFAULT: - default: - glfwSetCursor(win, cursorDefault); - break; - case HAND: - glfwSetCursor(win, cursorHand); - break; - case TEXT: - glfwSetCursor(win, cursorText); - break; - } - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import static org.lwjgl.glfw.GLFW.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFWGamepadState; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.system.MemoryStack; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformInput { + + private static long win = 0l; + + private static long cursorDefault = 0l; + private static long cursorHand = 0l; + private static long cursorText = 0l; + + private static boolean windowFocused = true; + private static boolean windowResized = true; + private static boolean windowResized2 = true; + + private static boolean windowCursorEntered = true; + private static boolean windowMouseGrabbed = false; + private static int cursorX = 0; + private static int cursorY = 0; + private static int cursorDX = 0; + private static int cursorDY = 0; + private static int DWheel = 0; + + private static int windowWidth = 640; + private static int windowHeight = 480; + + private static final List keyboardEventList = new LinkedList<>(); + private static KeyboardEvent currentKeyboardEvent = null; + + private static final char[] keyboardReleaseEventChars = new char[256]; + + private static boolean enableRepeatEvents = false; + private static int functionKeyModifier = GLFW_KEY_F; + + public static boolean lockKeys = false; + + private static final List keyboardCharList = new LinkedList<>(); + + private static boolean vsync = true; + private static boolean glfwVSyncState = false; + + private static class KeyboardEvent { + + protected final int key; + protected final boolean pressed; + protected final boolean repeating; + protected char resolvedCharacter = '\0'; + + protected KeyboardEvent(int key, boolean pressed, boolean repeating) { + this.key = key; + this.pressed = pressed; + this.repeating = repeating; + } + + } + + private static final List mouseEventList = new LinkedList<>(); + private static MouseEvent currentMouseEvent = null; + + private static class MouseEvent { + + protected final int button; + protected final boolean pressed; + protected final int posX; + protected final int posY; + protected final float wheel; + + protected MouseEvent(int button, boolean pressed, int posX, int posY, float wheel) { + this.button = button; + this.pressed = pressed; + this.posX = posX; + this.posY = posY; + this.wheel = wheel; + } + + } + + private static final List gamepadList = new ArrayList<>(); + private static int selectedGamepad = -1; + private static String selectedGamepadName = null; + private static String selectedGamepadUUID = null; + private static final boolean[] gamepadButtonStates = new boolean[24]; + private static final float[] gamepadAxisStates = new float[4]; + + private static class Gamepad { + + protected final int gamepadId; + protected final String gamepadName; + protected final String gamepadUUID; + + protected Gamepad(int gamepadId, String gamepadName, String gamepadUUID) { + this.gamepadId = gamepadId; + this.gamepadName = gamepadName; + this.gamepadUUID = gamepadUUID; + } + + @Override + public int hashCode() { + return Objects.hash(gamepadId, gamepadName, gamepadUUID); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Gamepad other = (Gamepad) obj; + return gamepadId == other.gamepadId && Objects.equals(gamepadName, other.gamepadName) + && Objects.equals(gamepadUUID, other.gamepadUUID); + } + } + + static void initHooks(long glfwWindow) { + win = glfwWindow; + + glfwSetErrorCallback((arg0, arg1) -> { + String errorString = ""; + if(arg1 != 0l) { + try(MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pbuffer = stack.mallocPointer(1); + pbuffer.put(0, arg1); + errorString = pbuffer.getStringUTF8(0); + } + } + PlatformRuntime.logger.error("GLFW Error #{}: {}", arg0, errorString); + }); + + if(!glfwRawMouseMotionSupported()) { + throw new UnsupportedOperationException("Raw mouse movement (cursor lock) is not supported!"); + } + + int[] v1 = new int[1], v2 = new int[1]; + glfwGetFramebufferSize(glfwWindow, v1, v2); + + windowWidth = v1[0]; + windowHeight = v2[0]; + windowResized = true; + windowResized2 = true; + + glfwSetFramebufferSizeCallback(glfwWindow, (window, width, height) -> { + if(windowWidth != width || windowHeight != height) { + windowWidth = width; + windowHeight = height; + windowResized = true; + windowResized2 = true; + } + }); + + windowFocused = true; + + glfwSetWindowFocusCallback(glfwWindow, (window, focused) -> { + windowFocused = focused; + }); + + glfwSetKeyCallback(glfwWindow, (window, key, scancode, action, mods) -> { + if (key == GLFW_KEY_F11 && action == GLFW_PRESS) { + toggleFullscreen(); + } + if(glfwGetKey(glfwWindow, functionKeyModifier) == GLFW_PRESS) { + if(key >= GLFW_KEY_1 && key <= GLFW_KEY_9) { + key = key - GLFW_KEY_1 + GLFW_KEY_F1; + } + } + key = KeyboardConstants.getEaglerKeyFromGLFW(key); + keyboardEventList.add(new KeyboardEvent(key, action != GLFW_RELEASE, action == GLFW_REPEAT)); + if(keyboardEventList.size() > 64) { + keyboardEventList.remove(0); + } + }); + + glfwSetCharCallback(glfwWindow, (window, character) -> { + keyboardCharList.add(Character.valueOf((char)character)); + if(keyboardCharList.size() > 64) { + keyboardCharList.remove(0); + } + }); + + glfwSetCursorPosCallback(glfwWindow, (window, posX, posY) -> { + posY = windowHeight - posY; + if(windowMouseGrabbed) { + cursorDX -= (cursorX - (int)posX); + cursorDY -= (cursorY - (int)posY); + cursorX = (int)posX; + cursorY = (int)posY; + }else { + cursorX = (int)posX; + cursorY = (int)posY; + mouseEventList.add(new MouseEvent(-1, false, cursorX, cursorY, 0.0f)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + } + }); + + glfwSetMouseButtonCallback(glfwWindow, (window, button, action, mods) -> { + mouseEventList.add(new MouseEvent(button, action != GLFW_RELEASE, cursorX, cursorY, 0.0f)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + }); + + glfwSetCursorEnterCallback(glfwWindow, (window, enter) -> { + windowCursorEntered = enter; + }); + + glfwSetScrollCallback(glfwWindow, (window, scrollX, scrollY) -> { + DWheel += (int)scrollY; + mouseEventList.add(new MouseEvent(-1, false, cursorX, cursorY, (float)scrollY)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + }); + + cursorDefault = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + cursorText = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + glfwSetCursor(glfwWindow, cursorDefault); + + if(!fullscreen && startupFullscreen) { + toggleFullscreen(); + } + + gamepadEnumerateDevices(); + + glfwSetJoystickCallback((jid, event) -> { + if(event == GLFW_DISCONNECTED && jid == selectedGamepad) { + selectedGamepad = -1; + } + gamepadEnumerateDevices(); + }); + } + + public static int getWindowWidth() { + return windowWidth; + } + + public static int getWindowHeight() { + return windowHeight; + } + + public static int getVisualViewportX() { + return 0; + } + + public static int getVisualViewportY() { + return 0; + } + + public static int getVisualViewportW() { + return windowWidth; + } + + public static int getVisualViewportH() { + return windowHeight; + } + + public static boolean getWindowFocused() { + return windowFocused; + } + + public static boolean isCloseRequested() { + return glfwWindowShouldClose(win); + } + + public static void setVSync(boolean enable) { + vsync = enable; + } + + public static void update() { + glfwPollEvents(); + if(vsync != glfwVSyncState) { + glfwSwapInterval(vsync ? 1 : 0); + glfwVSyncState = vsync; + } + glfwSwapBuffers(win); + } + + public static boolean isVSyncSupported() { + return true; + } + + public static boolean wasResized() { + boolean b = windowResized; + windowResized = false; + return b; + } + + public static boolean wasVisualViewportResized() { + boolean b = windowResized2; + windowResized2 = false; + return b; + } + + public static boolean keyboardNext() { + if(keyboardEventList.size() > 0) { + currentKeyboardEvent = keyboardEventList.remove(0); + if(currentKeyboardEvent.resolvedCharacter == '\0' && KeyboardConstants + .getKeyCharFromEagler(currentKeyboardEvent.key) != '\0') { + if(currentKeyboardEvent.pressed && keyboardCharList.size() > 0) { + currentKeyboardEvent.resolvedCharacter = keyboardCharList.remove(0); + keyboardReleaseEventChars[currentKeyboardEvent.key] = + currentKeyboardEvent.resolvedCharacter; + }else if(!currentKeyboardEvent.pressed) { + currentKeyboardEvent.resolvedCharacter = + keyboardReleaseEventChars[currentKeyboardEvent.key]; + keyboardReleaseEventChars[currentKeyboardEvent.key] = '\0'; + } + } + if(currentKeyboardEvent.repeating && !enableRepeatEvents) { + return keyboardNext(); + }else { + return true; + } + }else { + if(keyboardCharList.size() > 0) { + currentKeyboardEvent = new KeyboardEvent(KeyboardConstants.KEY_SPACE, true, false); + currentKeyboardEvent.resolvedCharacter = keyboardCharList.remove(0); + KeyboardEvent releaseEvent = new KeyboardEvent(KeyboardConstants.KEY_SPACE, false, false); + releaseEvent.resolvedCharacter = currentKeyboardEvent.resolvedCharacter; + keyboardEventList.add(releaseEvent); + return true; + }else { + return false; + } + } + } + + public static void keyboardFireEvent(EnumFireKeyboardEvent eventType, int eagKey, char keyChar) { + switch(eventType) { + case KEY_DOWN: + keyboardCharList.add(keyChar); + keyboardEventList.add(new KeyboardEvent(eagKey, true, false)); + break; + case KEY_UP: + if(eagKey >= 0 && eagKey < keyboardReleaseEventChars.length) { + keyboardReleaseEventChars[eagKey] = keyChar; + } + keyboardEventList.add(new KeyboardEvent(eagKey, false, false)); + break; + case KEY_REPEAT: + keyboardCharList.add(keyChar); + keyboardEventList.add(new KeyboardEvent(eagKey, true, true)); + break; + default: + throw new UnsupportedOperationException(); + } + if(keyboardEventList.size() > 64) { + keyboardEventList.remove(0); + } + if(keyboardCharList.size() > 64) { + keyboardCharList.remove(0); + } + } + + public static boolean keyboardGetEventKeyState() { + return currentKeyboardEvent.pressed; + } + + public static int keyboardGetEventKey() { + return currentKeyboardEvent.key; + } + + public static char keyboardGetEventCharacter() { + return currentKeyboardEvent.resolvedCharacter; + } + + public static boolean keyboardIsKeyDown(int key) { + if(glfwGetKey(win, functionKeyModifier) == GLFW_PRESS) { + if(key >= GLFW_KEY_1 && key <= GLFW_KEY_9) { + return false; + } + if(key >= GLFW_KEY_F1 && key <= GLFW_KEY_F9) { + key = key - GLFW_KEY_F1 + GLFW_KEY_1; + } + } + return glfwGetKey(win, KeyboardConstants.getGLFWKeyFromEagler(key)) == GLFW_PRESS; + } + + public static boolean keyboardIsRepeatEvent() { + return currentKeyboardEvent.repeating; + } + + public static void keyboardEnableRepeatEvents(boolean b) { + enableRepeatEvents = b; + } + + public static boolean mouseNext() { + if(mouseEventList.size() > 0) { + currentMouseEvent = mouseEventList.remove(0); + return true; + }else { + return false; + } + } + + public static void mouseFireMoveEvent(EnumFireMouseEvent eventType, int posX, int posY) { + if(eventType == EnumFireMouseEvent.MOUSE_MOVE) { + mouseEventList.add(new MouseEvent(-1, false, posX, posY, 0.0f)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + }else { + throw new UnsupportedOperationException(); + } + } + + public static void mouseFireButtonEvent(EnumFireMouseEvent eventType, int posX, int posY, int button) { + switch(eventType) { + case MOUSE_DOWN: + mouseEventList.add(new MouseEvent(button, true, posX, posY, 0.0f)); + break; + case MOUSE_UP: + mouseEventList.add(new MouseEvent(button, false, posX, posY, 0.0f)); + break; + default: + throw new UnsupportedOperationException(); + } + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + } + + public static void mouseFireWheelEvent(EnumFireMouseEvent eventType, int posX, int posY, float wheel) { + if(eventType == EnumFireMouseEvent.MOUSE_WHEEL) { + mouseEventList.add(new MouseEvent(-1, false, posX, posY, wheel)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + }else { + throw new UnsupportedOperationException(); + } + } + + public static boolean mouseGetEventButtonState() { + return currentMouseEvent.pressed; + } + + public static int mouseGetEventButton() { + return currentMouseEvent.button; + } + + public static int mouseGetEventX() { + return currentMouseEvent.posX; + } + + public static int mouseGetEventY() { + return currentMouseEvent.posY; + } + + public static int mouseGetEventDWheel() { + return (int)currentMouseEvent.wheel; + } + + public static int mouseGetX() { + return cursorX; + } + + public static int mouseGetY() { + return cursorY; + } + + public static boolean mouseIsButtonDown(int i) { + return glfwGetMouseButton(win, i) == GLFW_PRESS; + } + + public static int mouseGetDWheel() { + int i = DWheel; + DWheel = 0; + return i; + } + + public static void mouseSetGrabbed(boolean grab) { + if(grab != windowMouseGrabbed) { + cursorX = windowWidth / 2; + cursorY = windowHeight / 2; + glfwSetCursorPos(win, cursorX, cursorY); + windowMouseGrabbed = grab; + cursorDX = 0; + cursorDY = 0; + glfwSetInputMode(win, GLFW_CURSOR, grab ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); + glfwSetInputMode(win, GLFW_RAW_MOUSE_MOTION, grab ? GLFW_TRUE : GLFW_FALSE); + } + } + + public static boolean mouseGrabSupported() { + return true; + } + + public static boolean isPointerLocked() { + return windowMouseGrabbed; + } + + public static boolean isMouseGrabbed() { + return windowMouseGrabbed; + } + + public static int mouseGetDX() { + int i = cursorDX; + cursorDX = 0; + return i; + } + + public static int mouseGetDY() { + int i = cursorDY; + cursorDY = 0; + return i; + } + + public static void mouseSetCursorPosition(int x, int y) { + cursorX = x; + cursorY = y; + glfwSetCursorPos(win, x, y); + } + + public static boolean mouseIsInsideWindow() { + return windowCursorEntered; + } + + public static boolean contextLost() { + return glfwGetWindowAttrib(win, GLFW_ICONIFIED) == GLFW_TRUE; + } + + public static void setFunctionKeyModifier(int key) { + functionKeyModifier = KeyboardConstants.getGLFWKeyFromEagler(key); + } + + public static boolean supportsFullscreen() { + return true; + } + + private static boolean fullscreen = false; + private static boolean startupFullscreen = false; + private static int[] lastPos = new int[4]; + + public static void toggleFullscreen() { + long win = PlatformRuntime.getWindowHandle(); + long mon = getCurrentMonitor(win); + GLFWVidMode mode = glfwGetVideoMode(mon); + if (fullscreen) { + glfwSetWindowMonitor(win, 0, lastPos[0], lastPos[1], lastPos[2], lastPos[3], mode.refreshRate()); + } else { + int[] x = new int[1], y = new int[1]; + glfwGetWindowPos(win, x, y); + lastPos[0] = x[0]; + lastPos[1] = y[0]; + glfwGetWindowSize(win, x, y); + lastPos[2] = x[0]; + lastPos[3] = y[0]; + glfwSetWindowMonitor(win, mon, 0, 0, mode.width(), mode.height(), mode.refreshRate()); + } + fullscreen = !fullscreen; + } + + public static void setStartupFullscreen(boolean bool) { + startupFullscreen = bool; + } + + // https://stackoverflow.com/a/31526753 + private static long getCurrentMonitor(long window) { + int nmonitors, i; + int[] wx = new int[1], wy = new int[1], ww = new int[1], wh = new int[1]; + int[] mx = new int[1], my = new int[1], mw = new int[1], mh = new int[1]; + int overlap, bestoverlap = 0; + long bestmonitor = 0; + PointerBuffer monitors; + GLFWVidMode mode; + + glfwGetWindowPos(window, wx, wy); + glfwGetWindowSize(window, ww, wh); + monitors = glfwGetMonitors(); + nmonitors = monitors.remaining(); + + for (i = 0; i < nmonitors; ++i) { + mode = glfwGetVideoMode(monitors.get(i)); + glfwGetMonitorPos(monitors.get(i), mx, my); + mw[0] = mode.width(); + mh[0] = mode.height(); + + overlap = + Math.max(0, Math.min(wx[0] + ww[0], mx[0] + mw[0]) - Math.max(wx[0], mx[0])) * + Math.max(0, Math.min(wy[0] + wh[0], my[0] + mh[0]) - Math.max(wy[0], my[0])); + + if (bestoverlap < overlap) { + bestoverlap = overlap; + bestmonitor = monitors.get(i); + } + } + + return bestmonitor; + } + + public static boolean isFullscreen() { + return fullscreen; + } + + public static void showCursor(EnumCursorType cursor) { + switch(cursor) { + case DEFAULT: + default: + glfwSetCursor(win, cursorDefault); + break; + case HAND: + glfwSetCursor(win, cursorHand); + break; + case TEXT: + glfwSetCursor(win, cursorText); + break; + } + } + + public static boolean touchNext() { + return false; + } + + public static EnumTouchEvent touchGetEventType() { + return null; + } + + public static int touchGetEventTouchPointCount() { + return 0; + } + + public static int touchGetEventTouchX(int pointId) { + return 0; + } + + public static int touchGetEventTouchY(int pointId) { + return 0; + } + + public static float touchGetEventTouchRadiusX(int pointId) { + return 0.0f; + } + + public static float touchGetEventTouchRadiusY(int pointId) { + return 0.0f; + } + + public static float touchGetEventTouchRadiusMixed(int pointId) { + return touchGetEventTouchRadiusX(pointId) * 0.5f + touchGetEventTouchRadiusY(pointId) * 0.5f; + } + + public static float touchGetEventTouchForce(int pointId) { + return 0.0f; + } + + public static int touchGetEventTouchPointUID(int pointId) { + return 0; + } + + public static int touchPointCount() { + return 0; + } + + public static int touchPointX(int pointId) { + return 0; + } + + public static int touchPointY(int pointId) { + return 0; + } + + public static float touchRadiusX(int pointId) { + return 0.0f; + } + + public static float touchRadiusY(int pointId) { + return 0.0f; + } + + public static float touchRadiusMixed(int pointId) { + return touchRadiusX(pointId) * 0.5f + touchRadiusY(pointId) * 0.5f; + } + + public static float touchForce(int pointId) { + return 0.0f; + } + + public static int touchPointUID(int pointId) { + return 0; + } + + public static void touchSetOpenKeyboardZone(int x, int y, int w, int h) { + + } + + public static void touchCloseDeviceKeyboard() { + + } + + public static boolean touchIsDeviceKeyboardOpenMAYBE() { + return false; + } + + public static String touchGetPastedString() { + return null; + } + + private static void gamepadEnumerateDevices() { + if(selectedGamepad != -1 && !glfwJoystickIsGamepad(selectedGamepad)) { + selectedGamepad = -1; + } + List oldList = null; + if(!gamepadList.isEmpty()) { + oldList = new ArrayList<>(gamepadList); + gamepadList.clear(); + } + for(int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_16; ++i) { + if(glfwJoystickIsGamepad(i)) { + gamepadList.add(new Gamepad(i, gamepadMakeName(i), glfwGetJoystickGUID(i))); + } + } + vigg: if(selectedGamepad != -1) { + for(int i = 0, l = gamepadList.size(); i < l; ++i) { + Gamepad gp = gamepadList.get(i); + if(gp.gamepadId == selectedGamepad && gp.gamepadUUID.equals(selectedGamepadUUID)) { + break vigg; + } + } + selectedGamepad = -1; + } + if(oldList == null) { + if(!gamepadList.isEmpty()) { + for(int i = 0, l = gamepadList.size(); i < l; ++i) { + PlatformRuntime.logger.info("Found controller: {}", gamepadList.get(i).gamepadName); + } + } + }else { + if(gamepadList.isEmpty()) { + for(int i = 0, l = oldList.size(); i < l; ++i) { + PlatformRuntime.logger.info("Lost controller: {}", oldList.get(i).gamepadName); + } + }else { + Set oldGamepadUUIDs = new HashSet<>(); + for(int i = 0, l = oldList.size(); i < l; ++i) { + oldGamepadUUIDs.add(oldList.get(i).gamepadUUID); + } + Set newGamepadUUIDs = new HashSet<>(); + for(int i = 0, l = gamepadList.size(); i < l; ++i) { + newGamepadUUIDs.add(gamepadList.get(i).gamepadUUID); + } + for(int i = 0, l = oldList.size(); i < l; ++i) { + Gamepad g = oldList.get(i); + if(!newGamepadUUIDs.contains(g.gamepadUUID)) { + PlatformRuntime.logger.info("Lost controller: {}", g.gamepadName); + } + } + for(int i = 0, l = gamepadList.size(); i < l; ++i) { + Gamepad g = gamepadList.get(i); + if(!oldGamepadUUIDs.contains(g.gamepadUUID)) { + PlatformRuntime.logger.info("Found controller: {}", g.gamepadName); + } + } + } + } + + } + + private static String gamepadMakeName(int glfwId) { + String s = glfwGetGamepadName(glfwId); + if(s.endsWith(" (GLFW)")) { + s = s.substring(0, s.length() - 7); + } + return glfwGetJoystickName(glfwId) + " (" + s + ")"; + } + + public static int gamepadGetValidDeviceCount() { + return gamepadList.size(); + } + + public static String gamepadGetDeviceName(int deviceId) { + if(deviceId >= 0 && deviceId < gamepadList.size()) { + return gamepadList.get(deviceId).gamepadName; + }else { + return "Unknown"; + } + } + + public static void gamepadSetSelectedDevice(int deviceId) { + gamepadReset(); + if(deviceId >= 0 && deviceId < gamepadList.size()) { + selectedGamepad = gamepadList.get(deviceId).gamepadId; + if(!glfwJoystickIsGamepad(selectedGamepad)) { + selectedGamepad = -1; + } + }else { + selectedGamepad = -1; + } + } + + private static void gamepadReset() { + for(int i = 0; i < gamepadButtonStates.length; ++i) { + gamepadButtonStates[i] = false; + } + for(int i = 0; i < gamepadAxisStates.length; ++i) { + gamepadAxisStates[i] = 0.0f; + } + } + + public static void gamepadUpdate() { + gamepadReset(); + if(selectedGamepad != -1) { + if(!glfwJoystickIsGamepad(selectedGamepad)) { + selectedGamepad = -1; + return; + } + try(MemoryStack ms = MemoryStack.stackPush()) { + GLFWGamepadState state = GLFWGamepadState.calloc(ms); + glfwGetGamepadState(selectedGamepad, state); + java.nio.FloatBuffer axes = state.axes(); + axes.get(gamepadAxisStates); + java.nio.ByteBuffer buttons = state.buttons(); + for(int i = 0, l = buttons.remaining(); i < l && i < gamepadButtonStates.length; ++i) { + boolean v = buttons.get() != (byte)0; + int j = GamepadConstants.getEaglerButtonFromGLFW(i); + if(j != -1) { + gamepadButtonStates[j] = v; + } + } + gamepadButtonStates[GamepadConstants.GAMEPAD_LEFT_TRIGGER] = axes.get() > 0.4f; + gamepadButtonStates[GamepadConstants.GAMEPAD_RIGHT_TRIGGER] = axes.get() > 0.4f; + } + } + } + + public static boolean gamepadIsValid() { + return selectedGamepad != -1; + } + + public static String gamepadGetName() { + return selectedGamepad != -1 ? selectedGamepadName : "Unknown"; + } + + public static boolean gamepadGetButtonState(int button) { + return selectedGamepad != -1 && button >= 0 && button < gamepadButtonStates.length ? gamepadButtonStates[button] : false; + } + + public static float gamepadGetAxis(int axis) { + return selectedGamepad != -1 && axis >= 0 && axis < gamepadAxisStates.length ? gamepadAxisStates[axis] : 0.0f; + } + + public static float getDPI() { + float[] dpiX = new float[1]; + float[] dpiY = new float[1]; + glfwGetWindowContentScale(win, dpiX, dpiY); + float ret = dpiX[0] * 0.5f + dpiY[0] * 0.5f; + if(ret <= 0.0f) { + ret = 1.0f; + } + return ret; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java index 6bd9e9b..3b81f5d 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java @@ -2,164 +2,44 @@ package net.lax1dude.eaglercraft.v1_8.internal; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import org.java_websocket.enums.ReadyState; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopWebSocketClient; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class PlatformNetworking { - - static final Logger networkLogger = LogManager.getLogger("PlatformNetworking"); - - private static WebSocketPlayClient wsPlayClient = null; - static EnumEaglerConnectionState playConnectState = EnumEaglerConnectionState.CLOSED; - static EnumServerRateLimit serverRateLimit = null; - - static String currentURI = null; - - public static EnumEaglerConnectionState playConnectionState() { - return ((wsPlayClient == null || wsPlayClient.isClosed()) - && playConnectState == EnumEaglerConnectionState.CONNECTING) - ? EnumEaglerConnectionState.FAILED - : ((wsPlayClient != null && wsPlayClient.getReadyState() == ReadyState.NOT_YET_CONNECTED) - ? EnumEaglerConnectionState.CONNECTING - : (((wsPlayClient == null || wsPlayClient.isClosed()) - && playConnectState != EnumEaglerConnectionState.FAILED) - ? EnumEaglerConnectionState.CLOSED - : playConnectState)); - } - - public static void startPlayConnection(String destination) { - if (!playConnectionState().isClosed()) { - networkLogger.warn("Tried connecting to a server while already connected to a different server!"); - playDisconnect(); - } - - currentURI = destination; - - synchronized (playPackets) { - playPackets.clear(); - } - - playConnectState = EnumEaglerConnectionState.CONNECTING; - networkLogger.info("Connecting to server: {}", destination); - - URI u; - + + private static final Logger logger = LogManager.getLogger("PlatformNetworking"); + + public static IWebSocketClient openWebSocket(String socketURI) { try { - u = new URI(destination); - } catch (URISyntaxException ex) { - networkLogger.error("Invalid server URI: {}", destination); - playConnectState = EnumEaglerConnectionState.FAILED; - return; - } - - wsPlayClient = new WebSocketPlayClient(u); - wsPlayClient.connect(); - } - - public static void playDisconnect() { - if (!playConnectionState().isClosed() && wsPlayClient != null) { - try { - wsPlayClient.closeBlocking(); - } catch (InterruptedException e) { - // :( - } - playConnectState = EnumEaglerConnectionState.CLOSED; - } - } - - private static final List playPackets = new LinkedList(); - - public static byte[] readPlayPacket() { - synchronized (playPackets) { - return playPackets.size() > 0 ? playPackets.remove(0) : null; - } - } - - public static List readAllPacket() { - synchronized (playPackets) { - if (!playPackets.isEmpty()) { - List ret = new ArrayList<>(playPackets); - playPackets.clear(); - return ret; - } else { - return null; - } - } - } - - public static int countAvailableReadData() { - int total = 0; - synchronized (playPackets) { - for (int i = 0, l = playPackets.size(); i < l; ++i) { - total += playPackets.get(i).length; - } - } - return total; - } - - static void recievedPlayPacket(byte[] arg0) { - synchronized (playPackets) { - playPackets.add(arg0); - } - } - - public static void writePlayPacket(byte[] pkt) { - if (wsPlayClient == null || wsPlayClient.isClosed()) { - networkLogger.error("Tried to send {} byte play packet while the socket was closed!", pkt.length); - } else { - wsPlayClient.send(pkt); - } - } - - public static IServerQuery sendServerQuery(String uri, String accept) { - URI u; - - try { - u = new URI(uri); - } catch (URISyntaxException ex) { - networkLogger.error("Invalid server URI: {}", uri); - playConnectState = EnumEaglerConnectionState.FAILED; + URI uri = new URI(socketURI); + return new DesktopWebSocketClient(uri); + }catch(Throwable t) { + logger.error("Could not open WebSocket to \"{}\"!", socketURI); + logger.error(t); return null; } - - return new WebSocketServerQuery(accept, u); } - - public static EnumServerRateLimit getRateLimit() { - return serverRateLimit == null ? EnumServerRateLimit.OK : serverRateLimit; + + public static IWebSocketClient openWebSocketUnsafe(String socketURI) throws URISyntaxException { + URI uri = new URI(socketURI); + return new DesktopWebSocketClient(uri); } - - public static String getCurrentURI() { - return currentURI; - } - + } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java index f2a61dc..d861d08 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java @@ -1,553 +1,811 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; - -import static org.lwjgl.opengles.GLES30.*; - -import org.lwjgl.opengles.GLESCapabilities; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformOpenGL { - - private static boolean hasLinearHDR32FSupport = false; - - static void setCurrentContext(GLESCapabilities caps) { - hasLinearHDR32FSupport = caps.GL_OES_texture_float_linear; - } - - public static final void _wglEnable(int glEnum) { - glEnable(glEnum); - } - - public static final void _wglDisable(int glEnum) { - glDisable(glEnum); - } - - public static final void _wglClearColor(float r, float g, float b, float a) { - glClearColor(r, g, b, a); - } - - public static final void _wglClearDepth(float f) { - glClearDepthf(f); - } - - public static final void _wglClear(int bits) { - glClear(bits); - } - - public static final void _wglDepthFunc(int glEnum) { - glDepthFunc(glEnum); - } - - public static final void _wglDepthMask(boolean mask) { - glDepthMask(mask); - } - - public static final void _wglCullFace(int glEnum) { - glCullFace(glEnum); - } - - public static final void _wglViewport(int x, int y, int w, int h) { - glViewport(x, y, w, h); - } - - public static final void _wglBlendFunc(int src, int dst) { - glBlendFunc(src, dst); - } - - public static final void _wglBlendFuncSeparate(int srcColor, int dstColor, int srcAlpha, int dstAlpha) { - glBlendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha); - } - - public static final void _wglBlendEquation(int glEnum) { - glBlendEquation(glEnum); - } - - public static final void _wglBlendColor(float r, float g, float b, float a) { - glBlendColor(r, g, b, a); - } - - public static final void _wglColorMask(boolean r, boolean g, boolean b, boolean a) { - glColorMask(r, g, b, a); - } - - public static final void _wglDrawBuffers(int buffer) { - glDrawBuffers(buffer); - } - - public static final void _wglDrawBuffers(int[] buffers) { - glDrawBuffers(buffers); - } - - public static final void _wglReadBuffer(int buffer) { - glReadBuffer(buffer); - } - - public static final void _wglPolygonOffset(float f1, float f2) { - glPolygonOffset(f1, f2); - } - - public static final void _wglLineWidth(float width) { - glLineWidth(width); - } - - public static final IBufferGL _wglGenBuffers() { - return new OpenGLObjects.BufferGL(glGenBuffers()); - } - - public static final ITextureGL _wglGenTextures() { - return new OpenGLObjects.TextureGL(glGenTextures()); - } - - public static final IBufferArrayGL _wglGenVertexArrays() { - return new OpenGLObjects.BufferArrayGL(glGenVertexArrays()); - } - - public static final IProgramGL _wglCreateProgram() { - return new OpenGLObjects.ProgramGL(glCreateProgram()); - } - - public static final IShaderGL _wglCreateShader(int type) { - return new OpenGLObjects.ShaderGL(glCreateShader(type)); - } - - public static final IFramebufferGL _wglCreateFramebuffer() { - return new OpenGLObjects.FramebufferGL(glGenFramebuffers()); - } - - public static final IRenderbufferGL _wglCreateRenderbuffer() { - return new OpenGLObjects.RenderbufferGL(glGenRenderbuffers()); - } - - public static final IQueryGL _wglGenQueries() { - return new OpenGLObjects.QueryGL(glGenQueries()); - } - - public static final void _wglDeleteBuffers(IBufferGL obj) { - glDeleteBuffers(((OpenGLObjects.BufferGL) obj).ptr); - } - - public static final void _wglDeleteTextures(ITextureGL obj) { - glDeleteTextures(((OpenGLObjects.TextureGL) obj).ptr); - } - - public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) { - glDeleteVertexArrays(((OpenGLObjects.BufferArrayGL) obj).ptr); - } - - public static final void _wglDeleteProgram(IProgramGL obj) { - glDeleteProgram(((OpenGLObjects.ProgramGL) obj).ptr); - } - - public static final void _wglDeleteShader(IShaderGL obj) { - glDeleteShader(((OpenGLObjects.ShaderGL) obj).ptr); - } - - public static final void _wglDeleteFramebuffer(IFramebufferGL obj) { - glDeleteFramebuffers(((OpenGLObjects.FramebufferGL) obj).ptr); - } - - public static final void _wglDeleteRenderbuffer(IRenderbufferGL obj) { - glDeleteRenderbuffers(((OpenGLObjects.RenderbufferGL) obj).ptr); - } - - public static final void _wglDeleteQueries(IQueryGL obj) { - glDeleteQueries(((OpenGLObjects.QueryGL) obj).ptr); - } - - public static final void _wglBindBuffer(int target, IBufferGL obj) { - glBindBuffer(target, obj == null ? 0 : ((OpenGLObjects.BufferGL) obj).ptr); - } - - public static final void _wglBufferData(int target, ByteBuffer data, int usage) { - nglBufferData(target, data == null ? 0 : data.remaining(), - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); - } - - public static final void _wglBufferData(int target, IntBuffer data, int usage) { - nglBufferData(target, data == null ? 0 : (data.remaining() << 2), - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); - } - - public static final void _wglBufferData(int target, FloatBuffer data, int usage) { - nglBufferData(target, data == null ? 0 : (data.remaining() << 2), - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); - } - - public static final void _wglBufferData(int target, int size, int usage) { - glBufferData(target, size, usage); - } - - public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) { - nglBufferSubData(target, offset, data == null ? 0 : data.remaining(), - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglBufferSubData(int target, int offset, IntBuffer data) { - nglBufferSubData(target, offset, data == null ? 0 : (data.remaining() << 2), - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) { - nglBufferSubData(target, offset, data == null ? 0 : (data.remaining() << 2), - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglBindVertexArray(IBufferArrayGL obj) { - glBindVertexArray(obj == null ? 0 : ((OpenGLObjects.BufferArrayGL) obj).ptr); - } - - public static final void _wglEnableVertexAttribArray(int index) { - glEnableVertexAttribArray(index); - } - - public static final void _wglDisableVertexAttribArray(int index) { - glDisableVertexAttribArray(index); - } - - public static final void _wglVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, - int offset) { - glVertexAttribPointer(index, size, type, normalized, stride, offset); - } - - public static final void _wglVertexAttribDivisor(int index, int divisor) { - glVertexAttribDivisor(index, divisor); - } - - public static final void _wglActiveTexture(int texture) { - glActiveTexture(texture); - } - - public static final void _wglBindTexture(int target, ITextureGL obj) { - glBindTexture(target, obj == null ? 0 : ((OpenGLObjects.TextureGL) obj).ptr); - } - - public static final void _wglTexParameterf(int target, int param, float value) { - glTexParameterf(target, param, value); - } - - public static final void _wglTexParameteri(int target, int param, int value) { - glTexParameteri(target, param, value); - } - - public static final void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, - int border, int format, int type, ByteBuffer data) { - nglTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, - int border, int format, int type, ByteBuffer data) { - nglTexImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, - int border, int format, int type, IntBuffer data) { - nglTexImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, - int border, int format, int type, FloatBuffer data) { - nglTexImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexImage2Du16(int target, int level, int internalFormat, int width, int height, - int border, int format, int type, ByteBuffer data) { - nglTexImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, int height, - int border, int format, int type, ByteBuffer data) { - nglTexImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, - int format, int type, ByteBuffer data) { - nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, - int format, int type, IntBuffer data) { - nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, - int format, int type, FloatBuffer data) { - nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset, int width, - int height, - int format, int type, ByteBuffer data) { - nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); - } - - public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, - int width, int height) { - glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); - } - - public static final void _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h) { - glTexStorage2D(target, levels, internalFormat, w, h); - } - - public static final void _wglPixelStorei(int pname, int value) { - glPixelStorei(pname, value); - } - - public static final void _wglGenerateMipmap(int target) { - glGenerateMipmap(target); - } - - public static final void _wglShaderSource(IShaderGL obj, String source) { - glShaderSource(((OpenGLObjects.ShaderGL) obj).ptr, source); - } - - public static final void _wglCompileShader(IShaderGL obj) { - glCompileShader(((OpenGLObjects.ShaderGL) obj).ptr); - } - - public static final int _wglGetShaderi(IShaderGL obj, int param) { - return glGetShaderi(((OpenGLObjects.ShaderGL) obj).ptr, param); - } - - public static final String _wglGetShaderInfoLog(IShaderGL obj) { - return glGetShaderInfoLog(((OpenGLObjects.ShaderGL) obj).ptr); - } - - public static final void _wglUseProgram(IProgramGL obj) { - glUseProgram(obj == null ? 0 : ((OpenGLObjects.ProgramGL) obj).ptr); - } - - public static final void _wglAttachShader(IProgramGL obj, IShaderGL shader) { - glAttachShader(((OpenGLObjects.ProgramGL) obj).ptr, ((OpenGLObjects.ShaderGL) shader).ptr); - } - - public static final void _wglDetachShader(IProgramGL obj, IShaderGL shader) { - glDetachShader(((OpenGLObjects.ProgramGL) obj).ptr, ((OpenGLObjects.ShaderGL) shader).ptr); - } - - public static final void _wglLinkProgram(IProgramGL obj) { - glLinkProgram(((OpenGLObjects.ProgramGL) obj).ptr); - } - - public static final int _wglGetProgrami(IProgramGL obj, int param) { - return glGetProgrami(((OpenGLObjects.ProgramGL) obj).ptr, param); - } - - public static final String _wglGetProgramInfoLog(IProgramGL obj) { - return glGetProgramInfoLog(((OpenGLObjects.ProgramGL) obj).ptr); - } - - public static final void _wglBindAttribLocation(IProgramGL obj, int index, String name) { - glBindAttribLocation(((OpenGLObjects.ProgramGL) obj).ptr, index, name); - } - - public static final int _wglGetAttribLocation(IProgramGL obj, String name) { - return glGetAttribLocation(((OpenGLObjects.ProgramGL) obj).ptr, name); - } - - public static final void _wglDrawArrays(int mode, int first, int count) { - glDrawArrays(mode, first, count); - } - - public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) { - glDrawArraysInstanced(mode, first, count, instanced); - } - - public static final void _wglDrawElements(int mode, int count, int type, int offset) { - glDrawElements(mode, count, type, offset); - } - - public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) { - glDrawElementsInstanced(mode, count, type, offset, instanced); - } - - public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) { - int loc = glGetUniformLocation(((OpenGLObjects.ProgramGL) obj).ptr, name); - return loc < 0 ? null : new OpenGLObjects.UniformGL(loc); - } - - public static final int _wglGetUniformBlockIndex(IProgramGL obj, String name) { - return glGetUniformBlockIndex(((OpenGLObjects.ProgramGL) obj).ptr, name); - } - - public static final void _wglBindBufferRange(int target, int index, IBufferGL buffer, int offset, int size) { - glBindBufferRange(target, index, ((OpenGLObjects.BufferGL) buffer).ptr, offset, size); - } - - public static final void _wglUniformBlockBinding(IProgramGL obj, int blockIndex, int bufferIndex) { - glUniformBlockBinding(((OpenGLObjects.ProgramGL) obj).ptr, blockIndex, bufferIndex); - } - - public static final void _wglUniform1f(IUniformGL obj, float x) { - if (obj != null) - glUniform1f(((OpenGLObjects.UniformGL) obj).ptr, x); - } - - public static final void _wglUniform2f(IUniformGL obj, float x, float y) { - if (obj != null) - glUniform2f(((OpenGLObjects.UniformGL) obj).ptr, x, y); - } - - public static final void _wglUniform3f(IUniformGL obj, float x, float y, float z) { - if (obj != null) - glUniform3f(((OpenGLObjects.UniformGL) obj).ptr, x, y, z); - } - - public static final void _wglUniform4f(IUniformGL obj, float x, float y, float z, float w) { - if (obj != null) - glUniform4f(((OpenGLObjects.UniformGL) obj).ptr, x, y, z, w); - } - - public static final void _wglUniform1i(IUniformGL obj, int x) { - if (obj != null) - glUniform1i(((OpenGLObjects.UniformGL) obj).ptr, x); - } - - public static final void _wglUniform2i(IUniformGL obj, int x, int y) { - if (obj != null) - glUniform2i(((OpenGLObjects.UniformGL) obj).ptr, x, y); - } - - public static final void _wglUniform3i(IUniformGL obj, int x, int y, int z) { - if (obj != null) - glUniform3i(((OpenGLObjects.UniformGL) obj).ptr, x, y, z); - } - - public static final void _wglUniform4i(IUniformGL obj, int x, int y, int z, int w) { - if (obj != null) - glUniform4i(((OpenGLObjects.UniformGL) obj).ptr, x, y, z, w); - } - - public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if (obj != null) - nglUniformMatrix2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 2, transpose, - EaglerLWJGLAllocator.getAddress(mat)); - } - - public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if (obj != null) - nglUniformMatrix3fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 9, transpose, - EaglerLWJGLAllocator.getAddress(mat)); - } - - public static final void _wglUniformMatrix3x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if (obj != null) - nglUniformMatrix3x2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 6, transpose, - EaglerLWJGLAllocator.getAddress(mat)); - } - - public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if (obj != null) - nglUniformMatrix4fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 4, transpose, - EaglerLWJGLAllocator.getAddress(mat)); - } - - public static final void _wglUniformMatrix4x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if (obj != null) - nglUniformMatrix4x2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 3, transpose, - EaglerLWJGLAllocator.getAddress(mat)); - } - - public static final void _wglUniformMatrix4x3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if (obj != null) - nglUniformMatrix4x3fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 12, transpose, - EaglerLWJGLAllocator.getAddress(mat)); - } - - public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) { - if (framebuffer == null) { - glBindFramebuffer(target, 0); - } else { - glBindFramebuffer(target, ((OpenGLObjects.FramebufferGL) framebuffer).ptr); - } - } - - public static final int _wglCheckFramebufferStatus(int target) { - return glCheckFramebufferStatus(target); - } - - public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget, ITextureGL texture, - int level) { - glFramebufferTexture2D(target, attachment, texTarget, ((OpenGLObjects.TextureGL) texture).ptr, level); - } - - public static final void _wglFramebufferTextureLayer(int target, int attachment, ITextureGL texture, int level, - int layer) { - glFramebufferTextureLayer(target, attachment, ((OpenGLObjects.TextureGL) texture).ptr, level, layer); - } - - public static final void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, - int dstX1, int dstY1, int bits, int filter) { - glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter); - } - - public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) { - glBindRenderbuffer(target, renderbuffer == null ? 0 : ((OpenGLObjects.RenderbufferGL) renderbuffer).ptr); - } - - public static final void _wglRenderbufferStorage(int target, int internalformat, int width, int height) { - glRenderbufferStorage(target, internalformat, width, height); - } - - public static final void _wglFramebufferRenderbuffer(int target, int attachment, int renderbufferTarget, - IRenderbufferGL renderbuffer) { - glFramebufferRenderbuffer(target, attachment, renderbufferTarget, - ((OpenGLObjects.RenderbufferGL) renderbuffer).ptr); - } - - public static final String _wglGetString(int param) { - return glGetString(param); - } - - public static final int _wglGetInteger(int param) { - return glGetInteger(param); - } - - public static final int _wglGetError() { - return glGetError(); - } - - public static final boolean checkHDRFramebufferSupport(int bits) { - return true; - } - - public static final boolean checkLinearHDR32FSupport() { - return hasLinearHDR32FSupport; - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; + +import static org.lwjgl.opengles.GLES30.*; +import static org.lwjgl.opengles.ANGLEInstancedArrays.*; +import static org.lwjgl.opengles.EXTInstancedArrays.*; +import static org.lwjgl.opengles.EXTTextureStorage.*; +import static org.lwjgl.opengles.OESVertexArrayObject.*; + +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.opengles.GLESCapabilities; + +/** + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformOpenGL { + + private static int glesVers = -1; + + private static boolean hasANGLEInstancedArrays = false; + private static boolean hasEXTColorBufferFloat = false; + private static boolean hasEXTColorBufferHalfFloat = false; + private static boolean hasEXTGPUShader5 = false; + private static boolean hasEXTInstancedArrays = false; + private static boolean hasEXTShaderTextureLOD = false; + private static boolean hasEXTTextureStorage = false; + private static boolean hasOESFBORenderMipmap = false; + private static boolean hasOESGPUShader5 = false; + private static boolean hasOESVertexArrayObject = false; + private static boolean hasOESTextureFloat = false; + private static boolean hasOESTextureFloatLinear = false; + private static boolean hasOESTextureHalfFloat = false; + private static boolean hasOESTextureHalfFloatLinear = false; + private static boolean hasEXTTextureFilterAnisotropic = false; + + private static boolean hasFBO16FSupport = false; + private static boolean hasFBO32FSupport = false; + private static boolean hasLinearHDR16FSupport = false; + private static boolean hasLinearHDR32FSupport = false; + + private static final int VAO_IMPL_NONE = -1; + private static final int VAO_IMPL_CORE = 0; + private static final int VAO_IMPL_OES = 1; + private static int vertexArrayImpl = VAO_IMPL_NONE; + + private static final int INSTANCE_IMPL_NONE = -1; + private static final int INSTANCE_IMPL_CORE = 0; + private static final int INSTANCE_IMPL_ANGLE = 1; + private static final int INSTANCE_IMPL_EXT = 2; + private static int instancingImpl = INSTANCE_IMPL_NONE; + + private static final int TEX_STORAGE_IMPL_NONE = -1; + private static final int TEX_STORAGE_IMPL_CORE = 0; + private static final int TEX_STORAGE_IMPL_EXT = 1; + private static int texStorageImpl = TEX_STORAGE_IMPL_NONE; + + static void setCurrentContext(int glesVersIn, GLESCapabilities caps) { + glesVers = glesVersIn; + + hasANGLEInstancedArrays = glesVersIn == 200 && caps.GL_ANGLE_instanced_arrays; + hasOESTextureFloat = glesVersIn == 200 && caps.GL_OES_texture_float; + hasOESTextureFloatLinear = glesVersIn >= 300 && caps.GL_OES_texture_float_linear; + hasOESTextureHalfFloat = glesVersIn == 200 && caps.GL_OES_texture_half_float; + hasOESTextureHalfFloatLinear = glesVersIn == 200 && caps.GL_OES_texture_half_float_linear; + hasEXTColorBufferFloat = (glesVersIn == 310 || glesVersIn == 300) && caps.GL_EXT_color_buffer_float; + hasEXTColorBufferHalfFloat = !hasEXTColorBufferFloat + && (glesVersIn == 310 || glesVersIn == 300 || glesVersIn == 200) && caps.GL_EXT_color_buffer_half_float; + hasEXTInstancedArrays = !hasANGLEInstancedArrays && glesVersIn == 200 && caps.GL_EXT_instanced_arrays; + hasEXTShaderTextureLOD = glesVersIn == 200 && caps.GL_EXT_shader_texture_lod; + hasEXTTextureStorage = glesVersIn == 200 && caps.GL_EXT_texture_storage; + hasOESGPUShader5 = glesVersIn == 310 && caps.GL_OES_gpu_shader5; + hasEXTGPUShader5 = !hasOESGPUShader5 && glesVersIn == 310 && caps.GL_EXT_gpu_shader5; + hasOESFBORenderMipmap = glesVersIn == 200 && caps.GL_OES_fbo_render_mipmap; + hasOESVertexArrayObject = glesVersIn == 200 && caps.GL_OES_vertex_array_object; + hasLinearHDR32FSupport = caps.GL_OES_texture_float_linear; + hasEXTTextureFilterAnisotropic = caps.GL_EXT_texture_filter_anisotropic; + + hasFBO16FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureFloat) && (hasEXTColorBufferFloat || hasEXTColorBufferHalfFloat)); + hasFBO32FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureHalfFloat) && hasEXTColorBufferFloat); + hasLinearHDR16FSupport = glesVersIn >= 300 || hasOESTextureHalfFloatLinear; + hasLinearHDR32FSupport = glesVersIn >= 300 && hasOESTextureFloatLinear; + + if(glesVersIn >= 300) { + vertexArrayImpl = VAO_IMPL_CORE; + instancingImpl = INSTANCE_IMPL_CORE; + texStorageImpl = TEX_STORAGE_IMPL_CORE; + }else if(glesVersIn == 200) { + vertexArrayImpl = hasOESVertexArrayObject ? VAO_IMPL_OES : VAO_IMPL_NONE; + instancingImpl = hasANGLEInstancedArrays ? INSTANCE_IMPL_ANGLE : (hasEXTInstancedArrays ? INSTANCE_IMPL_EXT : INSTANCE_IMPL_NONE); + texStorageImpl = hasEXTTextureStorage ? TEX_STORAGE_IMPL_EXT : TEX_STORAGE_IMPL_NONE; + }else { + vertexArrayImpl = VAO_IMPL_NONE; + instancingImpl = INSTANCE_IMPL_NONE; + texStorageImpl = TEX_STORAGE_IMPL_NONE; + } + } + + public static final List dumpActiveExtensions() { + List exts = new ArrayList<>(); + if(hasANGLEInstancedArrays) exts.add("ANGLE_instanced_arrays"); + if(hasEXTColorBufferFloat) exts.add("EXT_color_buffer_float"); + if(hasEXTColorBufferHalfFloat) exts.add("EXT_color_buffer_half_float"); + if(hasEXTGPUShader5) exts.add("EXT_gpu_shader5"); + if(hasEXTInstancedArrays) exts.add("EXT_instanced_arrays"); + if(hasEXTTextureStorage) exts.add("EXT_texture_storage"); + if(hasOESFBORenderMipmap) exts.add("OES_fbo_render_mipmap"); + if(hasOESGPUShader5) exts.add("OES_gpu_shader5"); + if(hasOESVertexArrayObject) exts.add("OES_vertex_array_object"); + if(hasOESTextureFloat) exts.add("OES_texture_float"); + if(hasOESTextureFloatLinear) exts.add("OES_texture_float_linear"); + if(hasOESTextureHalfFloat) exts.add("OES_texture_half_float"); + if(hasOESTextureHalfFloatLinear) exts.add("OES_texture_half_float_linear"); + if(hasEXTTextureFilterAnisotropic) exts.add("EXT_texture_filter_anisotropic"); + return exts; + } + + public static final void _wglEnable(int glEnum) { + glEnable(glEnum); + } + + public static final void _wglDisable(int glEnum) { + glDisable(glEnum); + } + + public static final void _wglClearColor(float r, float g, float b, float a) { + glClearColor(r, g, b, a); + } + + public static final void _wglClearDepth(float f) { + glClearDepthf(f); + } + + public static final void _wglClear(int bits) { + glClear(bits); + } + + public static final void _wglDepthFunc(int glEnum) { + glDepthFunc(glEnum); + } + + public static final void _wglDepthMask(boolean mask) { + glDepthMask(mask); + } + + public static final void _wglCullFace(int glEnum) { + glCullFace(glEnum); + } + + public static final void _wglViewport(int x, int y, int w, int h) { + glViewport(x, y, w, h); + } + + public static final void _wglBlendFunc(int src, int dst) { + glBlendFunc(src, dst); + } + + public static final void _wglBlendFuncSeparate(int srcColor, int dstColor, int srcAlpha, int dstAlpha) { + glBlendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha); + } + + public static final void _wglBlendEquation(int glEnum) { + glBlendEquation(glEnum); + } + + public static final void _wglBlendColor(float r, float g, float b, float a) { + glBlendColor(r, g, b, a); + } + + public static final void _wglColorMask(boolean r, boolean g, boolean b, boolean a) { + glColorMask(r, g, b, a); + } + + public static final void _wglDrawBuffers(int buffer) { + if(glesVers == 200) { + if(buffer != 0x8CE0) { // GL_COLOR_ATTACHMENT0 + throw new UnsupportedOperationException(); + } + }else { + glDrawBuffers(buffer); + } + } + + public static final void _wglDrawBuffers(int[] buffers) { + if(glesVers == 200) { + if(buffers.length != 1 || buffers[0] != 0x8CE0) { // GL_COLOR_ATTACHMENT0 + throw new UnsupportedOperationException(); + } + }else { + glDrawBuffers(buffers); + } + } + + public static final void _wglReadBuffer(int buffer) { + glReadBuffer(buffer); + } + + public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) { + nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglReadPixels_u16(int x, int y, int width, int height, int format, int type, ByteBuffer data) { + nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer data) { + nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer data) { + nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglPolygonOffset(float f1, float f2) { + glPolygonOffset(f1, f2); + } + + public static final void _wglLineWidth(float width) { + glLineWidth(width); + } + + public static final IBufferGL _wglGenBuffers() { + return new OpenGLObjects.BufferGL(glGenBuffers()); + } + + public static final ITextureGL _wglGenTextures() { + return new OpenGLObjects.TextureGL(glGenTextures()); + } + + public static final IBufferArrayGL _wglGenVertexArrays() { + switch(vertexArrayImpl) { + case VAO_IMPL_CORE: + return new OpenGLObjects.BufferArrayGL(glGenVertexArrays()); + case VAO_IMPL_OES: + return new OpenGLObjects.BufferArrayGL(glGenVertexArraysOES()); + default: + throw new UnsupportedOperationException(); + } + } + + public static final IProgramGL _wglCreateProgram() { + return new OpenGLObjects.ProgramGL(glCreateProgram()); + } + + public static final IShaderGL _wglCreateShader(int type) { + return new OpenGLObjects.ShaderGL(glCreateShader(type)); + } + + public static final IFramebufferGL _wglCreateFramebuffer() { + return new OpenGLObjects.FramebufferGL(glGenFramebuffers()); + } + + public static final IRenderbufferGL _wglCreateRenderbuffer() { + return new OpenGLObjects.RenderbufferGL(glGenRenderbuffers()); + } + + public static final IQueryGL _wglGenQueries() { + return new OpenGLObjects.QueryGL(glGenQueries()); + } + + public static final void _wglDeleteBuffers(IBufferGL obj) { + glDeleteBuffers(((OpenGLObjects.BufferGL) obj).ptr); + } + + public static final void _wglDeleteTextures(ITextureGL obj) { + glDeleteTextures(((OpenGLObjects.TextureGL) obj).ptr); + } + + public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) { + int ptr = ((OpenGLObjects.BufferArrayGL) obj).ptr; + switch(vertexArrayImpl) { + case VAO_IMPL_CORE: + glDeleteVertexArrays(ptr); + break; + case VAO_IMPL_OES: + glDeleteVertexArraysOES(ptr); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglDeleteProgram(IProgramGL obj) { + glDeleteProgram(((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final void _wglDeleteShader(IShaderGL obj) { + glDeleteShader(((OpenGLObjects.ShaderGL) obj).ptr); + } + + public static final void _wglDeleteFramebuffer(IFramebufferGL obj) { + glDeleteFramebuffers(((OpenGLObjects.FramebufferGL) obj).ptr); + } + + public static final void _wglDeleteRenderbuffer(IRenderbufferGL obj) { + glDeleteRenderbuffers(((OpenGLObjects.RenderbufferGL) obj).ptr); + } + + public static final void _wglDeleteQueries(IQueryGL obj) { + glDeleteQueries(((OpenGLObjects.QueryGL) obj).ptr); + } + + public static final void _wglBindBuffer(int target, IBufferGL obj) { + glBindBuffer(target, obj == null ? 0 : ((OpenGLObjects.BufferGL) obj).ptr); + } + + public static final void _wglBufferData(int target, ByteBuffer data, int usage) { + nglBufferData(target, data == null ? 0 : data.remaining(), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); + } + + public static final void _wglBufferData(int target, IntBuffer data, int usage) { + nglBufferData(target, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); + } + + public static final void _wglBufferData(int target, FloatBuffer data, int usage) { + nglBufferData(target, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); + } + + public static final void _wglBufferData(int target, int size, int usage) { + glBufferData(target, size, usage); + } + + public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) { + nglBufferSubData(target, offset, data == null ? 0 : data.remaining(), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglBufferSubData(int target, int offset, IntBuffer data) { + nglBufferSubData(target, offset, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) { + nglBufferSubData(target, offset, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglBindVertexArray(IBufferArrayGL obj) { + int ptr = obj == null ? 0 : ((OpenGLObjects.BufferArrayGL) obj).ptr; + switch(vertexArrayImpl) { + case VAO_IMPL_CORE: + glBindVertexArray(ptr); + break; + case VAO_IMPL_OES: + glBindVertexArrayOES(ptr); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglEnableVertexAttribArray(int index) { + glEnableVertexAttribArray(index); + } + + public static final void _wglDisableVertexAttribArray(int index) { + glDisableVertexAttribArray(index); + } + + public static final void _wglVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, + int offset) { + glVertexAttribPointer(index, size, type, normalized, stride, offset); + } + + public static final void _wglVertexAttribDivisor(int index, int divisor) { + switch(instancingImpl) { + case INSTANCE_IMPL_CORE: + glVertexAttribDivisor(index, divisor); + break; + case INSTANCE_IMPL_ANGLE: + glVertexAttribDivisorANGLE(index, divisor); + break; + case INSTANCE_IMPL_EXT: + glVertexAttribDivisorEXT(index, divisor); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglActiveTexture(int texture) { + glActiveTexture(texture); + } + + public static final void _wglBindTexture(int target, ITextureGL obj) { + glBindTexture(target, obj == null ? 0 : ((OpenGLObjects.TextureGL) obj).ptr); + } + + public static final void _wglTexParameterf(int target, int param, float value) { + glTexParameterf(target, param, value); + } + + public static final void _wglTexParameteri(int target, int param, int value) { + glTexParameteri(target, param, value); + } + + public static final void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, + int border, int format, int type, ByteBuffer data) { + nglTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, ByteBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, IntBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, FloatBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2Du16(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, ByteBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, ByteBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, ByteBuffer data) { + nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, IntBuffer data) { + nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, FloatBuffer data) { + nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, ByteBuffer data) { + nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, + int width, int height) { + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } + + public static final void _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h) { + switch(texStorageImpl) { + case TEX_STORAGE_IMPL_CORE: + glTexStorage2D(target, levels, internalFormat, w, h); + break; + case TEX_STORAGE_IMPL_EXT: + glTexStorage2DEXT(target, levels, internalFormat, w, h); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglPixelStorei(int pname, int value) { + glPixelStorei(pname, value); + } + + public static final void _wglGenerateMipmap(int target) { + glGenerateMipmap(target); + } + + public static final void _wglShaderSource(IShaderGL obj, String source) { + glShaderSource(((OpenGLObjects.ShaderGL) obj).ptr, source); + } + + public static final void _wglCompileShader(IShaderGL obj) { + glCompileShader(((OpenGLObjects.ShaderGL) obj).ptr); + } + + public static final int _wglGetShaderi(IShaderGL obj, int param) { + return glGetShaderi(((OpenGLObjects.ShaderGL) obj).ptr, param); + } + + public static final String _wglGetShaderInfoLog(IShaderGL obj) { + return glGetShaderInfoLog(((OpenGLObjects.ShaderGL) obj).ptr); + } + + public static final void _wglUseProgram(IProgramGL obj) { + glUseProgram(obj == null ? 0 : ((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final void _wglAttachShader(IProgramGL obj, IShaderGL shader) { + glAttachShader(((OpenGLObjects.ProgramGL) obj).ptr, ((OpenGLObjects.ShaderGL) shader).ptr); + } + + public static final void _wglDetachShader(IProgramGL obj, IShaderGL shader) { + glDetachShader(((OpenGLObjects.ProgramGL) obj).ptr, ((OpenGLObjects.ShaderGL) shader).ptr); + } + + public static final void _wglLinkProgram(IProgramGL obj) { + glLinkProgram(((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final int _wglGetProgrami(IProgramGL obj, int param) { + return glGetProgrami(((OpenGLObjects.ProgramGL) obj).ptr, param); + } + + public static final String _wglGetProgramInfoLog(IProgramGL obj) { + return glGetProgramInfoLog(((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final void _wglBindAttribLocation(IProgramGL obj, int index, String name) { + glBindAttribLocation(((OpenGLObjects.ProgramGL) obj).ptr, index, name); + } + + public static final int _wglGetAttribLocation(IProgramGL obj, String name) { + return glGetAttribLocation(((OpenGLObjects.ProgramGL) obj).ptr, name); + } + + public static final void _wglDrawArrays(int mode, int first, int count) { + glDrawArrays(mode, first, count); + } + + public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) { + switch(instancingImpl) { + case INSTANCE_IMPL_CORE: + glDrawArraysInstanced(mode, first, count, instanced); + break; + case INSTANCE_IMPL_ANGLE: + glDrawArraysInstancedANGLE(mode, first, count, instanced); + break; + case INSTANCE_IMPL_EXT: + glDrawArraysInstancedEXT(mode, first, count, instanced); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglDrawElements(int mode, int count, int type, int offset) { + glDrawElements(mode, count, type, offset); + } + + public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) { + switch(instancingImpl) { + case INSTANCE_IMPL_CORE: + glDrawElementsInstanced(mode, count, type, offset, instanced); + break; + case INSTANCE_IMPL_ANGLE: + glDrawElementsInstancedANGLE(mode, count, type, offset, instanced); + break; + case INSTANCE_IMPL_EXT: + glDrawElementsInstancedEXT(mode, count, type, offset, instanced); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) { + int loc = glGetUniformLocation(((OpenGLObjects.ProgramGL) obj).ptr, name); + return loc < 0 ? null : new OpenGLObjects.UniformGL(loc); + } + + public static final int _wglGetUniformBlockIndex(IProgramGL obj, String name) { + return glGetUniformBlockIndex(((OpenGLObjects.ProgramGL) obj).ptr, name); + } + + public static final void _wglBindBufferRange(int target, int index, IBufferGL buffer, int offset, int size) { + glBindBufferRange(target, index, ((OpenGLObjects.BufferGL) buffer).ptr, offset, size); + } + + public static final void _wglUniformBlockBinding(IProgramGL obj, int blockIndex, int bufferIndex) { + glUniformBlockBinding(((OpenGLObjects.ProgramGL) obj).ptr, blockIndex, bufferIndex); + } + + public static final void _wglUniform1f(IUniformGL obj, float x) { + if (obj != null) + glUniform1f(((OpenGLObjects.UniformGL) obj).ptr, x); + } + + public static final void _wglUniform2f(IUniformGL obj, float x, float y) { + if (obj != null) + glUniform2f(((OpenGLObjects.UniformGL) obj).ptr, x, y); + } + + public static final void _wglUniform3f(IUniformGL obj, float x, float y, float z) { + if (obj != null) + glUniform3f(((OpenGLObjects.UniformGL) obj).ptr, x, y, z); + } + + public static final void _wglUniform4f(IUniformGL obj, float x, float y, float z, float w) { + if (obj != null) + glUniform4f(((OpenGLObjects.UniformGL) obj).ptr, x, y, z, w); + } + + public static final void _wglUniform1i(IUniformGL obj, int x) { + if (obj != null) + glUniform1i(((OpenGLObjects.UniformGL) obj).ptr, x); + } + + public static final void _wglUniform2i(IUniformGL obj, int x, int y) { + if (obj != null) + glUniform2i(((OpenGLObjects.UniformGL) obj).ptr, x, y); + } + + public static final void _wglUniform3i(IUniformGL obj, int x, int y, int z) { + if (obj != null) + glUniform3i(((OpenGLObjects.UniformGL) obj).ptr, x, y, z); + } + + public static final void _wglUniform4i(IUniformGL obj, int x, int y, int z, int w) { + if (obj != null) + glUniform4i(((OpenGLObjects.UniformGL) obj).ptr, x, y, z, w); + } + + public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 2, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix3fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 9, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix3x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix3x2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 6, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix4fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 4, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix4x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix4x2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 3, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix4x3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix4x3fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 12, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) { + if(framebuffer == null) { + glBindFramebuffer(target, 0); + }else { + glBindFramebuffer(target, ((OpenGLObjects.FramebufferGL) framebuffer).ptr); + } + } + + public static final int _wglCheckFramebufferStatus(int target) { + return glCheckFramebufferStatus(target); + } + + public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget, ITextureGL texture, + int level) { + glFramebufferTexture2D(target, attachment, texTarget, ((OpenGLObjects.TextureGL) texture).ptr, level); + } + + public static final void _wglFramebufferTextureLayer(int target, int attachment, ITextureGL texture, int level, int layer) { + glFramebufferTextureLayer(target, attachment, ((OpenGLObjects.TextureGL) texture).ptr, level, layer); + } + + public static final void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, + int dstX1, int dstY1, int bits, int filter) { + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter); + } + + public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) { + glBindRenderbuffer(target, renderbuffer == null ? 0 : ((OpenGLObjects.RenderbufferGL) renderbuffer).ptr); + } + + public static final void _wglRenderbufferStorage(int target, int internalformat, int width, int height) { + glRenderbufferStorage(target, internalformat, width, height); + } + + public static final void _wglFramebufferRenderbuffer(int target, int attachment, int renderbufferTarget, + IRenderbufferGL renderbuffer) { + glFramebufferRenderbuffer(target, attachment, renderbufferTarget, + ((OpenGLObjects.RenderbufferGL) renderbuffer).ptr); + } + + public static final String _wglGetString(int param) { + return glGetString(param); + } + + public static final int _wglGetInteger(int param) { + return glGetInteger(param); + } + + public static final int _wglGetError() { + return glGetError(); + } + + public static final int checkOpenGLESVersion() { + return glesVers; + } + + public static final boolean checkEXTGPUShader5Capable() { + return hasEXTGPUShader5; + } + + public static final boolean checkOESGPUShader5Capable() { + return hasOESGPUShader5; + } + + public static final boolean checkFBORenderMipmapCapable() { + return hasOESFBORenderMipmap; + } + + public static final boolean checkVAOCapable() { + return vertexArrayImpl != VAO_IMPL_NONE; + } + + public static final boolean checkInstancingCapable() { + return instancingImpl != INSTANCE_IMPL_NONE; + } + + public static final boolean checkTexStorageCapable() { + return texStorageImpl != TEX_STORAGE_IMPL_NONE; + } + + public static final boolean checkTextureLODCapable() { + return glesVers >= 300 || hasEXTShaderTextureLOD; + } + + public static final boolean checkNPOTCapable() { + return glesVers >= 300; + } + + public static final boolean checkHDRFramebufferSupport(int bits) { + switch(bits) { + case 16: + return hasFBO16FSupport; + case 32: + return hasFBO32FSupport; + default: + return false; + } + } + + public static final boolean checkLinearHDRFilteringSupport(int bits) { + switch(bits) { + case 16: + return hasLinearHDR16FSupport; + case 32: + return hasLinearHDR32FSupport; + default: + return false; + } + } + + // legacy + public static final boolean checkLinearHDR32FSupport() { + return hasLinearHDR32FSupport; + } + + public static final boolean checkAnisotropicFilteringSupport() { + return hasEXTTextureFilterAnisotropic; + } + + public static final String[] getAllExtensions() { + return glGetString(GL_EXTENSIONS).split(" "); + } + + public static final void enterVAOEmulationHook() { + + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index ad6ace9..ea4647a 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -1,573 +1,633 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import static org.lwjgl.egl.EGL10.*; -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.glfw.GLFWNativeEGL.*; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Random; -import java.util.function.Consumer; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import java.util.zip.InflaterInputStream; - -import javax.imageio.ImageIO; - -import org.lwjgl.PointerBuffer; -import org.lwjgl.egl.EGL; -import org.lwjgl.glfw.GLFWImage; -import org.lwjgl.glfw.GLFWVidMode; -import org.lwjgl.opengles.GLDebugMessageKHRCallback; -import org.lwjgl.opengles.GLDebugMessageKHRCallbackI; -import org.lwjgl.opengles.GLES; -import org.lwjgl.opengles.GLES30; -import org.lwjgl.opengles.KHRDebug; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.system.MemoryUtil; -import org.lwjgl.system.jemalloc.JEmalloc; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator; -import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformRuntime { - - static final Logger logger = LogManager.getLogger("RuntimeLWJGL3"); - - private static String glVersion = "unknown"; - private static String glRenderer = "unknown"; - - private static EnumPlatformANGLE rendererANGLEPlatform = null; - - private static long windowHandle = 0l; - - public static void create() { - logger.info("Starting Desktop Runtime..."); - PlatformFilesystem.initialize(); - EaglerFolderResourcePack.setSupported(true); - - if (requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT) { - logger.info("Setting ANGLE Platform: {}", requestedANGLEPlatform.name); - glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, requestedANGLEPlatform.eglEnum); - } - - glfwInit(); - logger.info("GLFW Version: {}", glfwGetVersionString()); - - glfwDefaultWindowHints(); - glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - - glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); - - PointerBuffer buf = glfwGetMonitors(); - GLFWVidMode mon = glfwGetVideoMode(buf.get(0)); - - int windowWidth = mon.width() - 200; - int windowHeight = mon.height() - 250; - - int winX = (mon.width() - windowWidth) / 2; - int winY = (mon.height() - windowHeight - 20) / 2; - - windowHandle = glfwCreateWindow(windowWidth, windowHeight, "Eaglercraft Desktop Runtime", 0l, 0l); - - glfwSetWindowPos(windowHandle, winX, winY); - - int[] x2 = new int[1]; - int[] y2 = new int[1]; - int[] w2 = new int[1]; - int[] h2 = new int[1]; - glfwGetWindowFrameSize(windowHandle, x2, y2, w2, h2); - glfwSetWindowSize(windowHandle, windowWidth - x2[0] - w2[0], windowHeight - y2[0] - h2[0]); - - ImageIO.setUseCache(false); - BufferedImage[] windowIcons = null; - - try { - windowIcons = new BufferedImage[] { - ImageIO.read(new File("icon16.png")), - ImageIO.read(new File("icon32.png")) - }; - } catch (IOException t) { - logger.error("Could not load default window icons!"); - logger.error(t); - } - - if (windowIcons != null) { - try (MemoryStack st = MemoryStack.stackPush()) { - GLFWImage.Buffer windowIconsBuffer = GLFWImage.malloc(windowIcons.length, st); - - for (int i = 0; i < windowIcons.length; ++i) { - int w = windowIcons[i].getWidth(); - int h = windowIcons[i].getHeight(); - - int[] px = new int[w * h]; - windowIcons[i].getRGB(0, 0, w, h, px, 0, w); - - for(int j = 0; j < px.length; ++j) { - px[j] = (px[j] & 0xFF00FF00) | ((px[j] >>> 16) & 0xFF) | ((px[j] & 0xFF) << 16); // swap R/B - } - - java.nio.ByteBuffer iconBuffer = st.malloc(w * h * 4); - iconBuffer.asIntBuffer().put(px); - iconBuffer.flip(); - - windowIconsBuffer.position(i); - windowIconsBuffer.width(w); - windowIconsBuffer.height(h); - windowIconsBuffer.pixels(iconBuffer); - } - - glfwSetWindowIcon(windowHandle, windowIconsBuffer); - } - } - - long glfw_eglHandle = glfwGetEGLDisplay(); - logger.info("EGL Version: {}", eglQueryString(glfw_eglHandle, EGL_VERSION)); - - int[] major = new int[] { 1 }; - int[] minor = new int[] { 4 }; - if (!eglInitialize(glfw_eglHandle, major, minor)) { - throw new RuntimeException("Could not initialize EGL"); - } - - EGL.createDisplayCapabilities(glfw_eglHandle, major[0], minor[0]); - glfwMakeContextCurrent(windowHandle); - PlatformOpenGL.setCurrentContext(GLES.createCapabilities()); - - logger.info("OpenGL Version: {}", (glVersion = GLES30.glGetString(GLES30.GL_VERSION))); - logger.info("OpenGL Renderer: {}", (glRenderer = GLES30.glGetString(GLES30.GL_RENDERER))); - - rendererANGLEPlatform = EnumPlatformANGLE.fromGLRendererString(glRenderer); - - if (requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT - && rendererANGLEPlatform != requestedANGLEPlatform) { - logger.warn("Incorrect ANGLE Platform: {}", rendererANGLEPlatform.name); - } - - if (requestedANGLEPlatform == EnumPlatformANGLE.DEFAULT) { - logger.info("ANGLE Platform: {}", rendererANGLEPlatform.name); - } - - glfwSwapInterval(0); - - KHRDebug.glDebugMessageCallbackKHR(new GLDebugMessageKHRCallbackI() { - @Override - public void invoke(int source, int type, int id, int severity, int length, long message, long userParam) { - StringBuilder b = new StringBuilder(); - b.append("[KHR DEBUG #"); - b.append(id); - b.append("] "); - - switch (source) { - case KHRDebug.GL_DEBUG_SOURCE_API_KHR: - b.append("[API - "); - break; - case KHRDebug.GL_DEBUG_SOURCE_APPLICATION_KHR: - b.append("[APPLICATION - "); - break; - case KHRDebug.GL_DEBUG_SOURCE_SHADER_COMPILER_KHR: - b.append("[SHADER COMPILER - "); - break; - case KHRDebug.GL_DEBUG_SOURCE_THIRD_PARTY_KHR: - b.append("[THIRD PARTY - "); - break; - case KHRDebug.GL_DEBUG_SOURCE_OTHER_KHR: - default: - b.append("[OTHER - "); - break; - } - - switch (type) { - case KHRDebug.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR: - b.append("DEPRECATED BEHAVIOR] "); - break; - case KHRDebug.GL_DEBUG_TYPE_ERROR_KHR: - b.append("ERROR] "); - break; - default: - case KHRDebug.GL_DEBUG_TYPE_OTHER_KHR: - b.append("OTHER] "); - break; - case KHRDebug.GL_DEBUG_TYPE_PERFORMANCE_KHR: - b.append("PERFORMANCE] "); - break; - case KHRDebug.GL_DEBUG_TYPE_PORTABILITY_KHR: - b.append("PORTABILITY] "); - break; - case KHRDebug.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: - b.append("UNDEFINED BEHAVIOR] "); - break; - } - - switch (severity) { - default: - case KHRDebug.GL_DEBUG_SEVERITY_LOW_KHR: - b.append("[LOW Severity] "); - break; - case KHRDebug.GL_DEBUG_SEVERITY_MEDIUM_KHR: - b.append("[MEDIUM Severity] "); - break; - case KHRDebug.GL_DEBUG_SEVERITY_HIGH_KHR: - b.append("[SEVERE] "); - break; - } - - String message2 = GLDebugMessageKHRCallback.getMessage(length, message); - if (message2.contains("GPU stall due to ReadPixels")) - return; - b.append(message2); - logger.error(b.toString()); - - StackTraceElement[] ex = new RuntimeException().getStackTrace(); - for (int i = 0; i < ex.length; ++i) { - logger.error(" at {}", ex[i]); - } - } - }, 0l); - - GLES30.glEnable(KHRDebug.GL_DEBUG_OUTPUT_KHR); - GLES30.glEnable(KHRDebug.GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); - - logger.info("Initializing Audio..."); - PlatformAudio.platformInitialize(); - - logger.info("Initializing Hooks..."); - PlatformInput.initHooks(windowHandle); - PlatformApplication.initHooks(windowHandle); - } - - public static void destroy() { - PlatformAudio.platformShutdown(); - PlatformFilesystem.platformShutdown(); - GLES.destroy(); - EGL.destroy(); - glfwDestroyWindow(windowHandle); - glfwTerminate(); - } - - public static EnumPlatformType getPlatformType() { - return EnumPlatformType.DESKTOP; - } - - public static EnumPlatformAgent getPlatformAgent() { - return EnumPlatformAgent.DESKTOP; - } - - public static String getUserAgentString() { - return "Desktop/" + System.getProperty("os.name"); - } - - private static EnumPlatformOS currentPlatformOS = null; - - public static EnumPlatformOS getPlatformOS() { - if (currentPlatformOS == null) { - currentPlatformOS = EnumPlatformOS.getFromJVM(System.getProperty("os.name")); - } - return currentPlatformOS; - } - - private static EnumPlatformANGLE requestedANGLEPlatform = EnumPlatformANGLE.DEFAULT; - - public static void requestANGLE(EnumPlatformANGLE plaf) { - requestedANGLEPlatform = plaf; - } - - public static EnumPlatformANGLE getPlatformANGLE() { - return rendererANGLEPlatform; - } - - public static String getGLVersion() { - return glVersion; - } - - public static String getGLRenderer() { - return glRenderer; - } - - public static ByteBuffer allocateByteBuffer(int length) { - return EaglerLWJGLAllocator.allocByteBuffer(length); - } - - public static IntBuffer allocateIntBuffer(int length) { - return EaglerLWJGLAllocator.allocIntBuffer(length); - } - - public static FloatBuffer allocateFloatBuffer(int length) { - return EaglerLWJGLAllocator.allocFloatBuffer(length); - } - - public static ByteBuffer castPrimitiveByteArray(byte[] array) { - return null; - } - - public static IntBuffer castPrimitiveIntArray(int[] array) { - return null; - } - - public static FloatBuffer castPrimitiveFloatArray(float[] array) { - return null; - } - - public static byte[] castNativeByteBuffer(ByteBuffer buffer) { - return null; - } - - public static int[] castNativeIntBuffer(IntBuffer buffer) { - return null; - } - - public static float[] castNativeFloatBuffer(FloatBuffer buffer) { - return null; - } - - public static void freeByteBuffer(ByteBuffer byteBuffer) { - EaglerLWJGLAllocator.freeByteBuffer(byteBuffer); - } - - public static void freeIntBuffer(IntBuffer intBuffer) { - EaglerLWJGLAllocator.freeIntBuffer(intBuffer); - } - - public static void freeFloatBuffer(FloatBuffer floatBuffer) { - EaglerLWJGLAllocator.freeFloatBuffer(floatBuffer); - } - - public static class NativeNIO { - - public static java.nio.ByteBuffer allocateByteBuffer(int length) { - long ret = JEmalloc.nje_malloc(length); - if (ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - return MemoryUtil.memByteBuffer(ret, length); - } - - public static java.nio.IntBuffer allocateIntBuffer(int length) { - long ret = JEmalloc.nje_malloc(length << 2); - if (ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - return MemoryUtil.memIntBuffer(ret, length); - } - - public static java.nio.FloatBuffer allocateFloatBuffer(int length) { - long ret = JEmalloc.nje_malloc(length << 2); - if (ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - return MemoryUtil.memFloatBuffer(ret, length); - } - - public static java.nio.IntBuffer getIntBuffer(java.nio.ByteBuffer byteBuffer) { - return MemoryUtil.memIntBuffer(MemoryUtil.memAddress(byteBuffer), byteBuffer.capacity() >> 2); - } - - public static java.nio.FloatBuffer getFloatBuffer(java.nio.ByteBuffer byteBuffer) { - return MemoryUtil.memFloatBuffer(MemoryUtil.memAddress(byteBuffer), byteBuffer.capacity() >> 2); - } - - public static java.nio.ByteBuffer getAsByteBuffer(java.nio.IntBuffer intBuffer) { - return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(intBuffer), intBuffer.capacity() << 2); - } - - public static java.nio.ByteBuffer getAsByteBuffer(java.nio.FloatBuffer floatBuffer) { - return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(floatBuffer), floatBuffer.capacity() << 2); - } - - public static void freeByteBuffer(java.nio.ByteBuffer byteBuffer) { - JEmalloc.nje_free(MemoryUtil.memAddress(byteBuffer)); - } - - public static void freeIntBuffer(java.nio.IntBuffer intBuffer) { - JEmalloc.nje_free(MemoryUtil.memAddress(intBuffer)); - } - - public static void freeFloatBuffer(java.nio.FloatBuffer floatBuffer) { - JEmalloc.nje_free(MemoryUtil.memAddress(floatBuffer)); - } - - } - - public static boolean isDebugRuntime() { - return true; - } - - public static void writeCrashReport(String crashDump) { - File file1 = new File("./crash-reports"); - if (!file1.exists()) { - if (!file1.mkdirs()) { - PlatformRuntime.logger.fatal("Could not create crash report directory: {}", file1.getAbsolutePath()); - return; - } - } - File file2 = new File(file1, - "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt"); - try (FileOutputStream os = new FileOutputStream(file2)) { - os.write(crashDump.getBytes(StandardCharsets.UTF_8)); - } catch (IOException ex) { - PlatformRuntime.logger.fatal("Could not write crash report: {}", file2.getAbsolutePath()); - PlatformRuntime.logger.fatal(ex); - return; - } - PlatformRuntime.logger.fatal("Crash report was written to: {}", file2.getAbsolutePath()); - } - - public static void getStackTrace(Throwable t, Consumer ret) { - StackTraceElement[] stackTrace = t.getStackTrace(); - for (int i = 0; i < stackTrace.length; ++i) { - ret.accept(stackTrace[i].toString()); - } - } - - public static boolean printJSExceptionIfBrowser(Throwable t) { - return false; - } - - public static void exit() { - System.exit(0); - } - - public static void setThreadName(String string) { - Thread.currentThread().setName(string); - } - - public static long maxMemory() { - return Runtime.getRuntime().maxMemory(); - } - - public static long totalMemory() { - return Runtime.getRuntime().totalMemory(); - } - - public static long freeMemory() { - return Runtime.getRuntime().freeMemory(); - } - - public static String getCallingClass(int backTrace) { - StackTraceElement[] astacktraceelement = Thread.currentThread().getStackTrace(); - StackTraceElement stacktraceelement = astacktraceelement[Math.min(backTrace + 1, astacktraceelement.length)]; - return "" + stacktraceelement.getFileName() + ":" + stacktraceelement.getLineNumber(); - } - - public static OutputStream newDeflaterOutputStream(OutputStream os) throws IOException { - return new DeflaterOutputStream(os); - } - - public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { - return new GZIPOutputStream(os); - } - - public static InputStream newInflaterInputStream(InputStream is) throws IOException { - return new InflaterInputStream(is); - } - - public static InputStream newGZIPInputStream(InputStream is) throws IOException { - return new GZIPInputStream(is); - } - - public static void downloadRemoteURIByteArray(String assetPackageURI, final Consumer cb) { - logger.info("Downloading: {}"); - try (InputStream is = (new URL(assetPackageURI)).openStream()) { - EaglerOutputStream bao = new EaglerOutputStream(); - byte[] copyBuffer = new byte[16384]; - int i; - while ((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { - bao.write(copyBuffer, 0, i); - } - cb.accept(bao.toByteArray()); - } catch (IOException ex) { - logger.error("Failed to download file!"); - logger.error(ex); - } - } - - public static boolean requireSSL() { - return false; - } - - public static boolean isOfflineDownloadURL() { - return false; - } - - public static IClientConfigAdapter getClientConfigAdapter() { - return DesktopClientConfigAdapter.instance; - } - - public static String getRecText() { - return "recording.unsupported"; - } - - public static boolean recSupported() { - return false; - } - - public static void toggleRec() { - // - } - - private static final Random seedProvider = new Random(); - - public static long randomSeed() { - synchronized (seedProvider) { - return seedProvider.nextLong(); - } - } - - public static void getWindowXY(int[] x, int[] y) { - glfwGetWindowPos(windowHandle, x, y); - } - - public static String currentThreadName() { - return Thread.currentThread().getName(); - } - - public static long getWindowHandle() { - return windowHandle; - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import static org.lwjgl.egl.EGL10.*; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFWNativeEGL.*; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterInputStream; + +import javax.imageio.ImageIO; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.egl.EGL; +import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.opengles.GLDebugMessageKHRCallback; +import org.lwjgl.opengles.GLDebugMessageKHRCallbackI; +import org.lwjgl.opengles.GLES; +import org.lwjgl.opengles.GLES30; +import org.lwjgl.opengles.GLESCapabilities; +import org.lwjgl.opengles.KHRDebug; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.jemalloc.JEmalloc; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator; +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.Filesystem; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; +import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformRuntime { + + static final Logger logger = LogManager.getLogger("RuntimeLWJGL3"); + + private static String glVersion = "unknown"; + private static String glRenderer = "unknown"; + + private static EnumPlatformANGLE rendererANGLEPlatform = null; + + private static long windowHandle = 0l; + + public static void create() { + logger.info("Starting Desktop Runtime..."); + + ByteBuffer endiannessTestBytes = allocateByteBuffer(4); + try { + endiannessTestBytes.asIntBuffer().put(0x6969420); + if (((endiannessTestBytes.get(0) & 0xFF) | ((endiannessTestBytes.get(1) & 0xFF) << 8) + | ((endiannessTestBytes.get(2) & 0xFF) << 16) | ((endiannessTestBytes.get(3) & 0xFF) << 24)) != 0x6969420) { + throw new UnsupportedOperationException("Big endian CPU detected! (somehow)"); + }else { + logger.info("Endianness: this CPU is little endian"); + } + }finally { + freeByteBuffer(endiannessTestBytes); + } + + IEaglerFilesystem resourcePackFilesystem = Filesystem.getHandleFor(getClientConfigAdapter().getResourcePacksDB()); + VFile2.setPrimaryFilesystem(resourcePackFilesystem); + EaglerFolderResourcePack.setSupported(true); + + if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT) { + logger.info("Setting ANGLE Platform: {}", requestedANGLEPlatform.name); + glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, requestedANGLEPlatform.eglEnum); + } + + glfwInit(); + logger.info("GLFW Version: {}", glfwGetVersionString()); + + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + + glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + + PointerBuffer buf = glfwGetMonitors(); + GLFWVidMode mon = glfwGetVideoMode(buf.get(0)); + + int windowWidth = mon.width() - 200; + int windowHeight = mon.height() - 250; + String title = "Eaglercraft Desktop Runtime"; + + int winX = (mon.width() - windowWidth) / 2; + int winY = (mon.height() - windowHeight - 20) / 2; + + int myGLVersion = -1; + if(requestedGLVersion >= 310 && windowHandle == 0) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + windowHandle = glfwCreateWindow(windowWidth, windowHeight, title, 0l, 0l); + if(windowHandle == 0l) { + logger.error("Failed to create OpenGL ES 3.1 context!"); + }else { + myGLVersion = 310; + } + } + if(requestedGLVersion >= 300 && windowHandle == 0) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + windowHandle = glfwCreateWindow(windowWidth, windowHeight, title, 0l, 0l); + if(windowHandle == 0l) { + logger.error("Failed to create OpenGL ES 3.0 context!"); + }else { + myGLVersion = 300; + } + } + if(requestedGLVersion >= 200 && windowHandle == 0) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + windowHandle = glfwCreateWindow(windowWidth, windowHeight, title, 0l, 0l); + if(windowHandle == 0l) { + logger.error("Failed to create OpenGL ES 2.0 context!"); + }else { + myGLVersion = 200; + } + } + if(myGLVersion == -1) { + throw new RuntimeException("Could not create a supported OpenGL ES context!"); + } + + glfwSetWindowPos(windowHandle, winX, winY); + + int[] x2 = new int[1]; + int[] y2 = new int[1]; + int[] w2 = new int[1]; + int[] h2 = new int[1]; + glfwGetWindowFrameSize(windowHandle, x2, y2, w2, h2); + glfwSetWindowSize(windowHandle, windowWidth - x2[0] - w2[0], windowHeight - y2[0] - h2[0]); + + ImageIO.setUseCache(false); + BufferedImage[] windowIcons = null; + + try { + windowIcons = new BufferedImage[] { + ImageIO.read(new File("icon16.png")), + ImageIO.read(new File("icon32.png")) + }; + }catch(IOException t) { + logger.error("Could not load default window icons!"); + logger.error(t); + } + + if(windowIcons != null) { + try(MemoryStack st = MemoryStack.stackPush()) { + GLFWImage.Buffer windowIconsBuffer = GLFWImage.malloc(windowIcons.length, st); + + for(int i = 0; i < windowIcons.length; ++i) { + int w = windowIcons[i].getWidth(); + int h = windowIcons[i].getHeight(); + + int[] px = new int[w * h]; + windowIcons[i].getRGB(0, 0, w, h, px, 0, w); + + for(int j = 0; j < px.length; ++j) { + px[j] = (px[j] & 0xFF00FF00) | ((px[j] >>> 16) & 0xFF) | ((px[j] & 0xFF) << 16); // swap R/B + } + + java.nio.ByteBuffer iconBuffer = st.malloc(w * h * 4); + iconBuffer.asIntBuffer().put(px); + iconBuffer.flip(); + + windowIconsBuffer.position(i); + windowIconsBuffer.width(w); + windowIconsBuffer.height(h); + windowIconsBuffer.pixels(iconBuffer); + } + + glfwSetWindowIcon(windowHandle, windowIconsBuffer); + } + } + + long glfw_eglHandle = glfwGetEGLDisplay(); + logger.info("EGL Version: {}", eglQueryString(glfw_eglHandle, EGL_VERSION)); + + int[] major = new int[] { 1 }; + int[] minor = new int[] { 4 }; + if(!eglInitialize(glfw_eglHandle, major, minor)) { + throw new RuntimeException("Could not initialize EGL"); + } + + EGL.createDisplayCapabilities(glfw_eglHandle, major[0], minor[0]); + glfwMakeContextCurrent(windowHandle); + GLESCapabilities caps = GLES.createCapabilities(); + PlatformOpenGL.setCurrentContext(myGLVersion, caps); + + logger.info("OpenGL Version: {}", (glVersion = GLES30.glGetString(GLES30.GL_VERSION))); + logger.info("OpenGL Renderer: {}", (glRenderer = GLES30.glGetString(GLES30.GL_RENDERER))); + rendererANGLEPlatform = EnumPlatformANGLE.fromGLRendererString(glRenderer); + + int realGLVersion = (glVersion != null && probablyGLES2(glVersion)) ? 200 + : (GLES30.glGetInteger(GLES30.GL_MAJOR_VERSION) * 100 + + GLES30.glGetInteger(GLES30.GL_MINOR_VERSION) * 10); + if(realGLVersion != myGLVersion) { + logger.warn("Unexpected GLES verison resolved for requested {} context: {}", myGLVersion, realGLVersion); + if(myGLVersion == 200) { + logger.warn("Note: try adding the \"d3d9\" option if you are on windows trying to get GLES 2.0"); + } + if(realGLVersion != 320 && realGLVersion != 310 && realGLVersion != 300 && realGLVersion != 200) { + throw new RuntimeException("Unsupported OpenGL ES version detected: " + realGLVersion); + } + myGLVersion = realGLVersion; + PlatformOpenGL.setCurrentContext(myGLVersion, caps); + } + + if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT + && rendererANGLEPlatform != requestedANGLEPlatform) { + logger.warn("Incorrect ANGLE Platform: {}", rendererANGLEPlatform.name); + } + + if(requestedANGLEPlatform == EnumPlatformANGLE.DEFAULT) { + logger.info("ANGLE Platform: {}", rendererANGLEPlatform.name); + } + + List exts = PlatformOpenGL.dumpActiveExtensions(); + if(exts.isEmpty()) { + logger.info("Unlocked the following OpenGL ES extensions: (NONE)"); + }else { + Collections.sort(exts); + logger.info("Unlocked the following OpenGL ES extensions:"); + for(int i = 0, l = exts.size(); i < l; ++i) { + logger.info(" - " + exts.get(i)); + } + } + + + glfwSwapInterval(0); + + KHRDebug.glDebugMessageCallbackKHR(new GLDebugMessageKHRCallbackI() { + @Override + public void invoke(int source, int type, int id, int severity, int length, long message, long userParam) { + StringBuilder b = new StringBuilder(); + b.append("[KHR DEBUG #"); b.append(id); b.append("] "); + + switch(source) { + case KHRDebug.GL_DEBUG_SOURCE_API_KHR: b.append("[API - "); break; + case KHRDebug.GL_DEBUG_SOURCE_APPLICATION_KHR: b.append("[APPLICATION - "); break; + case KHRDebug.GL_DEBUG_SOURCE_SHADER_COMPILER_KHR: b.append("[SHADER COMPILER - "); break; + case KHRDebug.GL_DEBUG_SOURCE_THIRD_PARTY_KHR: b.append("[THIRD PARTY - "); break; + case KHRDebug.GL_DEBUG_SOURCE_OTHER_KHR: default: b.append("[OTHER - "); break; + } + + switch(type) { + case KHRDebug.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR: b.append("DEPRECATED BEHAVIOR] "); break; + case KHRDebug.GL_DEBUG_TYPE_ERROR_KHR: b.append("ERROR] "); break; + default: + case KHRDebug.GL_DEBUG_TYPE_OTHER_KHR: b.append("OTHER] "); break; + case KHRDebug.GL_DEBUG_TYPE_PERFORMANCE_KHR: b.append("PERFORMANCE] "); break; + case KHRDebug.GL_DEBUG_TYPE_PORTABILITY_KHR: b.append("PORTABILITY] "); break; + case KHRDebug.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: b.append("UNDEFINED BEHAVIOR] "); break; + } + + switch(severity) { + default: + case KHRDebug.GL_DEBUG_SEVERITY_LOW_KHR: b.append("[LOW Severity] "); break; + case KHRDebug.GL_DEBUG_SEVERITY_MEDIUM_KHR: b.append("[MEDIUM Severity] "); break; + case KHRDebug.GL_DEBUG_SEVERITY_HIGH_KHR: b.append("[SEVERE] "); break; + } + + String message2 = GLDebugMessageKHRCallback.getMessage(length, message); + if(message2.contains("GPU stall due to ReadPixels")) return; + b.append(message2); + logger.error(b.toString()); + + StackTraceElement[] ex = new RuntimeException().getStackTrace(); + for(int i = 0; i < ex.length; ++i) { + logger.error(" at {}", ex[i]); + } + } + }, 0l); + + GLES30.glEnable(KHRDebug.GL_DEBUG_OUTPUT_KHR); + GLES30.glEnable(KHRDebug.GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); + + logger.info("Initializing Audio..."); + PlatformAudio.platformInitialize(); + + logger.info("Initializing Hooks..."); + PlatformInput.initHooks(windowHandle); + PlatformApplication.initHooks(windowHandle); + } + + public static void destroy() { + PlatformAudio.platformShutdown(); + Filesystem.closeAllHandles(); + ServerPlatformSingleplayer.platformShutdown(); + GLES.destroy(); + EGL.destroy(); + glfwDestroyWindow(windowHandle); + glfwTerminate(); + } + + private static boolean probablyGLES2(String glVersion) { + if(glVersion == null) return false; + glVersion = glVersion.toLowerCase(); + return glVersion.contains("opengl es 2.0") || glVersion.contains("ES 2.0"); + } + + public static EnumPlatformType getPlatformType() { + return EnumPlatformType.DESKTOP; + } + + public static EnumPlatformAgent getPlatformAgent() { + return EnumPlatformAgent.DESKTOP; + } + + public static String getUserAgentString() { + return "Desktop/" + System.getProperty("os.name"); + } + + private static EnumPlatformOS currentPlatformOS = null; + + public static EnumPlatformOS getPlatformOS() { + if(currentPlatformOS == null) { + currentPlatformOS = EnumPlatformOS.getFromJVM(System.getProperty("os.name")); + } + return currentPlatformOS; + } + + private static EnumPlatformANGLE requestedANGLEPlatform = EnumPlatformANGLE.DEFAULT; + private static int requestedGLVersion = 300; + + public static void requestANGLE(EnumPlatformANGLE plaf) { + requestedANGLEPlatform = plaf; + } + + public static void requestGL(int i) { + requestedGLVersion = i; + } + + public static EnumPlatformANGLE getPlatformANGLE() { + return rendererANGLEPlatform; + } + + public static String getGLVersion() { + return glVersion; + } + + public static String getGLRenderer() { + return glRenderer; + } + public static ByteBuffer allocateByteBuffer(int length) { + return EaglerLWJGLAllocator.allocByteBuffer(length); + } + + public static IntBuffer allocateIntBuffer(int length) { + return EaglerLWJGLAllocator.allocIntBuffer(length); + } + + public static FloatBuffer allocateFloatBuffer(int length) { + return EaglerLWJGLAllocator.allocFloatBuffer(length); + } + + public static ByteBuffer castPrimitiveByteArray(byte[] array) { + return null; + } + + public static IntBuffer castPrimitiveIntArray(int[] array) { + return null; + } + + public static FloatBuffer castPrimitiveFloatArray(float[] array) { + return null; + } + + public static byte[] castNativeByteBuffer(ByteBuffer buffer) { + return null; + } + + public static int[] castNativeIntBuffer(IntBuffer buffer) { + return null; + } + + public static float[] castNativeFloatBuffer(FloatBuffer buffer) { + return null; + } + + public static void freeByteBuffer(ByteBuffer byteBuffer) { + EaglerLWJGLAllocator.freeByteBuffer(byteBuffer); + } + + public static void freeIntBuffer(IntBuffer intBuffer) { + EaglerLWJGLAllocator.freeIntBuffer(intBuffer); + } + + public static void freeFloatBuffer(FloatBuffer floatBuffer) { + EaglerLWJGLAllocator.freeFloatBuffer(floatBuffer); + } + + public static class NativeNIO { + + public static java.nio.ByteBuffer allocateByteBuffer(int length) { + long ret = JEmalloc.nje_malloc(length); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return MemoryUtil.memByteBuffer(ret, length); + } + + public static java.nio.IntBuffer allocateIntBuffer(int length) { + long ret = JEmalloc.nje_malloc(length << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return MemoryUtil.memIntBuffer(ret, length); + } + + public static java.nio.FloatBuffer allocateFloatBuffer(int length) { + long ret = JEmalloc.nje_malloc(length << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return MemoryUtil.memFloatBuffer(ret, length); + } + + public static java.nio.IntBuffer getIntBuffer(java.nio.ByteBuffer byteBuffer) { + return MemoryUtil.memIntBuffer(MemoryUtil.memAddress(byteBuffer), byteBuffer.capacity() >> 2); + } + + public static java.nio.FloatBuffer getFloatBuffer(java.nio.ByteBuffer byteBuffer) { + return MemoryUtil.memFloatBuffer(MemoryUtil.memAddress(byteBuffer), byteBuffer.capacity() >> 2); + } + + public static java.nio.ByteBuffer getAsByteBuffer(java.nio.IntBuffer intBuffer) { + return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(intBuffer), intBuffer.capacity() << 2); + } + + public static java.nio.ByteBuffer getAsByteBuffer(java.nio.FloatBuffer floatBuffer) { + return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(floatBuffer), floatBuffer.capacity() << 2); + } + + public static void freeByteBuffer(java.nio.ByteBuffer byteBuffer) { + JEmalloc.nje_free(MemoryUtil.memAddress(byteBuffer)); + } + + public static void freeIntBuffer(java.nio.IntBuffer intBuffer) { + JEmalloc.nje_free(MemoryUtil.memAddress(intBuffer)); + } + + public static void freeFloatBuffer(java.nio.FloatBuffer floatBuffer) { + JEmalloc.nje_free(MemoryUtil.memAddress(floatBuffer)); + } + + } + + public static boolean isDebugRuntime() { + return true; + } + + public static void writeCrashReport(String crashDump) { + File file1 = new File("./crash-reports"); + if(!file1.exists()) { + if(!file1.mkdirs()) { + PlatformRuntime.logger.fatal("Could not create crash report directory: {}", file1.getAbsolutePath()); + return; + } + } + File file2 = new File(file1, + "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt"); + try(FileOutputStream os = new FileOutputStream(file2)) { + os.write(crashDump.getBytes(StandardCharsets.UTF_8)); + }catch(IOException ex) { + PlatformRuntime.logger.fatal("Could not write crash report: {}", file2.getAbsolutePath()); + PlatformRuntime.logger.fatal(ex); + return; + } + PlatformRuntime.logger.fatal("Crash report was written to: {}", file2.getAbsolutePath()); + } + + public static void getStackTrace(Throwable t, Consumer ret) { + StackTraceElement[] stackTrace = t.getStackTrace(); + for(int i = 0; i < stackTrace.length; ++i) { + ret.accept(stackTrace[i].toString()); + } + } + + public static boolean printJSExceptionIfBrowser(Throwable t) { + return false; + } + + public static void exit() { + System.exit(0); + } + + public static void setThreadName(String string) { + Thread.currentThread().setName(string); + } + + public static long maxMemory() { + return Runtime.getRuntime().maxMemory(); + } + + public static long totalMemory() { + return Runtime.getRuntime().totalMemory(); + } + + public static long freeMemory() { + return Runtime.getRuntime().freeMemory(); + } + + public static String getCallingClass(int backTrace) { + StackTraceElement[] astacktraceelement = Thread.currentThread().getStackTrace(); + StackTraceElement stacktraceelement = astacktraceelement[Math.min(backTrace + 1, astacktraceelement.length)]; + return "" + stacktraceelement.getFileName() + ":" + stacktraceelement.getLineNumber(); + } + + public static OutputStream newDeflaterOutputStream(OutputStream os) throws IOException { + return new DeflaterOutputStream(os); + } + + public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { + return new GZIPOutputStream(os); + } + + public static InputStream newInflaterInputStream(InputStream is) throws IOException { + return new InflaterInputStream(is); + } + + public static InputStream newGZIPInputStream(InputStream is) throws IOException { + return new GZIPInputStream(is); + } + + public static void downloadRemoteURIByteArray(String assetPackageURI, final Consumer cb) { + logger.info("Downloading: {}"); + try(InputStream is = (new URL(assetPackageURI)).openStream()) { + EaglerOutputStream bao = new EaglerOutputStream(); + byte[] copyBuffer = new byte[16384]; + int i; + while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { + bao.write(copyBuffer, 0, i); + } + cb.accept(bao.toByteArray()); + }catch(IOException ex) { + logger.error("Failed to download file!"); + logger.error(ex); + } + } + + public static boolean requireSSL() { + return false; + } + + public static boolean isOfflineDownloadURL() { + return false; + } + + public static IClientConfigAdapter getClientConfigAdapter() { + return DesktopClientConfigAdapter.instance; + } + + private static final Random seedProvider = new Random(); + + public static long randomSeed() { + synchronized(seedProvider) { + return seedProvider.nextLong(); + } + } + + public static void getWindowXY(int[] x, int[] y) { + glfwGetWindowPos(windowHandle, x, y); + } + + public static String currentThreadName() { + return Thread.currentThread().getName(); + } + + public static long getWindowHandle() { + return windowHandle; + } + + public static long steadyTimeMillis() { + return System.nanoTime() / 1000000l; + } + + public static long nanoTime() { + return System.nanoTime(); + } + + public static void postCreate() { + + } + + public static void setDisplayBootMenuNextRefresh(boolean en) { + + } + + public static void immediateContinue() { + // nope + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformScreenRecord.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformScreenRecord.java new file mode 100755 index 0000000..eb1f80c --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformScreenRecord.java @@ -0,0 +1,59 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformScreenRecord { + + public static boolean isSupported() { + return false; + } + + public static boolean isCodecSupported(EnumScreenRecordingCodec codec) { + return false; + } + + public static void setGameVolume(float volume) { + + } + + public static void setMicrophoneVolume(float volume) { + + } + + public static void startRecording(ScreenRecordParameters params) { + + } + + public static void endRecording() { + + } + + public static boolean isRecording() { + return false; + } + + public static boolean isMicVolumeLocked() { + return false; + } + + public static boolean isVSyncLocked() { + return false; + } + + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java index cd3cc71..c82caaf 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java @@ -1,56 +1,66 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; -import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformUpdateSvc { - - private static final UpdateProgressStruct dummyStruct = new UpdateProgressStruct(); - - public static boolean supported() { - return false; - } - - public static void initialize() { - - } - - public static byte[] getClientSignatureData() { - return null; - } - - public static byte[] getClientBundleData() { - return null; - } - - public static void startClientUpdateFrom(UpdateCertificate clientUpdate) { - - } - - public static UpdateProgressStruct getUpdatingStatus() { - return dummyStruct; - } - - public static void quine(String filename, byte[] cert, byte[] data, String date) { - - } - - public static void quine(UpdateCertificate clientUpdate, byte[] data) { - - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; +import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct; +import net.lax1dude.eaglercraft.v1_8.update.UpdateResultObj; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformUpdateSvc { + + private static final UpdateProgressStruct dummyStruct = new UpdateProgressStruct(); + + public static boolean supported() { + return false; + } + + public static void initialize() { + + } + + public static byte[] getClientSignatureData() { + return null; + } + + public static byte[] getClientBundleData() { + return null; + } + + public static void startClientUpdateFrom(UpdateCertificate clientUpdate) { + + } + + public static UpdateProgressStruct getUpdatingStatus() { + return dummyStruct; + } + + public static UpdateResultObj getUpdateResult() { + return null; + } + + public static void installSignedClient(UpdateCertificate clientCert, byte[] clientPayload, boolean setDefault, + boolean setTimeout) { + + } + + public static void quine(String filename, byte[] cert, byte[] data, String date) { + + } + + public static void quine(UpdateCertificate clientUpdate, byte[] data) { + + } +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java index 9b7eef7..cfea7c3 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java @@ -4,20 +4,22 @@ import dev.onvoid.webrtc.*; import dev.onvoid.webrtc.internal.NativeLoader; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANPeerEvent; -import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayLoggerImpl; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQueryImpl; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQueryRateLimitDummy; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerRateLimitTracker; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocketImpl; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocketRateLimitDummy; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQuery; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.*; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQueryImpl; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQueryRateLimitDummy; import org.apache.commons.lang3.SystemUtils; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; import org.json.JSONArray; import org.json.JSONObject; import org.json.JSONWriter; @@ -25,10 +27,7 @@ import org.json.JSONWriter; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; -import java.io.DataInputStream; -import java.io.IOException; import java.lang.reflect.Field; -import java.net.URI; import java.nio.ByteBuffer; import java.nio.file.Paths; import java.util.*; @@ -52,10 +51,25 @@ public class PlatformWebRTC { private static final Logger logger = LogManager.getLogger("PlatformWebRTC"); + private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket")); + private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); private static final Object lock3 = new Object(); - private static final Object lock4 = new Object(); + + private static final List scheduledRunnables = new LinkedList<>(); + + private static class ScheduledRunnable { + + private final long runAt; + private final Runnable runnable; + + private ScheduledRunnable(long runAt, Runnable runnable) { + this.runAt = runAt; + this.runnable = runnable; + } + + } public static PeerConnectionFactory pcFactory; @@ -72,7 +86,8 @@ public class PlatformWebRTC { Runtime.getRuntime().addShutdownHook(new Thread(() -> pcFactory.dispose())); supported = true; } catch (Exception e) { - EagRuntime.debugPrintStackTrace(e); + logger.error("Failed to load WebRTC native library!"); + logger.error(e); supported = false; } } @@ -81,6 +96,46 @@ public class PlatformWebRTC { private static final Map fuckTeaVM = new HashMap<>(); + private static final Comparator sortTasks = (r1, r2) -> { + return (int)(r1.runAt - r2.runAt); + }; + + public static void runScheduledTasks() { + List toRun = null; + synchronized(scheduledRunnables) { + if(scheduledRunnables.isEmpty()) return; + long millis = PlatformRuntime.steadyTimeMillis(); + Iterator itr = scheduledRunnables.iterator(); + while(itr.hasNext()) { + ScheduledRunnable r = itr.next(); + if(r.runAt < millis) { + itr.remove(); + if(toRun == null) { + toRun = new ArrayList<>(1); + } + toRun.add(r); + } + } + } + if(toRun != null) { + Collections.sort(toRun, sortTasks); + for(int i = 0, l = toRun.size(); i < l; ++i) { + try { + toRun.get(i).runnable.run(); + }catch(Throwable t) { + logger.error("Caught exception running scheduled WebRTC task!"); + logger.error(t); + } + } + } + } + + static void scheduleTask(long runAfter, Runnable runnable) { + synchronized(scheduledRunnables) { + scheduledRunnables.add(new ScheduledRunnable(PlatformRuntime.steadyTimeMillis() + runAfter, runnable)); + } + } + public static class LANClient { public static final byte READYSTATE_INIT_FAILED = -2; public static final byte READYSTATE_FAILED = -1; @@ -123,15 +178,14 @@ public class PlatformWebRTC { synchronized (lock1) { if (iceCandidate.sdp != null && !iceCandidate.sdp.isEmpty()) { if (iceCandidates.isEmpty()) { - new Thread(() -> { - EagUtils.sleep(3000); + scheduleTask(3000l, () -> { synchronized (lock1) { if (peerConnection != null && peerConnection.getConnectionState() != RTCPeerConnectionState.DISCONNECTED) { clientICECandidate = JSONWriter.valueToString(iceCandidates); iceCandidates.clear(); } } - }).start(); + }); } Map m = new HashMap<>(); m.put("sdpMLineIndex", "" + iceCandidate.sdpMLineIndex); @@ -203,7 +257,7 @@ public class PlatformWebRTC { @Override public void onStateChange() { if (dataChannel != null && dataChannel.getState() == RTCDataChannelState.OPEN) { - new Thread(() -> { + scheduleTask(-1l, () -> { while (true) { synchronized (lock1) { if (iceCandidates.isEmpty()) { @@ -216,7 +270,7 @@ public class PlatformWebRTC { clientDataChannelClosed = false; clientDataChannelOpen = true; } - }).start(); + }); } } @@ -486,8 +540,7 @@ public class PlatformWebRTC { synchronized (lock3) { if (iceCandidate.sdp != null && !iceCandidate.sdp.isEmpty()) { if (iceCandidates.isEmpty()) { - new Thread(() -> { - EagUtils.sleep(3000); + scheduleTask(3000l, () -> { synchronized (lock3) { if (peerConnection[0] != null && peerConnection[0].getConnectionState() != RTCPeerConnectionState.DISCONNECTED) { LANPeerEvent.LANPeerICECandidateEvent e = new LANPeerEvent.LANPeerICECandidateEvent(peerId, JSONWriter.valueToString(iceCandidates)); @@ -497,7 +550,7 @@ public class PlatformWebRTC { iceCandidates.clear(); } } - }).start(); + }); } Map m = new HashMap<>(); m.put("sdpMLineIndex", "" + iceCandidate.sdpMLineIndex); @@ -509,7 +562,7 @@ public class PlatformWebRTC { @Override public void onDataChannel(RTCDataChannel dataChannel) { - new Thread(() -> { + scheduleTask(-1l, () -> { while (true) { synchronized (lock3) { if (iceCandidates.isEmpty()) { @@ -547,7 +600,7 @@ public class PlatformWebRTC { } } }); - }).start(); + }); } @Override @@ -625,795 +678,27 @@ public class PlatformWebRTC { return peerList.size(); } } - - private static final Map relayQueryLimited = new HashMap<>(); - private static final Map relayQueryBlocked = new HashMap<>(); - - private static class RelayQueryImpl implements RelayQuery { - - private final WebSocketClient sock; - private final String uri; - - private boolean open; - private boolean failed; - - private boolean hasRecievedAnyData = false; - - private int vers = -1; - private String comment = ""; - private String brand = ""; - - private long connectionOpenedAt; - private long connectionPingStart = -1; - private long connectionPingTimer = -1; - - private RateLimit rateLimitStatus = RateLimit.NONE; - - private VersionMismatch versError = VersionMismatch.UNKNOWN; - - private RelayQueryImpl(String uri) { - this.uri = uri; - WebSocketClient s; - try { - connectionOpenedAt = System.currentTimeMillis(); - s = new WebSocketClient(new URI(uri)) { - @Override - public void onOpen(ServerHandshake serverHandshake) { - try { - connectionPingStart = System.currentTimeMillis(); - sock.send(IPacket.writePacket(new IPacket00Handshake(0x03, RelayManager.preferredRelayVersion, ""))); - } catch (IOException e) { - logger.error(e.toString()); - sock.close(); - failed = true; - } - } - - @Override - public void onMessage(String s) { - // - } - - @Override - public void onMessage(ByteBuffer bb) { - if(bb != null) { - hasRecievedAnyData = true; - byte[] arr = new byte[bb.remaining()]; - bb.get(arr); - if(arr.length == 2 && arr[0] == (byte)0xFC) { - long millis = System.currentTimeMillis(); - if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) { - rateLimitStatus = RateLimit.BLOCKED; - relayQueryLimited.put(RelayQueryImpl.this.uri, millis); - }else if(arr[1] == (byte)0x02) { - rateLimitStatus = RateLimit.NOW_LOCKED; - relayQueryLimited.put(RelayQueryImpl.this.uri, millis); - relayQueryBlocked.put(RelayQueryImpl.this.uri, millis); - }else { - rateLimitStatus = RateLimit.LOCKED; - relayQueryBlocked.put(RelayQueryImpl.this.uri, millis); - } - failed = true; - open = false; - sock.close(); - }else { - if(open) { - try { - IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); - if(pkt instanceof IPacket69Pong) { - IPacket69Pong ipkt = (IPacket69Pong)pkt; - versError = VersionMismatch.COMPATIBLE; - if(connectionPingTimer == -1) { - connectionPingTimer = System.currentTimeMillis() - connectionPingStart; - } - vers = ipkt.protcolVersion; - comment = ipkt.comment; - brand = ipkt.brand; - open = false; - failed = false; - sock.close(); - }else if(pkt instanceof IPacket70SpecialUpdate) { - IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate)pkt; - if(ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { - UpdateService.addCertificateToSet(ipkt.updatePacket); - } - }else if(pkt instanceof IPacketFFErrorCode) { - IPacketFFErrorCode ipkt = (IPacketFFErrorCode)pkt; - if(ipkt.code == IPacketFFErrorCode.TYPE_PROTOCOL_VERSION) { - String s1 = ipkt.desc.toLowerCase(); - if(s1.contains("outdated client") || s1.contains("client outdated")) { - versError = VersionMismatch.CLIENT_OUTDATED; - }else if(s1.contains("outdated server") || s1.contains("server outdated") || - s1.contains("outdated relay") || s1.contains("server relay")) { - versError = VersionMismatch.RELAY_OUTDATED; - }else { - versError = VersionMismatch.UNKNOWN; - } - } - logger.error("{}\": Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc); - open = false; - failed = true; - sock.close(); - }else { - throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'"); - } - } catch (IOException e) { - logger.error("Relay Query Error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - open = false; - failed = true; - sock.close(); - } - } - } - } - } - - @Override - public void onClose(int i, String s, boolean b) { - open = false; - if(!hasRecievedAnyData) { - failed = true; - Long l = relayQueryBlocked.get(uri); - if(l != null) { - if(System.currentTimeMillis() - l.longValue() < 400000l) { - rateLimitStatus = RateLimit.LOCKED; - return; - } - } - l = relayQueryLimited.get(uri); - if(l != null) { - if(System.currentTimeMillis() - l.longValue() < 900000l) { - rateLimitStatus = RateLimit.BLOCKED; - return; - } - } - } - } - - @Override - public void onError(Exception e) { - EagRuntime.debugPrintStackTrace(e); - } - }; - s.connect(); - open = true; - failed = false; - }catch(Throwable t) { - connectionOpenedAt = 0l; - sock = null; - open = false; - failed = true; - return; - } - sock = s; - } - - @Override - public boolean isQueryOpen() { - return open; - } - - @Override - public boolean isQueryFailed() { - return failed; - } - - @Override - public RateLimit isQueryRateLimit() { - return rateLimitStatus; - } - - @Override - public void close() { - if(sock != null && open) { - sock.close(); - } - open = false; - } - - @Override - public int getVersion() { - return vers; - } - - @Override - public String getComment() { - return comment; - } - - @Override - public String getBrand() { - return brand; - } - - @Override - public long getPing() { - return connectionPingTimer < 1 ? 1 : connectionPingTimer; - } - - @Override - public VersionMismatch getCompatible() { - return versError; - } - - } - - private static class RelayQueryRatelimitDummy implements RelayQuery { - - private final RateLimit type; - - private RelayQueryRatelimitDummy(RateLimit type) { - this.type = type; - } - - @Override - public boolean isQueryOpen() { - return false; - } - - @Override - public boolean isQueryFailed() { - return true; - } - - @Override - public RateLimit isQueryRateLimit() { - return type; - } - - @Override - public void close() { - } - - @Override - public int getVersion() { - return RelayManager.preferredRelayVersion; - } - - @Override - public String getComment() { - return "this query was rate limited"; - } - - @Override - public String getBrand() { - return "lax1dude"; - } - - @Override - public long getPing() { - return 0l; - } - - @Override - public VersionMismatch getCompatible() { - return VersionMismatch.COMPATIBLE; - } - - } - public static RelayQuery openRelayQuery(String addr) { - long millis = System.currentTimeMillis(); - - Long l = relayQueryBlocked.get(addr); - if(l != null && millis - l.longValue() < 60000l) { - return new RelayQueryRatelimitDummy(RelayQuery.RateLimit.LOCKED); + RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr); + if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) { + return new RelayQueryRateLimitDummy(limit); } - - l = relayQueryLimited.get(addr); - if(l != null && millis - l.longValue() < 10000l) { - return new RelayQueryRatelimitDummy(RelayQuery.RateLimit.BLOCKED); - } - return new RelayQueryImpl(addr); } - private static class RelayWorldsQueryImpl implements RelayWorldsQuery { - - private final WebSocketClient sock; - private final String uri; - - private boolean open; - private boolean failed; - - private boolean hasRecievedAnyData = false; - private RelayQuery.RateLimit rateLimitStatus = RelayQuery.RateLimit.NONE; - - private RelayQuery.VersionMismatch versError = RelayQuery.VersionMismatch.UNKNOWN; - - private List worlds = null; - - private RelayWorldsQueryImpl(String uri) { - this.uri = uri; - WebSocketClient s; - try { - s = new WebSocketClient(new URI(uri)) { - @Override - public void onOpen(ServerHandshake serverHandshake) { - try { - sock.send(IPacket.writePacket(new IPacket00Handshake(0x04, RelayManager.preferredRelayVersion, ""))); - } catch (IOException e) { - logger.error(e.toString()); - sock.close(); - open = false; - failed = true; - } - } - - @Override - public void onMessage(String s) { - // - } - - @Override - public void onMessage(ByteBuffer bb) { - if(bb != null) { - hasRecievedAnyData = true; - byte[] arr = new byte[bb.remaining()]; - bb.get(arr); - if(arr.length == 2 && arr[0] == (byte)0xFC) { - long millis = System.currentTimeMillis(); - if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) { - rateLimitStatus = RelayQuery.RateLimit.BLOCKED; - relayQueryLimited.put(RelayWorldsQueryImpl.this.uri, millis); - }else if(arr[1] == (byte)0x02) { - rateLimitStatus = RelayQuery.RateLimit.NOW_LOCKED; - relayQueryLimited.put(RelayWorldsQueryImpl.this.uri, millis); - relayQueryBlocked.put(RelayWorldsQueryImpl.this.uri, millis); - }else { - rateLimitStatus = RelayQuery.RateLimit.LOCKED; - relayQueryBlocked.put(RelayWorldsQueryImpl.this.uri, millis); - } - open = false; - failed = true; - sock.close(); - }else { - if(open) { - try { - IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); - if(pkt instanceof IPacket07LocalWorlds) { - worlds = ((IPacket07LocalWorlds)pkt).worldsList; - sock.close(); - open = false; - failed = false; - }else if(pkt instanceof IPacket70SpecialUpdate) { - IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate)pkt; - if(ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { - UpdateService.addCertificateToSet(ipkt.updatePacket); - } - }else if(pkt instanceof IPacketFFErrorCode) { - IPacketFFErrorCode ipkt = (IPacketFFErrorCode)pkt; - if(ipkt.code == IPacketFFErrorCode.TYPE_PROTOCOL_VERSION) { - String s1 = ipkt.desc.toLowerCase(); - if(s1.contains("outdated client") || s1.contains("client outdated")) { - versError = RelayQuery.VersionMismatch.CLIENT_OUTDATED; - }else if(s1.contains("outdated server") || s1.contains("server outdated") || - s1.contains("outdated relay") || s1.contains("server relay")) { - versError = RelayQuery.VersionMismatch.RELAY_OUTDATED; - }else { - versError = RelayQuery.VersionMismatch.UNKNOWN; - } - } - logger.error("{}: Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc); - open = false; - failed = true; - sock.close(); - }else { - throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'"); - } - } catch (IOException e) { - logger.error("Relay World Query Error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - open = false; - failed = true; - sock.close(); - } - } - } - } - } - - @Override - public void onClose(int i, String s, boolean b) { - open = false; - if(!hasRecievedAnyData) { - failed = true; - Long l = relayQueryBlocked.get(uri); - if(l != null) { - if(System.currentTimeMillis() - l.longValue() < 400000l) { - rateLimitStatus = RelayQuery.RateLimit.LOCKED; - return; - } - } - l = relayQueryLimited.get(uri); - if(l != null) { - if(System.currentTimeMillis() - l.longValue() < 900000l) { - rateLimitStatus = RelayQuery.RateLimit.BLOCKED; - return; - } - } - } - } - - @Override - public void onError(Exception e) { - EagRuntime.debugPrintStackTrace(e); - } - }; - s.connect(); - open = true; - failed = false; - }catch(Throwable t) { - sock = null; - open = false; - failed = true; - return; - } - sock = s; - } - - @Override - public boolean isQueryOpen() { - return open; - } - - @Override - public boolean isQueryFailed() { - return failed; - } - - @Override - public RelayQuery.RateLimit isQueryRateLimit() { - return rateLimitStatus; - } - - @Override - public void close() { - if(open && sock != null) { - sock.close(); - } - open = false; - } - - @Override - public List getWorlds() { - return worlds; - } - - @Override - public RelayQuery.VersionMismatch getCompatible() { - return versError; - } - - } - - private static class RelayWorldsQueryRatelimitDummy implements RelayWorldsQuery { - - private final RelayQuery.RateLimit rateLimit; - - private RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit rateLimit) { - this.rateLimit = rateLimit; - } - - @Override - public boolean isQueryOpen() { - return false; - } - - @Override - public boolean isQueryFailed() { - return true; - } - - @Override - public RelayQuery.RateLimit isQueryRateLimit() { - return rateLimit; - } - - @Override - public void close() { - } - - @Override - public List getWorlds() { - return new ArrayList(0); - } - - @Override - public RelayQuery.VersionMismatch getCompatible() { - return RelayQuery.VersionMismatch.COMPATIBLE; - } - } - public static RelayWorldsQuery openRelayWorldsQuery(String addr) { - long millis = System.currentTimeMillis(); - - Long l = relayQueryBlocked.get(addr); - if(l != null && millis - l.longValue() < 60000l) { - return new RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit.LOCKED); + RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr); + if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) { + return new RelayWorldsQueryRateLimitDummy(limit); } - - l = relayQueryLimited.get(addr); - if(l != null && millis - l.longValue() < 10000l) { - return new RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit.BLOCKED); - } - return new RelayWorldsQueryImpl(addr); } - private static class RelayServerSocketImpl implements RelayServerSocket { - - private final WebSocketClient sock; - private final String uri; - - private boolean open; - private boolean closed; - private boolean failed; - - private boolean hasRecievedAnyData; - - private final List exceptions = new LinkedList(); - private final List packets = new LinkedList(); - - private RelayServerSocketImpl(String uri, int timeout) { - this.uri = uri; - WebSocketClient s; - try { - s = new WebSocketClient(new URI(uri)) { - @Override - public void onOpen(ServerHandshake serverHandshake) { - synchronized (lock4) { - open = true; - } - } - - @Override - public void onMessage(String s) { - // - } - - @Override - public void onMessage(ByteBuffer bb) { - if(bb != null) { - hasRecievedAnyData = true; - try { - byte[] arr = new byte[bb.remaining()]; - bb.get(arr); - IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); - if(pkt instanceof IPacket70SpecialUpdate) { - IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate)pkt; - if(ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { - UpdateService.addCertificateToSet(ipkt.updatePacket); - } - }else { - packets.add(pkt); - } - } catch (IOException e) { - exceptions.add(e); - logger.error("Relay Socket Error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - synchronized (lock4) { - open = false; - failed = true; - } - closed = true; - synchronized (lock4) { - sock.close(); - } - } - } - } - - @Override - public void onClose(int i, String s, boolean b) { - if (!hasRecievedAnyData) { - failed = true; - } - synchronized (lock4) { - open = false; - closed = true; - } - } - - @Override - public void onError(Exception e) { - EagRuntime.debugPrintStackTrace(e); - } - }; - s.connect(); - synchronized (lock4) { - open = false; - closed = false; - } - failed = false; - }catch(Throwable t) { - exceptions.add(t); - synchronized (lock4) { - sock = null; - open = false; - closed = true; - } - failed = true; - return; - } - synchronized (lock4) { - sock = s; - } - new Thread(() -> { - EagUtils.sleep(timeout); - synchronized (lock4) { - if (!open && !closed) { - closed = true; - sock.close(); - } - } - }).start(); - } - - @Override - public boolean isOpen() { - return open; - } - - @Override - public boolean isClosed() { - synchronized (lock4) { - return closed; - } - } - - @Override - public void close() { - if(open && sock != null) { - synchronized (lock4) { - sock.close(); - } - } - synchronized (lock4) { - open = false; - closed = true; - } - } - - @Override - public boolean isFailed() { - return failed; - } - - @Override - public Throwable getException() { - if(!exceptions.isEmpty()) { - return exceptions.remove(0); - }else { - return null; - } - } - - @Override - public void writePacket(IPacket pkt) { - try { - sock.send(IPacket.writePacket(pkt)); - } catch (Throwable e) { - logger.error("Relay connection error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - exceptions.add(e); - failed = true; - synchronized (lock4) { - open = false; - closed = true; - sock.close(); - } - } - } - - @Override - public IPacket readPacket() { - if(!packets.isEmpty()) { - return packets.remove(0); - }else { - return null; - } - } - - @Override - public IPacket nextPacket() { - if(!packets.isEmpty()) { - return packets.get(0); - }else { - return null; - } - } - - @Override - public RelayQuery.RateLimit getRatelimitHistory() { - if(relayQueryBlocked.containsKey(uri)) { - return RelayQuery.RateLimit.LOCKED; - } - if(relayQueryLimited.containsKey(uri)) { - return RelayQuery.RateLimit.BLOCKED; - } - return RelayQuery.RateLimit.NONE; - } - - @Override - public String getURI() { - return uri; - } - - } - - private static class RelayServerSocketRatelimitDummy implements RelayServerSocket { - - private final RelayQuery.RateLimit limit; - - private RelayServerSocketRatelimitDummy(RelayQuery.RateLimit limit) { - this.limit = limit; - } - - @Override - public boolean isOpen() { - return false; - } - - @Override - public boolean isClosed() { - return true; - } - - @Override - public void close() { - } - - @Override - public boolean isFailed() { - return true; - } - - @Override - public Throwable getException() { - return null; - } - - @Override - public void writePacket(IPacket pkt) { - } - - @Override - public IPacket readPacket() { - return null; - } - - @Override - public IPacket nextPacket() { - return null; - } - - @Override - public RelayQuery.RateLimit getRatelimitHistory() { - return limit; - } - - @Override - public String getURI() { - return ""; - } - - } - public static RelayServerSocket openRelayConnection(String addr, int timeout) { - long millis = System.currentTimeMillis(); - - Long l = relayQueryBlocked.get(addr); - if(l != null && millis - l.longValue() < 60000l) { - return new RelayServerSocketRatelimitDummy(RelayQuery.RateLimit.LOCKED); + RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr); + if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) { + return new RelayServerSocketRateLimitDummy(limit); } - - l = relayQueryLimited.get(addr); - if(l != null && millis - l.longValue() < 10000l) { - return new RelayServerSocketRatelimitDummy(RelayQuery.RateLimit.BLOCKED); - } - return new RelayServerSocketImpl(addr, timeout); } @@ -1453,7 +738,7 @@ public class PlatformWebRTC { public static List clientLANReadAllPacket() { synchronized(clientLANPacketBuffer) { if(!clientLANPacketBuffer.isEmpty()) { - List ret = new ArrayList(clientLANPacketBuffer); + List ret = new ArrayList<>(clientLANPacketBuffer); clientLANPacketBuffer.clear(); return ret; }else { diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebView.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebView.java new file mode 100755 index 0000000..d08cea8 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebView.java @@ -0,0 +1,93 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.FallbackWebViewServer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformWebView { + + private static FallbackWebViewServer fallbackServer = null; + private static IPacketSendCallback packetCallback = null; + + public static boolean supported() { + return false; + } + + public static boolean isShowing() { + return false; + } + + public static void beginShowing(WebViewOptions options, int x, int y, int w, int h) { + + } + + public static void resize(int x, int y, int w, int h) { + + } + + public static void endShowing() { + + } + + public static boolean fallbackSupported() { + return true; + } + + public static void launchFallback(WebViewOptions options) { + fallbackServer = new FallbackWebViewServer(options); + fallbackServer.setPacketSendCallback(packetCallback); + fallbackServer.start(); + } + + public static boolean fallbackRunning() { + return fallbackServer != null && !fallbackServer.isDead(); + } + + public static String getFallbackURL() { + return fallbackServer != null ? fallbackServer.getURL() : null; + } + + public static void endFallbackServer() { + if(fallbackServer != null && !fallbackServer.isDead()) { + fallbackServer.killServer(); + } + } + + public static void handleMessageFromServer(SPacketWebViewMessageV4EAG packet) { + if(fallbackServer != null && !fallbackServer.isDead()) { + fallbackServer.handleMessageFromServer(packet); + } + } + + public static void setPacketSendCallback(IPacketSendCallback callback) { + packetCallback = callback; + if(fallbackServer != null) { + fallbackServer.setPacketSendCallback(callback); + } + } + + public static void runTick() { + if(fallbackServer != null) { + fallbackServer.runTick(); + if(fallbackServer.isDead()) { + fallbackServer = null; + } + } + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java deleted file mode 100644 index 50ca000..0000000 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java +++ /dev/null @@ -1,182 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.util.LinkedList; -import java.util.List; - -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.drafts.Draft; -import org.java_websocket.drafts.Draft_6455; -import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension; -import org.java_websocket.handshake.ServerHandshake; -import org.json.JSONObject; - -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -class WebSocketServerQuery extends WebSocketClient implements IServerQuery { - - private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension()); - - public static final Logger logger = LogManager.getLogger("WebSocketQuery"); - - private final List queryResponses = new LinkedList(); - private final List queryResponsesBytes = new LinkedList(); - private final String type; - private boolean open = true; - private boolean alive = false; - private long pingStart = -1l; - private long pingTimer = -1l; - private EnumServerRateLimit rateLimit = EnumServerRateLimit.OK; - - WebSocketServerQuery(String type, URI serverUri) { - super(serverUri, perMessageDeflateDraft); - this.type = type; - this.setConnectionLostTimeout(5); - this.setTcpNoDelay(true); - this.connect(); - } - - @Override - public int responsesAvailable() { - synchronized (queryResponses) { - return queryResponses.size(); - } - } - - @Override - public QueryResponse getResponse() { - synchronized (queryResponses) { - if (queryResponses.size() > 0) { - return queryResponses.remove(0); - } else { - return null; - } - } - } - - @Override - public int binaryResponsesAvailable() { - synchronized (queryResponsesBytes) { - return queryResponsesBytes.size(); - } - } - - @Override - public byte[] getBinaryResponse() { - synchronized (queryResponsesBytes) { - if (queryResponsesBytes.size() > 0) { - return queryResponsesBytes.remove(0); - } else { - return null; - } - } - } - - @Override - public QueryReadyState readyState() { - return open ? (alive ? QueryReadyState.OPEN : QueryReadyState.CONNECTING) - : (alive ? QueryReadyState.CLOSED : QueryReadyState.FAILED); - } - - @Override - public EnumServerRateLimit getRateLimit() { - return rateLimit; - } - - @Override - public void onClose(int arg0, String arg1, boolean arg2) { - open = false; - } - - @Override - public void onError(Exception arg0) { - logger.error("Exception thrown by websocket query \"" + this.getURI().toString() + "\"!"); - logger.error(arg0); - } - - @Override - public void onMessage(String arg0) { - alive = true; - if (pingTimer == -1) { - pingTimer = System.currentTimeMillis() - pingStart; - if (pingTimer < 1) { - pingTimer = 1; - } - } - if (arg0.equalsIgnoreCase("BLOCKED")) { - logger.error("Reached full IP ratelimit for {}!", this.uri.toString()); - rateLimit = EnumServerRateLimit.BLOCKED; - return; - } - if (arg0.equalsIgnoreCase("LOCKED")) { - logger.error("Reached full IP ratelimit lockout for {}!", this.uri.toString()); - rateLimit = EnumServerRateLimit.LOCKED_OUT; - return; - } - try { - JSONObject obj = new JSONObject(arg0); - if ("blocked".equalsIgnoreCase(obj.optString("type", null))) { - logger.error("Reached query ratelimit for {}!", this.uri.toString()); - rateLimit = EnumServerRateLimit.BLOCKED; - } else if ("locked".equalsIgnoreCase(obj.optString("type", null))) { - logger.error("Reached query ratelimit lockout for {}!", this.uri.toString()); - rateLimit = EnumServerRateLimit.LOCKED_OUT; - } else { - QueryResponse response = new QueryResponse(obj, pingTimer); - synchronized (queryResponses) { - queryResponses.add(response); - } - } - } catch (Throwable t) { - logger.error( - "Exception thrown parsing websocket query response from \"" + this.getURI().toString() + "\"!"); - logger.error(t); - } - } - - @Override - public void onMessage(ByteBuffer arg0) { - alive = true; - if (pingTimer == -1) { - pingTimer = System.currentTimeMillis() - pingStart; - if (pingTimer < 1) { - pingTimer = 1; - } - } - synchronized (queryResponsesBytes) { - queryResponsesBytes.add(arg0.array()); - } - } - - @Override - public void onOpen(ServerHandshake arg0) { - pingStart = System.currentTimeMillis(); - send("Accept: " + type); - } - -} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java index a163fe7..abe450c 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java @@ -1,278 +1,378 @@ -package net.lax1dude.eaglercraft.v1_8.internal.buffer; - -import org.lwjgl.system.jemalloc.JEmalloc; - -import net.lax1dude.unsafememcpy.UnsafeMemcpy; -import net.lax1dude.unsafememcpy.UnsafeUtils; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglerLWJGLFloatBuffer implements FloatBuffer { - - final long address; - final boolean original; - - private final int capacity; - private int position; - private int limit; - private int mark; - - private static final int SHIFT = 2; - - EaglerLWJGLFloatBuffer(long address, int capacity, boolean original) { - this(address, capacity, 0, capacity, -1, original); - } - - EaglerLWJGLFloatBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { - this.address = address; - this.capacity = capacity; - this.position = position; - this.limit = limit; - this.mark = mark; - this.original = original; - } - - @Override - public int capacity() { - return capacity; - } - - @Override - public int position() { - return position; - } - - @Override - public int limit() { - return limit; - } - - @Override - public int remaining() { - return limit - position; - } - - @Override - public boolean hasRemaining() { - return position < limit; - } - - @Override - public boolean isReadOnly() { - return false; - } - - @Override - public boolean hasArray() { - return false; - } - - @Override - public Object array() { - throw new UnsupportedOperationException(); - } - - @Override - public int arrayOffset() { - return position; - } - - @Override - public FloatBuffer slice() { - return new EaglerLWJGLFloatBuffer(address + (position << SHIFT), limit - position, false); - } - - @Override - public FloatBuffer duplicate() { - return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false); - } - - @Override - public FloatBuffer asReadOnlyBuffer() { - return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false); - } - - @Override - public float get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return UnsafeUtils.getMemFloat(address + ((position++) << SHIFT)); - } - - @Override - public FloatBuffer put(float b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), b); - return this; - } - - @Override - public float get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return UnsafeUtils.getMemFloat(address + (index << SHIFT)); - } - - @Override - public FloatBuffer put(int index, float b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - UnsafeUtils.setMemFloat(address + (index << SHIFT), b); - return this; - } - - @Override - public float getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return UnsafeUtils.getMemFloat(address + (index << SHIFT)); - } - - @Override - public void putElement(int index, float value) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), value); - } - - @Override - public FloatBuffer get(float[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); - position += length; - return this; - } - - @Override - public FloatBuffer get(float[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); - position += dst.length; - return this; - } - - @Override - public FloatBuffer put(FloatBuffer src) { - if(src instanceof EaglerLWJGLFloatBuffer) { - EaglerLWJGLFloatBuffer c = (EaglerLWJGLFloatBuffer)src; - int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); - position += l; - c.position += l; - }else { - int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - for(int i = 0; i < l; ++i) { - UnsafeUtils.setMemFloat(address + ((position + l) << SHIFT), src.get()); - } - position += l; - } - return this; - } - - @Override - public FloatBuffer put(float[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); - position += length; - return this; - } - - @Override - public FloatBuffer put(float[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); - position += src.length; - return this; - } - - @Override - public int getArrayOffset() { - return position; - } - - @Override - public FloatBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerLWJGLFloatBuffer(0l, 0, false); - } - - int newLen = limit - position; - long newAlloc = JEmalloc.nje_malloc(newLen); - if(newAlloc == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); - - return new EaglerLWJGLFloatBuffer(newAlloc, newLen, true); - } - - @Override - public boolean isDirect() { - return true; - } - - @Override - public FloatBuffer mark() { - mark = position; - return this; - } - - @Override - public FloatBuffer reset() { - int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); - position = m; - return this; - } - - @Override - public FloatBuffer clear() { - position = 0; - limit = capacity; - mark = -1; - return this; - } - - @Override - public FloatBuffer flip() { - limit = position; - position = 0; - mark = -1; - return this; - } - - @Override - public FloatBuffer rewind() { - position = 0; - mark = -1; - return this; - } - - @Override - public FloatBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); - limit = newLimit; - return this; - } - - @Override - public FloatBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); - position = newPosition; - return this; - } - -} +package net.lax1dude.eaglercraft.v1_8.internal.buffer; + +import net.lax1dude.unsafememcpy.UnsafeMemcpy; +import net.lax1dude.unsafememcpy.UnsafeUtils; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerLWJGLByteBuffer implements ByteBuffer { + + final long address; + final boolean original; + + private final int capacity; + private int position; + private int limit; + private int mark; + + EaglerLWJGLByteBuffer(long address, int capacity, boolean original) { + this(address, capacity, 0, capacity, -1, original); + } + + EaglerLWJGLByteBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { + this.address = address; + this.capacity = capacity; + this.position = position; + this.limit = limit; + this.mark = mark; + this.original = original; + } + + @Override + public int capacity() { + return capacity; + } + + @Override + public int position() { + return position; + } + + @Override + public int limit() { + return limit; + } + + @Override + public int remaining() { + return limit - position; + } + + @Override + public boolean hasRemaining() { + return position < limit; + } + + @Override + public boolean hasArray() { + return false; + } + + @Override + public byte[] array() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDirect() { + return true; + } + + @Override + public ByteBuffer duplicate() { + return new EaglerLWJGLByteBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public byte get() { + if(position >= limit) throw Buffer.makeIOOBE(position); + return UnsafeUtils.getMemByte(address + position++); + } + + @Override + public ByteBuffer put(byte b) { + if(position >= limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemByte(address + position++, b); + return this; + } + + @Override + public byte get(int index) { + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemByte(address + index); + } + + @Override + public ByteBuffer put(int index, byte b) { + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemByte(address + index, b); + return this; + } + + @Override + public ByteBuffer get(byte[] dst, int offset, int length) { + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); + UnsafeMemcpy.memcpy(dst, offset, address + position, length); + position += length; + return this; + } + + @Override + public ByteBuffer get(byte[] dst) { + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); + UnsafeMemcpy.memcpy(dst, 0, address + position, dst.length); + position += dst.length; + return this; + } + + @Override + public ByteBuffer put(ByteBuffer src) { + if(src instanceof EaglerLWJGLByteBuffer) { + EaglerLWJGLByteBuffer c = (EaglerLWJGLByteBuffer)src; + int l = c.limit - c.position; + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); + UnsafeMemcpy.memcpy(address + position, c.address + c.position, l); + position += l; + c.position += l; + }else { + int l = src.remaining(); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); + for(int i = 0; i < l; ++i) { + UnsafeUtils.setMemByte(address + position + l, src.get()); + } + position += l; + } + return this; + } + + @Override + public ByteBuffer put(byte[] src, int offset, int length) { + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); + UnsafeMemcpy.memcpy(address + position, src, offset, length); + position += length; + return this; + } + + @Override + public ByteBuffer put(byte[] src) { + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); + UnsafeMemcpy.memcpy(address + position, src, 0, src.length); + position += src.length; + return this; + } + + @Override + public char getChar() { + if(position + 2 > limit) throw Buffer.makeIOOBE(position); + char c = UnsafeUtils.getMemChar(address + position); + position += 2; + return c; + } + + @Override + public ByteBuffer putChar(char value) { + if(position + 2 > limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemChar(address + position, value); + position += 2; + return this; + } + + @Override + public char getChar(int index) { + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemChar(address + index); + } + + @Override + public ByteBuffer putChar(int index, char value) { + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemChar(address + index, value); + return this; + } + + @Override + public short getShort() { + if(position + 2 > limit) throw Buffer.makeIOOBE(position); + short s = UnsafeUtils.getMemShort(address + position); + position += 2; + return s; + } + + @Override + public ByteBuffer putShort(short value) { + if(position + 2 > limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemShort(address + position, value); + position += 2; + return this; + } + + @Override + public short getShort(int index) { + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemShort(address + index); + } + + @Override + public ByteBuffer putShort(int index, short value) { + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemShort(address + index, value); + return this; + } + + @Override + public ShortBuffer asShortBuffer() { + return new EaglerLWJGLShortBuffer(address, capacity >> 1, false); + } + + @Override + public int getInt() { + if(position + 4 > limit) throw Buffer.makeIOOBE(position); + int i = UnsafeUtils.getMemInt(address + position); + position += 4; + return i; + } + + @Override + public ByteBuffer putInt(int value) { + if(position + 4 > limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemInt(address + position, value); + position += 4; + return this; + } + + @Override + public int getInt(int index) { + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemInt(address + index); + } + + @Override + public ByteBuffer putInt(int index, int value) { + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemInt(address + index, value); + return this; + } + + @Override + public IntBuffer asIntBuffer() { + return new EaglerLWJGLIntBuffer(address, capacity >> 2, false); + } + + @Override + public long getLong() { + if(position + 8 > limit) throw Buffer.makeIOOBE(position); + long l = UnsafeUtils.getMemLong(address + position); + position += 8; + return l; + } + + @Override + public ByteBuffer putLong(long value) { + if(position + 8 > limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemLong(address + position, value); + position += 8; + return this; + } + + @Override + public long getLong(int index) { + if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemLong(address + index); + } + + @Override + public ByteBuffer putLong(int index, long value) { + if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemLong(address + index, value); + return this; + } + + @Override + public float getFloat() { + if(position + 4 > limit) throw Buffer.makeIOOBE(position); + float f = UnsafeUtils.getMemFloat(address + position); + position += 4; + return f; + } + + @Override + public ByteBuffer putFloat(float value) { + if(position + 4 > limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemFloat(address + position, value); + position += 4; + return this; + } + + @Override + public float getFloat(int index) { + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemFloat(address + index); + } + + @Override + public ByteBuffer putFloat(int index, float value) { + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemFloat(address + index, value); + return this; + } + + @Override + public FloatBuffer asFloatBuffer() { + return new EaglerLWJGLFloatBuffer(address, capacity >> 2, false); + } + + @Override + public ByteBuffer mark() { + mark = position; + return this; + } + + @Override + public ByteBuffer reset() { + int m = mark; + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); + position = m; + return this; + } + + @Override + public ByteBuffer clear() { + position = 0; + limit = capacity; + mark = -1; + return this; + } + + @Override + public ByteBuffer flip() { + limit = position; + position = 0; + mark = -1; + return this; + } + + @Override + public ByteBuffer rewind() { + position = 0; + mark = -1; + return this; + } + + @Override + public ByteBuffer limit(int newLimit) { + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); + limit = newLimit; + return this; + } + + @Override + public ByteBuffer position(int newPosition) { + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); + position = newPosition; + return this; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java index bf306a2..aa862fd 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java @@ -1,7 +1,5 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.jemalloc.JEmalloc; - import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; @@ -20,8 +18,8 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerLWJGLIntBuffer implements IntBuffer { - +public class EaglerLWJGLFloatBuffer implements FloatBuffer { + final long address; final boolean original; @@ -29,14 +27,14 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { private int position; private int limit; private int mark; - + private static final int SHIFT = 2; - - EaglerLWJGLIntBuffer(long address, int capacity, boolean original) { + + EaglerLWJGLFloatBuffer(long address, int capacity, boolean original) { this(address, capacity, 0, capacity, -1, original); } - EaglerLWJGLIntBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { + EaglerLWJGLFloatBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { this.address = address; this.capacity = capacity; this.position = position; @@ -70,109 +68,89 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { return position < limit; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public float[] array() { throw new UnsupportedOperationException(); } @Override - public int arrayOffset() { - return position; + public FloatBuffer duplicate() { + return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false); } @Override - public IntBuffer slice() { - return new EaglerLWJGLIntBuffer(address + (position << SHIFT), limit - position, false); + public float get() { + if(position >= limit) throw Buffer.makeIOOBE(position); + return UnsafeUtils.getMemFloat(address + ((position++) << SHIFT)); } @Override - public IntBuffer duplicate() { - return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false); - } - - @Override - public IntBuffer asReadOnlyBuffer() { - return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false); - } - - @Override - public int get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return UnsafeUtils.getMemInt(address + ((position++) << SHIFT)); - } - - @Override - public IntBuffer put(int b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - UnsafeUtils.setMemInt(address + ((position++) << SHIFT), b); + public FloatBuffer put(float b) { + if(position >= limit) throw Buffer.makeIOOBE(position); + UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), b); return this; } @Override - public int get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return UnsafeUtils.getMemInt(address + (index << SHIFT)); + public float get(int index) { + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemFloat(address + (index << SHIFT)); } @Override - public IntBuffer put(int index, int b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - UnsafeUtils.setMemInt(address + (index << SHIFT), b); + public FloatBuffer put(int index, float b) { + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemFloat(address + (index << SHIFT), b); return this; } @Override - public int getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return UnsafeUtils.getMemInt(address + (index << SHIFT)); + public float getElement(int index) { + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); + return UnsafeUtils.getMemFloat(address + (index << SHIFT)); } @Override - public void putElement(int index, int value) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - UnsafeUtils.setMemInt(address + (index << SHIFT), value); + public void putElement(int index, float value) { + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); + UnsafeUtils.setMemFloat(address + (index << SHIFT), value); } @Override - public IntBuffer get(int[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + public FloatBuffer get(float[] dst, int offset, int length) { + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); position += length; return this; } @Override - public IntBuffer get(int[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + public FloatBuffer get(float[] dst) { + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); position += dst.length; return this; } @Override - public IntBuffer put(IntBuffer src) { - if(src instanceof EaglerLWJGLIntBuffer) { - EaglerLWJGLIntBuffer c = (EaglerLWJGLIntBuffer)src; + public FloatBuffer put(FloatBuffer src) { + if(src instanceof EaglerLWJGLFloatBuffer) { + EaglerLWJGLFloatBuffer c = (EaglerLWJGLFloatBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { - UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get()); + UnsafeUtils.setMemFloat(address + ((position + l) << SHIFT), src.get()); } position += l; } @@ -180,66 +158,42 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { } @Override - public IntBuffer put(int[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + public FloatBuffer put(float[] src, int offset, int length) { + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); position += length; return this; } @Override - public IntBuffer put(int[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + public FloatBuffer put(float[] src) { + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); position += src.length; return this; } - @Override - public int getArrayOffset() { - return position; - } - - @Override - public IntBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerLWJGLIntBuffer(0l, 0, false); - } - - int newLen = limit - position; - long newAlloc = JEmalloc.nje_malloc(newLen); - if(newAlloc == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); - - return new EaglerLWJGLIntBuffer(newAlloc, newLen, true); - } - @Override public boolean isDirect() { return true; } @Override - public IntBuffer mark() { + public FloatBuffer mark() { mark = position; return this; } @Override - public IntBuffer reset() { + public FloatBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @Override - public IntBuffer clear() { + public FloatBuffer clear() { position = 0; limit = capacity; mark = -1; @@ -247,7 +201,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { } @Override - public IntBuffer flip() { + public FloatBuffer flip() { limit = position; position = 0; mark = -1; @@ -255,22 +209,22 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { } @Override - public IntBuffer rewind() { + public FloatBuffer rewind() { position = 0; mark = -1; return this; } @Override - public IntBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + public FloatBuffer limit(int newLimit) { + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override - public IntBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + public FloatBuffer position(int newPosition) { + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java index bf306a2..030c3a7 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java @@ -1,7 +1,5 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.jemalloc.JEmalloc; - import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; @@ -21,7 +19,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * */ public class EaglerLWJGLIntBuffer implements IntBuffer { - + final long address; final boolean original; @@ -29,9 +27,9 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { private int position; private int limit; private int mark; - + private static final int SHIFT = 2; - + EaglerLWJGLIntBuffer(long address, int capacity, boolean original) { this(address, capacity, 0, capacity, -1, original); } @@ -44,7 +42,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { this.mark = mark; this.original = original; } - + @Override public int capacity() { return capacity; @@ -70,82 +68,62 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { return position < limit; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public int[] array() { throw new UnsupportedOperationException(); } - @Override - public int arrayOffset() { - return position; - } - - @Override - public IntBuffer slice() { - return new EaglerLWJGLIntBuffer(address + (position << SHIFT), limit - position, false); - } - @Override public IntBuffer duplicate() { return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false); } - @Override - public IntBuffer asReadOnlyBuffer() { - return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false); - } - @Override public int get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); return UnsafeUtils.getMemInt(address + ((position++) << SHIFT)); } @Override public IntBuffer put(int b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); UnsafeUtils.setMemInt(address + ((position++) << SHIFT), b); return this; } @Override public int get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return UnsafeUtils.getMemInt(address + (index << SHIFT)); } @Override public IntBuffer put(int index, int b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); UnsafeUtils.setMemInt(address + (index << SHIFT), b); return this; } @Override public int getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return UnsafeUtils.getMemInt(address + (index << SHIFT)); } @Override public void putElement(int index, int value) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); UnsafeUtils.setMemInt(address + (index << SHIFT), value); } @Override public IntBuffer get(int[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); position += length; return this; @@ -153,7 +131,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer get(int[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); position += dst.length; return this; @@ -164,13 +142,13 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { if(src instanceof EaglerLWJGLIntBuffer) { EaglerLWJGLIntBuffer c = (EaglerLWJGLIntBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get()); } @@ -181,7 +159,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); position += length; return this; @@ -189,36 +167,12 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); position += src.length; return this; } - @Override - public int getArrayOffset() { - return position; - } - - @Override - public IntBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerLWJGLIntBuffer(0l, 0, false); - } - - int newLen = limit - position; - long newAlloc = JEmalloc.nje_malloc(newLen); - if(newAlloc == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); - - return new EaglerLWJGLIntBuffer(newAlloc, newLen, true); - } - @Override public boolean isDirect() { return true; @@ -233,7 +187,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @@ -263,14 +217,14 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override public IntBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java index eba363d..41d45f6 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java @@ -1,7 +1,5 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.jemalloc.JEmalloc; - import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; @@ -21,7 +19,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * */ public class EaglerLWJGLShortBuffer implements ShortBuffer { - + final long address; final boolean original; @@ -29,9 +27,9 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { private int position; private int limit; private int mark; - + private static final int SHIFT = 1; - + EaglerLWJGLShortBuffer(long address, int capacity, boolean original) { this(address, capacity, 0, capacity, -1, original); } @@ -44,7 +42,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { this.mark = mark; this.original = original; } - + @Override public int capacity() { return capacity; @@ -70,82 +68,62 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { return position < limit; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public short[] array() { throw new UnsupportedOperationException(); } - @Override - public int arrayOffset() { - return position; - } - - @Override - public ShortBuffer slice() { - return new EaglerLWJGLShortBuffer(address + (position << SHIFT), limit - position, false); - } - @Override public ShortBuffer duplicate() { return new EaglerLWJGLShortBuffer(address, capacity, position, limit, mark, false); } - @Override - public ShortBuffer asReadOnlyBuffer() { - return new EaglerLWJGLShortBuffer(address, capacity, position, limit, mark, false); - } - @Override public short get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); return UnsafeUtils.getMemShort(address + ((position++) << SHIFT)); } @Override public ShortBuffer put(short b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); UnsafeUtils.setMemShort(address + ((position++) << SHIFT), b); return this; } @Override public short get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return UnsafeUtils.getMemShort(address + (index << SHIFT)); } @Override public ShortBuffer put(int index, short b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); UnsafeUtils.setMemShort(address + (index << SHIFT), b); return this; } @Override public short getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return UnsafeUtils.getMemShort(address + (index << SHIFT)); } @Override public void putElement(int index, short value) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); UnsafeUtils.setMemShort(address + (index << SHIFT), value); } @Override public ShortBuffer get(short[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); position += length; return this; @@ -153,7 +131,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer get(short[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); position += dst.length; return this; @@ -164,13 +142,13 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { if(src instanceof EaglerLWJGLShortBuffer) { EaglerLWJGLShortBuffer c = (EaglerLWJGLShortBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get()); } @@ -181,7 +159,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); position += length; return this; @@ -189,36 +167,12 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); position += src.length; return this; } - @Override - public int getArrayOffset() { - return position; - } - - @Override - public ShortBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerLWJGLShortBuffer(0l, 0, false); - } - - int newLen = limit - position; - long newAlloc = JEmalloc.nje_malloc(newLen); - if(newAlloc == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); - } - UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); - - return new EaglerLWJGLShortBuffer(newAlloc, newLen, true); - } - @Override public boolean isDirect() { return true; @@ -233,7 +187,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @@ -263,14 +217,14 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override public ShortBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java index ce1badd..e8203eb 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java @@ -1,224 +1,248 @@ -package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2.BreakLoop; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class DebugFilesystem implements PlatformFilesystem.IFilesystemProvider { - - public static DebugFilesystem initialize(File filesystemRoot) { - if(!filesystemRoot.isDirectory() && !filesystemRoot.mkdirs()) { - throw new EaglerFileSystemException("Could not create directory for virtual filesystem: " + filesystemRoot.getAbsolutePath()); - } - return new DebugFilesystem(filesystemRoot); - } - - private final File filesystemRoot; - - private DebugFilesystem(File root) { - this.filesystemRoot = root; - } - - @Override - public boolean eaglerDelete(String pathName) { - File f = getJREFile(pathName); - if(!f.exists()) { - PlatformFilesystem.logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); - return false; - } - if(f.delete()) { - deleteParentIfEmpty(f); - return true; - } - return false; - } - - @Override - public ByteBuffer eaglerRead(String pathName) { - File f = getJREFile(pathName); - if(f.isFile()) { - long fileSize = f.length(); - if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); - ByteBuffer buf = PlatformRuntime.allocateByteBuffer((int)fileSize); - try(FileInputStream is = new FileInputStream(f)) { - byte[] copyBuffer = new byte[4096]; - int i; - while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { - buf.put(copyBuffer, 0, i); - } - if(buf.remaining() > 0) { - throw new EaglerFileSystemException("ERROR: " + buf.remaining() + " bytes are remaining after reading: " + f.getAbsolutePath()); - } - buf.flip(); - ByteBuffer tmp = buf; - buf = null; - return tmp; - }catch (IOException e) { - throw new EaglerFileSystemException("Failed to read: " + f.getAbsolutePath(), e); - }catch(ArrayIndexOutOfBoundsException ex) { - throw new EaglerFileSystemException("ERROR: Expected " + fileSize + " bytes, buffer overflow reading: " + f.getAbsolutePath(), ex); - }finally { - if(buf != null) { - PlatformRuntime.freeByteBuffer(buf); - } - } - }else { - PlatformFilesystem.logger.warn("Tried to read file that doesn't exist: \"{}\"", f.getAbsolutePath()); - return null; - } - } - - @Override - public void eaglerWrite(String pathName, ByteBuffer data) { - File f = getJREFile(pathName); - File p = f.getParentFile(); - if(!p.isDirectory()) { - if(!p.mkdirs()) { - throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); - } - } - try(FileOutputStream fos = new FileOutputStream(f)) { - byte[] copyBuffer = new byte[Math.min(4096, data.remaining())]; - int i; - while((i = data.remaining()) > 0) { - if(i > copyBuffer.length) { - i = copyBuffer.length; - } - data.get(copyBuffer, 0, i); - fos.write(copyBuffer, 0, i); - } - }catch (IOException e) { - throw new EaglerFileSystemException("Failed to write: " + f.getAbsolutePath(), e); - } - } - - @Override - public boolean eaglerExists(String pathName) { - return getJREFile(pathName).isFile(); - } - - @Override - public boolean eaglerMove(String pathNameOld, String pathNameNew) { - File f1 = getJREFile(pathNameOld); - File f2 = getJREFile(pathNameNew); - if(f2.exists()) { - PlatformFilesystem.logger.warn("Tried to rename file \"{}\" to \"{}\" which already exists! File will be replaced"); - if(!f2.delete()) { - return false; - } - } - if(f1.renameTo(f2)) { - deleteParentIfEmpty(f1); - return true; - } - return false; - } - - @Override - public int eaglerCopy(String pathNameOld, String pathNameNew) { - File f1 = getJREFile(pathNameOld); - File f2 = getJREFile(pathNameNew); - if(!f1.isFile()) { - return -1; - } - if(f2.isDirectory()) { - throw new EaglerFileSystemException("Destination file is a directory: " + f2.getAbsolutePath()); - } - File p = f2.getParentFile(); - if(!p.isDirectory()) { - if(!p.mkdirs()) { - throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); - } - } - int sz = 0; - try(FileInputStream is = new FileInputStream(f1)) { - try(FileOutputStream os = new FileOutputStream(f2)) { - byte[] copyBuffer = new byte[4096]; - int i; - while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { - os.write(copyBuffer, 0, i); - sz += i; - } - } - }catch (IOException e) { - throw new EaglerFileSystemException("Failed to copy \"" + f1.getAbsolutePath() + "\" to file \"" + f2.getAbsolutePath() + "\"", e); - } - return sz; - } - - @Override - public int eaglerSize(String pathName) { - File f = getJREFile(pathName); - if(f.isFile()) { - long fileSize = f.length(); - if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); - return (int)fileSize; - }else { - return -1; - } - } - - @Override - public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { - try { - iterateFile(pathName, getJREFile(pathName), itr, recursive); - }catch(BreakLoop ex) { - } - } - - private void iterateFile(String pathName, File f, VFSFilenameIterator itr, boolean recursive) { - if(!f.exists()) { - return; - } - if(!f.isDirectory()) { - itr.next(pathName); - return; - } - File[] fa = f.listFiles(); - for(int i = 0; i < fa.length; ++i) { - File ff = fa[i]; - String fn = pathName + "/" + ff.getName(); - if(ff.isDirectory()) { - if(recursive) { - iterateFile(fn, ff, itr, true); - } - }else { - itr.next(fn); - } - } - } - - private File getJREFile(String path) { - return new File(filesystemRoot, path); - } - - private void deleteParentIfEmpty(File f) { - String[] s; - while((f = f.getParentFile()) != null && (s = f.list()) != null && s.length == 0) { - f.delete(); - } - } -} \ No newline at end of file +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2.BreakLoop; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class DebugFilesystem implements IEaglerFilesystem { + + public static DebugFilesystem initialize(String fsName, File filesystemRoot) { + if(!filesystemRoot.isDirectory() && !filesystemRoot.mkdirs()) { + throw new EaglerFileSystemException("Could not create directory for virtual filesystem: " + filesystemRoot.getAbsolutePath()); + } + return new DebugFilesystem(fsName, filesystemRoot); + } + + private final File filesystemRoot; + private final String fsName; + + private DebugFilesystem(String fsName, File root) { + this.fsName = fsName; + this.filesystemRoot = root; + } + + @Override + public String getFilesystemName() { + return fsName; + } + + @Override + public String getInternalDBName() { + return "desktopruntime:" + filesystemRoot.getAbsolutePath(); + } + + @Override + public boolean eaglerDelete(String pathName) { + File f = getJREFile(pathName); + if(!f.exists()) { + PlatformFilesystem.logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); + return false; + } + if(f.delete()) { + deleteParentIfEmpty(f); + return true; + } + return false; + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + File f = getJREFile(pathName); + if(f.isFile()) { + long fileSize = f.length(); + if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); + ByteBuffer buf = PlatformRuntime.allocateByteBuffer((int)fileSize); + try(FileInputStream is = new FileInputStream(f)) { + byte[] copyBuffer = new byte[4096]; + int i; + while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { + buf.put(copyBuffer, 0, i); + } + if(buf.remaining() > 0) { + throw new EaglerFileSystemException("ERROR: " + buf.remaining() + " bytes are remaining after reading: " + f.getAbsolutePath()); + } + buf.flip(); + ByteBuffer tmp = buf; + buf = null; + return tmp; + }catch (IOException e) { + throw new EaglerFileSystemException("Failed to read: " + f.getAbsolutePath(), e); + }catch(IndexOutOfBoundsException ex) { + throw new EaglerFileSystemException("ERROR: Expected " + fileSize + " bytes, buffer overflow reading: " + f.getAbsolutePath(), ex); + }finally { + if(buf != null) { + PlatformRuntime.freeByteBuffer(buf); + } + } + }else { + PlatformFilesystem.logger.warn("Tried to read file that doesn't exist: \"{}\"", f.getAbsolutePath()); + return null; + } + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + File f = getJREFile(pathName); + File p = f.getParentFile(); + if(!p.isDirectory()) { + if(!p.mkdirs()) { + throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); + } + } + try(FileOutputStream fos = new FileOutputStream(f)) { + byte[] copyBuffer = new byte[Math.min(4096, data.remaining())]; + int i; + while((i = data.remaining()) > 0) { + if(i > copyBuffer.length) { + i = copyBuffer.length; + } + data.get(copyBuffer, 0, i); + fos.write(copyBuffer, 0, i); + } + }catch (IOException e) { + throw new EaglerFileSystemException("Failed to write: " + f.getAbsolutePath(), e); + } + } + + @Override + public boolean eaglerExists(String pathName) { + return getJREFile(pathName).isFile(); + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + File f1 = getJREFile(pathNameOld); + File f2 = getJREFile(pathNameNew); + if(f2.exists()) { + PlatformFilesystem.logger.warn("Tried to rename file \"{}\" to \"{}\" which already exists! File will be replaced"); + if(!f2.delete()) { + return false; + } + } + if(f1.renameTo(f2)) { + deleteParentIfEmpty(f1); + return true; + } + return false; + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + File f1 = getJREFile(pathNameOld); + File f2 = getJREFile(pathNameNew); + if(!f1.isFile()) { + return -1; + } + if(f2.isDirectory()) { + throw new EaglerFileSystemException("Destination file is a directory: " + f2.getAbsolutePath()); + } + File p = f2.getParentFile(); + if(!p.isDirectory()) { + if(!p.mkdirs()) { + throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); + } + } + int sz = 0; + try(FileInputStream is = new FileInputStream(f1)) { + try(FileOutputStream os = new FileOutputStream(f2)) { + byte[] copyBuffer = new byte[4096]; + int i; + while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { + os.write(copyBuffer, 0, i); + sz += i; + } + } + }catch (IOException e) { + throw new EaglerFileSystemException("Failed to copy \"" + f1.getAbsolutePath() + "\" to file \"" + f2.getAbsolutePath() + "\"", e); + } + return sz; + } + + @Override + public int eaglerSize(String pathName) { + File f = getJREFile(pathName); + if(f.isFile()) { + long fileSize = f.length(); + if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); + return (int)fileSize; + }else { + return -1; + } + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + try { + iterateFile(pathName, getJREFile(pathName), itr, recursive); + }catch(BreakLoop ex) { + } + } + + private void iterateFile(String pathName, File f, VFSFilenameIterator itr, boolean recursive) { + if(!f.exists()) { + return; + } + if(!f.isDirectory()) { + itr.next(pathName); + return; + } + File[] fa = f.listFiles(); + for(int i = 0; i < fa.length; ++i) { + File ff = fa[i]; + String fn = pathName + "/" + ff.getName(); + if(ff.isDirectory()) { + if(recursive) { + iterateFile(fn, ff, itr, true); + } + }else { + itr.next(fn); + } + } + } + + private File getJREFile(String path) { + return new File(filesystemRoot, path); + } + + private void deleteParentIfEmpty(File f) { + String[] s; + while((f = f.getParentFile()) != null && (s = f.list()) != null && s.length == 0) { + f.delete(); + } + } + + @Override + public boolean isRamdisk() { + return false; + } + + @Override + public void closeHandle() { + + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java index c912934..0cbe8e4 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java @@ -38,7 +38,7 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { public static final IClientConfigAdapter instance = new DesktopClientConfigAdapter(); - public final List defaultServers = new ArrayList(); + public final List defaultServers = new ArrayList<>(); private final DesktopClientConfigAdapterHooks hooks = new DesktopClientConfigAdapterHooks(); @@ -59,17 +59,17 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { @Override public String getWorldsDB() { - return "desktop"; + return "worlds"; } @Override public String getResourcePacksDB() { - return "desktop"; + return "resourcePacks"; } @Override public JSONObject getIntegratedServerOpts() { - return new JSONObject("{\"container\":null,\"worldsDB\":\"desktop\"}"); + return new JSONObject("{\"container\":null,\"worldsDB\":\"worlds\"}"); } private final List relays = new ArrayList<>(); @@ -150,6 +150,56 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { return EaglercraftVersion.localStorageNamespace; } + @Override + public boolean isEnableMinceraft() { + return true; + } + + @Override + public boolean isEnableServerCookies() { + return true; + } + + @Override + public boolean isAllowServerRedirects() { + return true; + } + + @Override + public boolean isOpenDebugConsoleOnLaunch() { + return false; + } + + @Override + public boolean isForceWebViewSupport() { + return false; + } + + @Override + public boolean isEnableWebViewCSP() { + return true; + } + + @Override + public boolean isAllowBootMenu() { + return false; + } + + @Override + public boolean isForceProfanityFilter() { + return false; + } + + @Override + public boolean isEaglerNoDelay() { + return false; + } + + @Override + public boolean isRamdiskMode() { + return false; + } + @Override public IClientConfigAdapterHooks getHooks() { return hooks; @@ -172,5 +222,12 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { } + @Override + public void callScreenChangedHook(String screenName, int scaledWidth, int scaledHeight, int realWidth, + int realHeight, int scaleFactor) { + + } + } + } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketClient.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketClient.java new file mode 100755 index 0000000..fb64504 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketClient.java @@ -0,0 +1,109 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.net.URI; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.internal.AbstractWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class DesktopWebSocketClient extends AbstractWebSocketClient { + + static final Logger logger = LogManager.getLogger("DesktopWebSocketClient"); + + volatile EnumEaglerConnectionState playConnectState = EnumEaglerConnectionState.CONNECTING; + final Object connectOpenMutex = new Object(); + final WebSocketClientImpl clientImpl; + final URI currentURI; + final String currentURIStr; + + public DesktopWebSocketClient(URI currentURI) { + super(currentURI.toString()); + this.currentURI = currentURI; + currentURIStr = currentURI.toString(); + clientImpl = new WebSocketClientImpl(this, currentURI); + clientImpl.addHeader("Origin", "EAG_LWJGL_" + (EaglercraftVersion.projectForkName + "_" + + EaglercraftVersion.projectOriginVersion).replaceAll("[^a-zA-Z0-9\\-_\\.]", "_")); + } + + @Override + public EnumEaglerConnectionState getState() { + return playConnectState; + } + + @Override + public boolean connectBlocking(int timeoutMS) { + synchronized(connectOpenMutex) { + try { + connectOpenMutex.wait(timeoutMS); + } catch (InterruptedException e) { + return false; + } + } + return playConnectState.isOpen(); + } + + @Override + public boolean isOpen() { + return playConnectState.isOpen(); + } + + @Override + public boolean isClosed() { + return playConnectState.isClosed(); + } + + @Override + public void close() { + if(!playConnectState.isClosed()) { + try { + clientImpl.closeBlocking(); + } catch (InterruptedException e) { + } + playConnectState = EnumEaglerConnectionState.CLOSED; + } + } + + @Override + public void send(String str) { + if(clientImpl.isClosed()) { + logger.error("[{}]: Client tried to send {} char packet while the socket was closed!", currentURIStr, str.length()); + }else { + clientImpl.send(str); + } + } + + @Override + public void send(byte[] bytes) { + if(clientImpl.isClosed()) { + logger.error("[{}]: Client tried to send {} byte packet while the socket was closed!", currentURIStr, bytes.length); + }else { + clientImpl.send(bytes); + } + } + + public void handleString(String str) { + addRecievedFrame(new DesktopWebSocketFrameString(str)); + } + + public void handleBytes(byte[] array) { + addRecievedFrame(new DesktopWebSocketFrameBinary(array)); + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketFrameBinary.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketFrameBinary.java new file mode 100755 index 0000000..951cbb1 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketFrameBinary.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.io.InputStream; + +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class DesktopWebSocketFrameBinary implements IWebSocketFrame { + + private final byte[] byteArray; + private final long timestamp; + + public DesktopWebSocketFrameBinary(byte[] byteArray) { + this.byteArray = byteArray; + this.timestamp = PlatformRuntime.steadyTimeMillis(); + } + + @Override + public boolean isString() { + return false; + } + + @Override + public String getString() { + return null; + } + + @Override + public byte[] getByteArray() { + return byteArray; + } + + @Override + public InputStream getInputStream() { + return new EaglerInputStream(byteArray); + } + + @Override + public int getLength() { + return byteArray.length; + } + + @Override + public long getTimestamp() { + return timestamp; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketFrameString.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketFrameString.java new file mode 100755 index 0000000..12e1b11 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopWebSocketFrameString.java @@ -0,0 +1,63 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.io.InputStream; + +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class DesktopWebSocketFrameString implements IWebSocketFrame { + + private final String string; + private final long timestamp; + + public DesktopWebSocketFrameString(String string) { + this.string = string; + this.timestamp = PlatformRuntime.steadyTimeMillis(); + } + + @Override + public boolean isString() { + return true; + } + + @Override + public String getString() { + return string; + } + + @Override + public byte[] getByteArray() { + return null; + } + + @Override + public InputStream getInputStream() { + return null; + } + + @Override + public int getLength() { + return string.length(); + } + + @Override + public long getTimestamp() { + return timestamp; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewHTTPD.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewHTTPD.java new file mode 100755 index 0000000..0875d24 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewHTTPD.java @@ -0,0 +1,41 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import fi.iki.elonen.NanoHTTPD; +import fi.iki.elonen.NanoHTTPD.Response.Status; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class FallbackWebViewHTTPD extends NanoHTTPD { + + static final Logger logger = FallbackWebViewServer.logger; + + private String index; + + FallbackWebViewHTTPD(String hostname, int port, String index) { + super(hostname, port); + this.index = index; + } + + @Override + public Response serve(IHTTPSession session) { + if("/RTWebViewClient".equals(session.getUri())) { + return newFixedLengthResponse(Status.OK, MIME_HTML, index); + }else { + return newFixedLengthResponse(Status.NOT_FOUND, MIME_HTML, "Eaglercraft Desktop Runtime

404 Not Found

"); + } + } +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewProtocol.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewProtocol.java new file mode 100755 index 0000000..6d55b84 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewProtocol.java @@ -0,0 +1,299 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONException; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class FallbackWebViewProtocol { + + static final Logger logger = FallbackWebViewServer.logger; + + private static final Map> packetIDToClass = new HashMap<>(); + private static final Map,Integer> packetClassToID = new HashMap<>(); + + private static final int CLIENT_TO_SERVER = 0; + private static final int SERVER_TO_CLIENT = 1; + + static { + register(0x00, CLIENT_TO_SERVER, CPacketClientHandshake.class); + register(0x01, SERVER_TO_CLIENT, SPacketServerHandshake.class); + register(0x02, SERVER_TO_CLIENT, SPacketServerError.class); + register(0x03, CLIENT_TO_SERVER, CPacketWebViewChannelOpen.class); + register(0x04, CLIENT_TO_SERVER, CPacketWebViewChannelClose.class); + register(0x05, CLIENT_TO_SERVER, CPacketWebViewMessage.class); + register(0x06, SERVER_TO_CLIENT, SPacketWebViewMessage.class); + register(0x07, CLIENT_TO_SERVER, CPacketWebViewJSPermission.class); + } + + private static void register(int id, int dir, Class packet) { + if(dir == CLIENT_TO_SERVER) { + packetIDToClass.put(id, packet); + }else if(dir == SERVER_TO_CLIENT) { + packetClassToID.put(packet, id); + }else { + throw new IllegalArgumentException(); + } + } + + static String writePacket(FallbackWebViewPacket packet) { + Class cls = packet.getClass(); + Integer id = packetClassToID.get(cls); + if(id == null) { + throw new RuntimeException("Tried to send unknown packet to client: " + cls.getSimpleName()); + } + JSONObject json = new JSONObject(); + json.put("$", id); + packet.writePacket(json); + return json.toString(); + } + + static FallbackWebViewPacket readPacket(String data) { + try { + JSONObject json = new JSONObject(data); + int id = json.getInt("$"); + Class cls = packetIDToClass.get(id); + if(cls == null) { + logger.error("Unknown packet ID {} recieved from webview controller", id); + return null; + } + FallbackWebViewPacket ret; + try { + ret = cls.newInstance(); + }catch(Throwable t) { + throw new RuntimeException("Failed to call packet constructor for \"" + cls.getSimpleName() + "\"! (is it defined?)"); + } + ret.readPacket(json); + return ret; + }catch(Throwable ex) { + logger.error("Failed to parse message from webview controller: \"{}\"", data); + logger.error(ex); + return null; + } + } + + static interface FallbackWebViewPacket { + + void readPacket(JSONObject json); + + void writePacket(JSONObject json); + + } + + static class CPacketClientHandshake implements FallbackWebViewPacket { + + public boolean cspSupport; + + public CPacketClientHandshake() { + } + + @Override + public void readPacket(JSONObject json) { + cspSupport = json.getBoolean("cspSupport"); + } + + @Override + public void writePacket(JSONObject json) { + throw new UnsupportedOperationException("Client only!"); + } + + } + + static class CPacketWebViewChannelOpen implements FallbackWebViewPacket { + + public String messageChannel; + + public CPacketWebViewChannelOpen() { + } + + public CPacketWebViewChannelOpen(String messageChannel) { + this.messageChannel = messageChannel; + } + + @Override + public void readPacket(JSONObject json) { + messageChannel = json.getString("channel"); + if(messageChannel.length() > 255) { + throw new JSONException("Channel name too long!"); + } + } + + @Override + public void writePacket(JSONObject json) { + throw new UnsupportedOperationException("Client only!"); + } + + } + + static class CPacketWebViewChannelClose implements FallbackWebViewPacket { + + public CPacketWebViewChannelClose() { + } + + @Override + public void readPacket(JSONObject json) { + + } + + @Override + public void writePacket(JSONObject json) { + throw new UnsupportedOperationException("Client only!"); + } + + } + + // for string messages, binary are sent as a binary frame + static class CPacketWebViewMessage implements FallbackWebViewPacket { + + public String messageContent; + + public CPacketWebViewMessage() { + } + + public CPacketWebViewMessage(String messageContent) { + this.messageContent = messageContent; + } + + @Override + public void readPacket(JSONObject json) { + messageContent = json.getString("msg"); + } + + @Override + public void writePacket(JSONObject json) { + throw new UnsupportedOperationException("Client only!"); + } + + } + + static class SPacketServerHandshake implements FallbackWebViewPacket { + + public WebViewOptions options; + public EnumWebViewJSPermission hasApprovedJS; + + public SPacketServerHandshake() { + } + + public SPacketServerHandshake(WebViewOptions options, EnumWebViewJSPermission hasApprovedJS) { + this.options = options; + this.hasApprovedJS = hasApprovedJS; + } + + @Override + public void readPacket(JSONObject json) { + throw new UnsupportedOperationException("Server only!"); + } + + @Override + public void writePacket(JSONObject json) { + json.put("contentMode", options.contentMode.toString()); + json.put("fallbackTitle", options.fallbackTitle); + json.put("scriptEnabled", options.scriptEnabled); + json.put("strictCSPEnable", options.strictCSPEnable); + json.put("serverMessageAPIEnabled", options.serverMessageAPIEnabled); + json.put("url", options.url); + json.put("blob", options.blob != null ? new String(options.blob, StandardCharsets.UTF_8) : null); + json.put("hasApprovedJS", hasApprovedJS.toString()); + } + + } + + static class SPacketServerError implements FallbackWebViewPacket { + + public String errorMessage; + + public SPacketServerError() { + } + + public SPacketServerError(String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public void readPacket(JSONObject json) { + throw new UnsupportedOperationException("Server only!"); + } + + @Override + public void writePacket(JSONObject json) { + json.put("msg", errorMessage); + } + + } + + static class SPacketWebViewMessage implements FallbackWebViewPacket { + + public String message; + + public SPacketWebViewMessage() { + } + + public SPacketWebViewMessage(String message) { + this.message = message; + } + + @Override + public void readPacket(JSONObject json) { + throw new UnsupportedOperationException("Server only!"); + } + + @Override + public void writePacket(JSONObject json) { + json.put("msg", message); + } + + } + + static enum EnumWebViewJSPermission { + NOT_SET, ALLOW, BLOCK; + + static EnumWebViewJSPermission fromPermission(PermissionsCache.Permission perm) { + if(perm != null) { + return perm.choice ? ALLOW : BLOCK; + }else { + return NOT_SET; + } + } + } + + static class CPacketWebViewJSPermission implements FallbackWebViewPacket { + + public EnumWebViewJSPermission permission; + + public CPacketWebViewJSPermission() { + } + + @Override + public void readPacket(JSONObject json) { + permission = EnumWebViewJSPermission.valueOf(json.getString("perm")); + } + + @Override + public void writePacket(JSONObject json) { + throw new UnsupportedOperationException("Client only!"); + } + + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewServer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewServer.java new file mode 100755 index 0000000..9bbce4f --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewServer.java @@ -0,0 +1,190 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.nio.charset.StandardCharsets; + +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class FallbackWebViewServer { + + static final Logger logger = LogManager.getLogger("FallbackWebViewServer"); + + public static final String LISTEN_ADDR = "127.69.69.69"; + + public static final File webViewClientHTML = new File("RTWebViewClient.html"); + + public final WebViewOptions options; + + private FallbackWebViewWSD websocketServer; + private FallbackWebViewHTTPD httpServer; + + private String currentURL; + private volatile boolean dead; + + private IPacketSendCallback callback = null; + + public FallbackWebViewServer(WebViewOptions options) { + this.options = options; + } + + public void start() throws RuntimeException { + dead = false; + StringBuilder vigg = new StringBuilder(); + try(BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(webViewClientHTML), StandardCharsets.UTF_8))) { + String line; + while((line = reader.readLine()) != null) { + vigg.append(line).append('\n'); + } + }catch(IOException ex) { + logger.error("Failed to read \"{}\"!"); + } + String indexHTML = vigg.toString(); + + Object mutex = new Object(); + websocketServer = new FallbackWebViewWSD(LISTEN_ADDR, randomPort(), options); + websocketServer.setEaglerPacketSendCallback(callback); + synchronized(mutex) { + websocketServer.doStartup(mutex); + try { + mutex.wait(5000l); + } catch (InterruptedException e) { + } + } + if(!websocketServer.hasStarted) { + logger.error("Failed to start WebSocket in time!"); + try { + websocketServer.stop(5000); + }catch(Throwable t) { + } + websocketServer = null; + throw new RuntimeException("Failed to start WebSocket server!"); + } + InetSocketAddress addr = websocketServer.getAddress(); + String wsAddr = "ws://" + addr.getHostString() + ":" + addr.getPort() + "/"; + logger.info("Listening for WebSocket on {}", wsAddr); + indexHTML = indexHTML.replace("${client_websocket_uri}", wsAddr); + + JSONObject optsExport = new JSONObject(); + IClientConfigAdapter cfgAdapter = PlatformRuntime.getClientConfigAdapter(); + optsExport.put("forceWebViewSupport", cfgAdapter.isForceWebViewSupport()); + optsExport.put("enableWebViewCSP", cfgAdapter.isEnableWebViewCSP()); + indexHTML = indexHTML.replace("{eaglercraftXOpts}", optsExport.toString()); + + httpServer = new FallbackWebViewHTTPD(LISTEN_ADDR, 0, indexHTML); + try { + httpServer.start(5000, true); + } catch (IOException e) { + logger.error("Failed to start NanoHTTPD!"); + try { + websocketServer.stop(5000); + }catch(Throwable t) { + } + websocketServer = null; + httpServer = null; + throw new RuntimeException("Failed to start NanoHTTPD!", e); + } + int httpPort = httpServer.getListeningPort(); + currentURL = "http://" + LISTEN_ADDR + ":" + httpPort + "/RTWebViewClient"; + logger.info("Listening for HTTP on {}", currentURL); + } + + private int randomPort() { + try(ServerSocket sockler = new ServerSocket(0)) { + return sockler.getLocalPort(); + }catch(IOException ex) { + throw new RuntimeException("Failed to find random port to bind to!", ex); + } + } + + public boolean isDead() { + return dead; + } + + public String getURL() { + return !dead ? currentURL : null; + } + + public void handleMessageFromServer(SPacketWebViewMessageV4EAG packet) { + if(packet.type == SPacketWebViewMessageV4EAG.TYPE_STRING) { + if(websocketServer != null) { + websocketServer.handleServerMessageStr(new String(packet.data, StandardCharsets.UTF_8)); + }else { + logger.error("Recieved string message, but the webview server is not running!"); + } + }else if(packet.type == SPacketWebViewMessageV4EAG.TYPE_BINARY) { + if(websocketServer != null) { + websocketServer.handleServerMessageBytes(packet.data); + }else { + logger.error("Recieved string message, but the webview server is not running!"); + } + }else { + logger.error("Unknown server webview message type {}", packet.type); + } + } + + public void setPacketSendCallback(IPacketSendCallback callback) { + this.callback = callback; + if(websocketServer != null) { + websocketServer.setEaglerPacketSendCallback(callback); + } + } + + public void runTick() { + + } + + public void killServer() { + if(!dead) { + dead = true; + if(websocketServer != null) { + try { + websocketServer.stop(10000); + } catch (Throwable th) { + logger.error("Failed to stop WebSocket server, aborting"); + logger.error(th); + } + websocketServer = null; + } + if(httpServer != null) { + try { + httpServer.stop(); + } catch (Throwable th) { + logger.error("Failed to stop HTTP server, aborting"); + logger.error(th); + } + httpServer = null; + } + } + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewWSD.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewWSD.java new file mode 100755 index 0000000..3d8b343 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FallbackWebViewWSD.java @@ -0,0 +1,273 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageEnV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageV4EAG; +import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache; +import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache.Permission; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback; + +import static net.lax1dude.eaglercraft.v1_8.internal.lwjgl.FallbackWebViewProtocol.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class FallbackWebViewWSD extends WebSocketServer { + + static final Logger logger = FallbackWebViewServer.logger; + + private Object onStartNotify; + + volatile boolean hasStarted = false; + + private final Object webSockMutex = new Object(); + volatile WebSocket webSocket = null; + + volatile boolean hasHandshake = false; + volatile String currentChannelName = null; + + private IPacketSendCallback callback = null; + WebViewOptions options; + + private boolean enableCSP; + private boolean cspSupport; + + FallbackWebViewWSD(String address, int port, WebViewOptions options) { + super(new InetSocketAddress(address, port), 1); + this.setTcpNoDelay(true); + this.setReuseAddr(true); + this.setConnectionLostTimeout(30); + this.options = options; + this.enableCSP = PlatformRuntime.getClientConfigAdapter().isEnableWebViewCSP(); + this.cspSupport = true; + } + + public void doStartup(Object onStartNotify) { + this.onStartNotify = onStartNotify; + this.start(); + } + + private void handleOpen() { + hasHandshake = false; + currentChannelName = null; + } + + private void handleClose() { + if(currentChannelName != null && callback != null) { + callback.sendPacket(new CPacketWebViewMessageEnV4EAG(false, null)); + } + currentChannelName = null; + } + + private int hashPermissionFlags() { + int i = (options.scriptEnabled ? 1 : 0); + i |= ((enableCSP && cspSupport && options.strictCSPEnable) ? 0 : 2); + i |= (options.serverMessageAPIEnabled ? 4 : 0); + return i; + } + + private void handleMessage(String str) { + WebSocket ws = webSocket; + FallbackWebViewPacket _packet = readPacket(str); + if(_packet != null) { + if(!hasHandshake) { + if(_packet instanceof CPacketClientHandshake) { + hasHandshake = true; + Permission perm = PermissionsCache.getJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags()); + ws.send(writePacket(new SPacketServerHandshake(options, EnumWebViewJSPermission.fromPermission(perm)))); + }else { + terminate("Unknown or unexpected packet: " + _packet.getClass().getSimpleName()); + } + }else { + if(_packet instanceof CPacketWebViewChannelOpen) { + CPacketWebViewChannelOpen packet = (CPacketWebViewChannelOpen)_packet; + if(currentChannelName == null) { + currentChannelName = packet.messageChannel; + logger.info("[{}]: opened WebView channel \"{}\"", ws.getRemoteSocketAddress(), packet.messageChannel); + safeCallbackSend(new CPacketWebViewMessageEnV4EAG(true, packet.messageChannel)); + }else { + terminate("Tried to open multiple channels"); + } + }else if(_packet instanceof CPacketWebViewMessage) { + CPacketWebViewMessage packet = (CPacketWebViewMessage)_packet; + if(currentChannelName != null) { + safeCallbackSend(new CPacketWebViewMessageV4EAG(packet.messageContent)); + }else { + terminate("Tried to send message without opening channel"); + } + }else if(_packet instanceof CPacketWebViewChannelClose) { + if(currentChannelName != null) { + currentChannelName = null; + safeCallbackSend(new CPacketWebViewMessageEnV4EAG(false, null)); + }else { + terminate("Tried to close missing channel"); + } + }else if(_packet instanceof CPacketWebViewJSPermission) { + CPacketWebViewJSPermission packet = (CPacketWebViewJSPermission)_packet; + switch(packet.permission) { + case NOT_SET: + PermissionsCache.clearJavaScriptAllowed(options.permissionsOriginUUID); + break; + case ALLOW: + PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(), true); + break; + case BLOCK: + PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(), false); + break; + default: + terminate("Unknown permission state selected!"); + break; + } + + }else { + terminate("Unknown or unexpected packet: " + _packet.getClass().getSimpleName()); + } + } + }else { + terminate("Invalid packet recieved"); + } + } + + private void handleMessage(ByteBuffer buffer) { + if(currentChannelName != null) { + safeCallbackSend(new CPacketWebViewMessageV4EAG(buffer.array())); + }else { + terminate("Sent binary webview message while channel was closed"); + } + } + + private void terminate(String msg) { + if(webSocket != null) { + logger.error("[{}]: Terminating connection, reason: \"{}\"", webSocket.getRemoteSocketAddress(), msg); + webSocket.send(writePacket(new SPacketServerError(msg))); + webSocket.close(); + } + } + + private void safeCallbackSend(GameMessagePacket packet) { + if(callback != null) { + callback.sendPacket(packet); + }else { + logger.error("webview sent packet to server, but there's no callback registered to send packets!"); + } + } + + void handleServerMessageStr(String msg) { + if(webSocket != null) { + if(currentChannelName != null) { + webSocket.send(writePacket(new SPacketWebViewMessage(msg))); + }else { + logger.error("Recieved string message from server, but the channel is not open!"); + } + }else { + logger.error("Recieved string message from server, but there is no active websocket!"); + } + } + + void handleServerMessageBytes(byte[] msg) { + if(webSocket != null) { + if(currentChannelName != null) { + webSocket.send(msg); + }else { + logger.error("Recieved binary message from server, but the channel is not open!"); + } + }else { + logger.error("Recieved binary message from server, but there is no active websocket!"); + } + } + + @Override + public void onStart() { + hasStarted = true; + if(onStartNotify != null) { + synchronized(onStartNotify) { + onStartNotify.notifyAll(); + } + onStartNotify = null; + }else { + logger.warn("No mutex to notify!"); + } + } + + @Override + public void onOpen(WebSocket arg0, ClientHandshake arg1) { + boolean result; + synchronized(webSockMutex) { + if(webSocket == null) { + webSocket = arg0; + result = true; + }else { + result = false; + } + } + if(result) { + logger.info("[{}]: WebSocket connection opened", arg0.getRemoteSocketAddress()); + handleOpen(); + }else { + logger.error("[{}]: Rejecting duplicate connection", arg0.getRemoteSocketAddress()); + arg0.send(writePacket(new SPacketServerError("You already have a tab open!"))); + arg0.close(); + } + } + + @Override + public void onMessage(WebSocket arg0, String arg1) { + if(arg0 == webSocket) { + handleMessage(arg1); + } + } + + @Override + public void onMessage(WebSocket arg0, ByteBuffer arg1) { + if(arg0 == webSocket) { + handleMessage(arg1); + } + } + + @Override + public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) { + synchronized(webSockMutex) { + if(arg0 == webSocket) { + logger.info("[{}]: WebSocket connection closed", arg0.getRemoteSocketAddress()); + try { + handleClose(); + }finally { + webSocket = null; + } + } + } + } + + @Override + public void onError(WebSocket arg0, Exception arg1) { + logger.error("[{}]: WebSocket caught exception", arg0 != null ? arg0.getRemoteSocketAddress() : "null"); + logger.error(arg1); + } + + public void setEaglerPacketSendCallback(IPacketSendCallback callback) { + this.callback = callback; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java index d47351b..92e199c 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java @@ -1,441 +1,459 @@ -package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; - -import java.sql.Connection; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Collection; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.Map.Entry; -import java.util.Properties; - -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.IFilesystemProvider; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; -import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class JDBCFilesystem implements IFilesystemProvider { - - public static final Logger logger = LogManager.getLogger("JDBCFilesystem"); - - private boolean newFilesystem = true; - - private static volatile boolean cleanupThreadStarted = false; - private static final Collection jdbcFilesystems = new LinkedList(); - - private final String jdbcUri; - private final String jdbcDriver; - - private final Connection conn; - private final PreparedStatement createStatement; - private final PreparedStatement updateStatement; - private final PreparedStatement readStatement; - private final PreparedStatement existsStatement; - private final PreparedStatement sizeStatement; - private final PreparedStatement deleteStatement; - private final PreparedStatement renameStatement; - private final PreparedStatement iterateNonRecursive; - private final PreparedStatement iterateRecursive; - private boolean hasClosed = false; - - private final Object mutex = new Object(); - - public static IFilesystemProvider initialize(String jdbcUri, String jdbcDriver) { - Class driver; - try { - driver = Class.forName(jdbcDriver); - } catch (ClassNotFoundException e) { - throw new EaglerFileSystemException("JDBC driver class not found in JRE: " + jdbcDriver, e); - } - Driver driverObj = null; - Enumeration registeredDriversItr = DriverManager.getDrivers(); - while(registeredDriversItr.hasMoreElements()) { - Driver drv = registeredDriversItr.nextElement(); - if(drv.getClass().equals(driver)) { - driverObj = drv; - break; - } - } - if(driverObj == null) { - logger.warn("The class \"{}\" is not a registered JDBC driver, eaglercraft will try all registered drivers...", jdbcDriver); - } - Properties props = new Properties(); - for(Entry etr : System.getProperties().entrySet()) { - if(etr.getKey() instanceof String) { - String str = (String)etr.getKey(); - if(str.startsWith("eagler.jdbc.opts.")) { - props.put(str.substring(17), etr.getValue()); - } - } - } - logger.info("Connecting to database: \"{}\"", jdbcUri); - Connection conn; - try { - if(driverObj != null) { - conn = driverObj.connect(jdbcUri, props); - }else { - conn = DriverManager.getConnection(jdbcUri, props); - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("Failed to connect to database: \"" + jdbcUri + "\"", ex); - } - try { - return new JDBCFilesystem(conn, jdbcUri, jdbcDriver); - } catch (SQLException ex) { - try { - conn.close(); - }catch(SQLException ex2) { - } - throw new EaglerFileSystemException("Failed to initialize database: \"" + jdbcUri + "\"", ex); - } - } - - private JDBCFilesystem(Connection conn, String jdbcUri, String jdbcDriver) throws SQLException { - this.conn = conn; - this.jdbcUri = jdbcUri; - this.jdbcDriver = jdbcDriver; - try(Statement stmt = conn.createStatement()) { - stmt.execute("CREATE TABLE IF NOT EXISTS " - + "\"eaglercraft_desktop_runtime_filesystem\" (" - + "\"FileName\" VARCHAR(1024) NOT NULL," - + "\"FileSize\" INT NOT NULL," - + "\"FileData\" BLOB NOT NULL," - + "PRIMARY KEY(\"FileName\"))"); - - int totalFiles = 0; - try(ResultSet resultSet = stmt.executeQuery("SELECT COUNT(*) AS total_files FROM eaglercraft_desktop_runtime_filesystem")) { - if(resultSet.next()) { - totalFiles = resultSet.getInt(1); - } - } - logger.info("Loaded JDBC filesystem with {} files: \"{}\"", totalFiles, jdbcUri); - if(totalFiles > 0) { - newFilesystem = false; - } - } - this.createStatement = conn.prepareStatement("INSERT INTO eaglercraft_desktop_runtime_filesystem (FileName, FileSize, FileData) VALUES(?,?,?)"); - this.updateStatement = conn.prepareStatement("UPDATE eaglercraft_desktop_runtime_filesystem SET FileSize = ?, FileData = ? WHERE FileName = ?"); - this.readStatement = conn.prepareStatement("SELECT FileData FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); - this.existsStatement = conn.prepareStatement("SELECT COUNT(FileName) AS has_object FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); - this.sizeStatement = conn.prepareStatement("SELECT FileSize FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); - this.deleteStatement = conn.prepareStatement("DELETE FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ?"); - this.renameStatement = conn.prepareStatement("UPDATE eaglercraft_desktop_runtime_filesystem SET FileName = ? WHERE FileName = ?"); - this.iterateNonRecursive = conn.prepareStatement("SELECT FileName FROM eaglercraft_desktop_runtime_filesystem WHERE FileName LIKE ? AND NOT FileName LIKE ?"); - this.iterateRecursive = conn.prepareStatement("SELECT FileName FROM eaglercraft_desktop_runtime_filesystem WHERE FileName LIKE ?"); - startCleanupThread(); - synchronized(jdbcFilesystems) { - jdbcFilesystems.add(this); - } - } - - public boolean isNewFilesystem() { - return newFilesystem; - } - - private static void startCleanupThread() { - if(!cleanupThreadStarted) { - cleanupThreadStarted = true; - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - synchronized(jdbcFilesystems) { - if(!jdbcFilesystems.isEmpty()) { - for(JDBCFilesystem fs : jdbcFilesystems) { - fs.shutdown0(); - } - jdbcFilesystems.clear(); - } - } - }, "JDBCFilesystemCleanup")); - } - } - - public void shutdown() { - shutdown0(); - synchronized(jdbcFilesystems) { - jdbcFilesystems.remove(this); - } - } - - private void shutdown0() { - synchronized(mutex) { - if(!hasClosed) { - hasClosed = true; - logger.info("Disconnecting from database: \"{}\"", jdbcUri); - try { - shutdown1(); - }catch(Throwable t) { - logger.error("Failed to disconnect from database: \"{}\""); - logger.error(t); - } - } - } - } - - private void shutdown1() throws SQLException { - if(!conn.isClosed()) { - quietClose(createStatement); - quietClose(updateStatement); - quietClose(readStatement); - quietClose(existsStatement); - quietClose(sizeStatement); - quietClose(deleteStatement); - quietClose(renameStatement); - quietClose(iterateNonRecursive); - quietClose(iterateRecursive); - conn.close(); - } - } - - private static void quietClose(Statement stmt) { - try { - stmt.close(); - }catch(Throwable t) { - } - } - - @Override - public boolean eaglerDelete(String pathName) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - deleteStatement.setString(1, pathName); - int ret = deleteStatement.executeUpdate(); - if(ret == 0) { - PlatformFilesystem.logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); - } - return ret > 0; - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing delete!", ex); - } - } - - @Override - public ByteBuffer eaglerRead(String pathName) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - readStatement.setString(1, pathName); - byte[] has = null; - try(ResultSet resultSet = readStatement.executeQuery()) { - if(resultSet.next()) { - has = resultSet.getBytes(1); - } - } - if(has == null) { - PlatformFilesystem.logger.warn("Tried to read file that doesn't exist: \"{}\"", pathName); - return null; - } - ByteBuffer byteBuf = PlatformRuntime.allocateByteBuffer(has.length); - byteBuf.put(has); - byteBuf.flip(); - return byteBuf; - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing read!", ex); - } - } - - @Override - public void eaglerWrite(String pathName, ByteBuffer data) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - existsStatement.setString(1, pathName); - boolean exists; - try(ResultSet resultSet = existsStatement.executeQuery()) { - if(resultSet.next()) { - exists = resultSet.getInt(1) > 0; - }else { - exists = false; - } - } - byte[] cp = new byte[data.remaining()]; - data.get(cp); - if(exists) { - updateStatement.setInt(1, cp.length); - updateStatement.setBytes(2, cp); - updateStatement.setString(3, pathName); - if(updateStatement.executeUpdate() == 0) { - throw new EaglerFileSystemException("SQL file update query did not update any rows!"); - } - }else { - createStatement.setString(1, pathName); - createStatement.setInt(2, cp.length); - createStatement.setBytes(3, cp); - createStatement.executeUpdate(); - } - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing write!", ex); - } - } - - @Override - public boolean eaglerExists(String pathName) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - existsStatement.setString(1, pathName); - try(ResultSet resultSet = existsStatement.executeQuery()) { - if(resultSet.next()) { - return resultSet.getInt(1) > 0; - }else { - return false; - } - } - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing exists!", ex); - } - } - - @Override - public boolean eaglerMove(String pathNameOld, String pathNameNew) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - renameStatement.setString(1, pathNameNew); - renameStatement.setString(2, pathNameOld); - return renameStatement.executeUpdate() > 0; - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing move!", ex); - } - } - - @Override - public int eaglerCopy(String pathNameOld, String pathNameNew) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - readStatement.setString(1, pathNameOld); - try(ResultSet resultSet = readStatement.executeQuery()) { - byte[] has = null; - if(resultSet.next()) { - has = resultSet.getBytes(1); - } - if(has == null) { - return -1; - } - existsStatement.setString(1, pathNameNew); - boolean exists; - try(ResultSet resultSet2 = existsStatement.executeQuery()) { - if(resultSet2.next()) { - exists = resultSet2.getInt(1) > 0; - }else { - exists = false; - } - } - if(exists) { - updateStatement.setInt(1, has.length); - updateStatement.setBytes(2, has); - updateStatement.setString(3, pathNameNew); - if(updateStatement.executeUpdate() == 0) { - throw new EaglerFileSystemException("SQL file update query did not update any rows!"); - } - }else { - createStatement.setString(1, pathNameNew); - createStatement.setInt(2, has.length); - createStatement.setBytes(3, has); - createStatement.executeUpdate(); - } - return has.length; - } - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing copy!", ex); - } - } - - @Override - public int eaglerSize(String pathName) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - sizeStatement.setString(1, pathName); - try(ResultSet resultSet = sizeStatement.executeQuery()) { - if(resultSet.next()) { - return resultSet.getInt(1); - }else { - return -1; - } - } - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing size!", ex); - } - } - - @Override - public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { - try { - synchronized(mutex) { - if(hasClosed || conn.isClosed()) { - throw new SQLException("Filesystem database connection is closed!"); - } - PreparedStatement stmt; - if(recursive) { - stmt = iterateRecursive; - stmt.setString(1, pathName + (!pathName.endsWith("/") ? "/%" : "%"));; - }else { - stmt = iterateNonRecursive; - if(!pathName.endsWith("/")) { - pathName += "/"; - } - stmt.setString(1, pathName + "%"); - stmt.setString(2, pathName + "%/%"); - } - try(ResultSet resultSet = stmt.executeQuery()) { - while(resultSet.next()) { - try { - itr.next(resultSet.getString(1)); - }catch(VFSIterator2.BreakLoop exx) { - break; - } - } - } - } - }catch(SQLException ex) { - throw new EaglerFileSystemException("JDBC exception thrown while executing iterate!", ex); - } - } - -} \ No newline at end of file +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collection; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.Map.Entry; +import java.util.Properties; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class JDBCFilesystem implements IEaglerFilesystem { + + public static final Logger logger = LogManager.getLogger("JDBCFilesystem"); + + private boolean newFilesystem = true; + + private static volatile boolean cleanupThreadStarted = false; + private static final Collection jdbcFilesystems = new LinkedList<>(); + + private final String dbName; + private final String jdbcUri; + private final String jdbcDriver; + + private final Connection conn; + private final PreparedStatement createStatement; + private final PreparedStatement updateStatement; + private final PreparedStatement readStatement; + private final PreparedStatement existsStatement; + private final PreparedStatement sizeStatement; + private final PreparedStatement deleteStatement; + private final PreparedStatement renameStatement; + private final PreparedStatement iterateNonRecursive; + private final PreparedStatement iterateRecursive; + private boolean hasClosed = false; + + private final Object mutex = new Object(); + + public static IEaglerFilesystem initialize(String dbName, String jdbcUri, String jdbcDriver) { + Class driver; + try { + driver = Class.forName(jdbcDriver); + } catch (ClassNotFoundException e) { + throw new EaglerFileSystemException("JDBC driver class not found in JRE: " + jdbcDriver, e); + } + Driver driverObj = null; + Enumeration registeredDriversItr = DriverManager.getDrivers(); + while(registeredDriversItr.hasMoreElements()) { + Driver drv = registeredDriversItr.nextElement(); + if(drv.getClass().equals(driver)) { + driverObj = drv; + break; + } + } + if(driverObj == null) { + logger.warn("The class \"{}\" is not a registered JDBC driver, eaglercraft will try all registered drivers...", jdbcDriver); + } + Properties props = new Properties(); + for(Entry etr : System.getProperties().entrySet()) { + if(etr.getKey() instanceof String) { + String str = (String)etr.getKey(); + if(str.startsWith("eagler.jdbc." + dbName + ".opts.")) { + props.put(str.substring(18 + dbName.length()), etr.getValue()); + } + } + } + logger.info("Connecting to database: \"{}\"", jdbcUri); + Connection conn; + try { + if(driverObj != null) { + conn = driverObj.connect(jdbcUri, props); + }else { + conn = DriverManager.getConnection(jdbcUri, props); + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("Failed to connect to database: \"" + jdbcUri + "\"", ex); + } + try { + return new JDBCFilesystem(dbName, conn, jdbcUri, jdbcDriver); + } catch (SQLException ex) { + try { + conn.close(); + }catch(SQLException ex2) { + } + throw new EaglerFileSystemException("Failed to initialize database: \"" + jdbcUri + "\"", ex); + } + } + + private JDBCFilesystem(String dbName, Connection conn, String jdbcUri, String jdbcDriver) throws SQLException { + this.dbName = dbName; + this.conn = conn; + this.jdbcUri = jdbcUri; + this.jdbcDriver = jdbcDriver; + try(Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE IF NOT EXISTS " + + "\"eaglercraft_desktop_runtime_filesystem\" (" + + "\"FileName\" VARCHAR(1024) NOT NULL," + + "\"FileSize\" INT NOT NULL," + + "\"FileData\" BLOB NOT NULL," + + "PRIMARY KEY(\"FileName\"))"); + + int totalFiles = 0; + try(ResultSet resultSet = stmt.executeQuery("SELECT COUNT(*) AS total_files FROM eaglercraft_desktop_runtime_filesystem")) { + if(resultSet.next()) { + totalFiles = resultSet.getInt(1); + } + } + logger.info("Loaded JDBC filesystem with {} files: \"{}\"", totalFiles, jdbcUri); + if(totalFiles > 0) { + newFilesystem = false; + } + } + this.createStatement = conn.prepareStatement("INSERT INTO eaglercraft_desktop_runtime_filesystem (FileName, FileSize, FileData) VALUES(?,?,?)"); + this.updateStatement = conn.prepareStatement("UPDATE eaglercraft_desktop_runtime_filesystem SET FileSize = ?, FileData = ? WHERE FileName = ?"); + this.readStatement = conn.prepareStatement("SELECT FileData FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); + this.existsStatement = conn.prepareStatement("SELECT COUNT(FileName) AS has_object FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); + this.sizeStatement = conn.prepareStatement("SELECT FileSize FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); + this.deleteStatement = conn.prepareStatement("DELETE FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ?"); + this.renameStatement = conn.prepareStatement("UPDATE eaglercraft_desktop_runtime_filesystem SET FileName = ? WHERE FileName = ?"); + this.iterateNonRecursive = conn.prepareStatement("SELECT FileName FROM eaglercraft_desktop_runtime_filesystem WHERE FileName LIKE ? AND NOT FileName LIKE ?"); + this.iterateRecursive = conn.prepareStatement("SELECT FileName FROM eaglercraft_desktop_runtime_filesystem WHERE FileName LIKE ?"); + startCleanupThread(); + synchronized(jdbcFilesystems) { + jdbcFilesystems.add(this); + } + } + + @Override + public String getFilesystemName() { + return dbName; + } + + @Override + public String getInternalDBName() { + return "desktopruntime:" + jdbcUri; + } + + public boolean isNewFilesystem() { + return newFilesystem; + } + + private static void startCleanupThread() { + if(!cleanupThreadStarted) { + cleanupThreadStarted = true; + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + synchronized(jdbcFilesystems) { + if(!jdbcFilesystems.isEmpty()) { + for(JDBCFilesystem fs : jdbcFilesystems) { + fs.shutdown0(); + } + jdbcFilesystems.clear(); + } + } + }, "JDBCFilesystemCleanup")); + } + } + + @Override + public void closeHandle() { + shutdown0(); + synchronized(jdbcFilesystems) { + jdbcFilesystems.remove(this); + } + } + + private void shutdown0() { + synchronized(mutex) { + if(!hasClosed) { + hasClosed = true; + logger.info("Disconnecting from database: \"{}\"", jdbcUri); + try { + shutdown1(); + }catch(Throwable t) { + logger.error("Failed to disconnect from database: \"{}\""); + logger.error(t); + } + } + } + } + + private void shutdown1() throws SQLException { + if(!conn.isClosed()) { + quietClose(createStatement); + quietClose(updateStatement); + quietClose(readStatement); + quietClose(existsStatement); + quietClose(sizeStatement); + quietClose(deleteStatement); + quietClose(renameStatement); + quietClose(iterateNonRecursive); + quietClose(iterateRecursive); + conn.close(); + } + } + + private static void quietClose(Statement stmt) { + try { + stmt.close(); + }catch(Throwable t) { + } + } + + @Override + public boolean eaglerDelete(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + deleteStatement.setString(1, pathName); + int ret = deleteStatement.executeUpdate(); + if(ret == 0) { + PlatformFilesystem.logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); + } + return ret > 0; + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing delete!", ex); + } + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + readStatement.setString(1, pathName); + byte[] has = null; + try(ResultSet resultSet = readStatement.executeQuery()) { + if(resultSet.next()) { + has = resultSet.getBytes(1); + } + } + if(has == null) { + PlatformFilesystem.logger.warn("Tried to read file that doesn't exist: \"{}\"", pathName); + return null; + } + ByteBuffer byteBuf = PlatformRuntime.allocateByteBuffer(has.length); + byteBuf.put(has); + byteBuf.flip(); + return byteBuf; + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing read!", ex); + } + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + existsStatement.setString(1, pathName); + boolean exists; + try(ResultSet resultSet = existsStatement.executeQuery()) { + if(resultSet.next()) { + exists = resultSet.getInt(1) > 0; + }else { + exists = false; + } + } + byte[] cp = new byte[data.remaining()]; + data.get(cp); + if(exists) { + updateStatement.setInt(1, cp.length); + updateStatement.setBytes(2, cp); + updateStatement.setString(3, pathName); + if(updateStatement.executeUpdate() == 0) { + throw new EaglerFileSystemException("SQL file update query did not update any rows!"); + } + }else { + createStatement.setString(1, pathName); + createStatement.setInt(2, cp.length); + createStatement.setBytes(3, cp); + createStatement.executeUpdate(); + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing write!", ex); + } + } + + @Override + public boolean eaglerExists(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + existsStatement.setString(1, pathName); + try(ResultSet resultSet = existsStatement.executeQuery()) { + if(resultSet.next()) { + return resultSet.getInt(1) > 0; + }else { + return false; + } + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing exists!", ex); + } + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + renameStatement.setString(1, pathNameNew); + renameStatement.setString(2, pathNameOld); + return renameStatement.executeUpdate() > 0; + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing move!", ex); + } + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + readStatement.setString(1, pathNameOld); + try(ResultSet resultSet = readStatement.executeQuery()) { + byte[] has = null; + if(resultSet.next()) { + has = resultSet.getBytes(1); + } + if(has == null) { + return -1; + } + existsStatement.setString(1, pathNameNew); + boolean exists; + try(ResultSet resultSet2 = existsStatement.executeQuery()) { + if(resultSet2.next()) { + exists = resultSet2.getInt(1) > 0; + }else { + exists = false; + } + } + if(exists) { + updateStatement.setInt(1, has.length); + updateStatement.setBytes(2, has); + updateStatement.setString(3, pathNameNew); + if(updateStatement.executeUpdate() == 0) { + throw new EaglerFileSystemException("SQL file update query did not update any rows!"); + } + }else { + createStatement.setString(1, pathNameNew); + createStatement.setInt(2, has.length); + createStatement.setBytes(3, has); + createStatement.executeUpdate(); + } + return has.length; + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing copy!", ex); + } + } + + @Override + public int eaglerSize(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + sizeStatement.setString(1, pathName); + try(ResultSet resultSet = sizeStatement.executeQuery()) { + if(resultSet.next()) { + return resultSet.getInt(1); + }else { + return -1; + } + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing size!", ex); + } + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + PreparedStatement stmt; + if(recursive) { + stmt = iterateRecursive; + stmt.setString(1, pathName + (!pathName.endsWith("/") ? "/%" : "%"));; + }else { + stmt = iterateNonRecursive; + if(!pathName.endsWith("/")) { + pathName += "/"; + } + stmt.setString(1, pathName + "%"); + stmt.setString(2, pathName + "%/%"); + } + try(ResultSet resultSet = stmt.executeQuery()) { + while(resultSet.next()) { + try { + itr.next(resultSet.getString(1)); + }catch(VFSIterator2.BreakLoop exx) { + break; + } + } + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing iterate!", ex); + } + } + + @Override + public boolean isRamdisk() { + return false; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java index cb5bd77..eef8d92 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java @@ -7,7 +7,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.IFilesystemProvider; +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; @@ -33,7 +33,7 @@ public class JDBCFilesystemConverter { private static final Logger logger = LogManager.getLogger("JDBCFilesystemConverter"); - public static void convertFilesystem(String title, File oldFS, IFilesystemProvider newFS, boolean deleteOld) { + public static void convertFilesystem(String title, File oldFS, IEaglerFilesystem newFS, boolean deleteOld) { FilesystemConvertingDialog progressDialog = new FilesystemConvertingDialog(title); try { progressDialog.setProgressIndeterminate(true); @@ -41,7 +41,7 @@ public class JDBCFilesystemConverter { progressDialog.setVisible(true); String slug = oldFS.getAbsolutePath(); - List filesToCopy = new ArrayList(); + List filesToCopy = new ArrayList<>(); logger.info("Discovering files to convert..."); iterateFolder(slug.length(), oldFS, filesToCopy); logger.info("Found {} files in the old directory", filesToCopy.size()); @@ -127,4 +127,4 @@ public class JDBCFilesystemConverter { } file.delete(); } -} \ No newline at end of file +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java index 849e836..23b1358 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java @@ -6,7 +6,6 @@ import javax.swing.UnsupportedLookAndFeelException; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource; @@ -82,11 +81,12 @@ public class LWJGLEntryPoint { PlatformInput.setStartupFullscreen(true); }else if(args[i].equalsIgnoreCase("highp")) { ShaderSource.setHighP(true); - }else if(args[i].startsWith("jdbc:")) { - if(i < args.length - 1) { - PlatformFilesystem.setUseJDBC(args[i]); - PlatformFilesystem.setJDBCDriverClass(args[++i]); - } + }else if(args[i].equalsIgnoreCase("gles=200")) { + PlatformRuntime.requestGL(200); + }else if(args[i].equalsIgnoreCase("gles=300")) { + PlatformRuntime.requestGL(300); + }else if(args[i].equalsIgnoreCase("gles=310")) { + PlatformRuntime.requestGL(310); }else { EnumPlatformANGLE angle = EnumPlatformANGLE.fromId(args[i]); if(angle != EnumPlatformANGLE.DEFAULT) { diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketPlayClient.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/WebSocketClientImpl.java similarity index 51% rename from src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketPlayClient.java rename to src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/WebSocketClientImpl.java index dc81127..feed775 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketPlayClient.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/WebSocketClientImpl.java @@ -1,4 +1,4 @@ -package net.lax1dude.eaglercraft.v1_8.internal; +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; import java.net.URI; import java.nio.ByteBuffer; @@ -9,77 +9,71 @@ import org.java_websocket.drafts.Draft_6455; import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension; import org.java_websocket.handshake.ServerHandshake; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ -class WebSocketPlayClient extends WebSocketClient { +class WebSocketClientImpl extends WebSocketClient { private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension()); + + protected final DesktopWebSocketClient clientObj; - public static final Logger logger = LogManager.getLogger("WebSocket"); - - WebSocketPlayClient(URI serverUri) { + WebSocketClientImpl(DesktopWebSocketClient clientObj, URI serverUri) { super(serverUri, perMessageDeflateDraft); + this.clientObj = clientObj; this.setConnectionLostTimeout(15); this.setTcpNoDelay(true); + this.connect(); } @Override public void onOpen(ServerHandshake arg0) { - PlatformNetworking.playConnectState = EnumEaglerConnectionState.CONNECTED; - PlatformNetworking.serverRateLimit = EnumServerRateLimit.OK; - logger.info("Connection opened: {}", this.uri.toString()); - } - - @Override - public void onClose(int arg0, String arg1, boolean arg2) { - logger.info("Connection closed: {}", this.uri.toString()); - } - - @Override - public void onError(Exception arg0) { - logger.error("Exception thrown by websocket \"" + this.getURI().toString() + "\"!"); - logger.error(arg0); - PlatformNetworking.playConnectState = EnumEaglerConnectionState.FAILED; - } - - @Override - public void onMessage(String arg0) { - if (arg0.equalsIgnoreCase("BLOCKED")) { - logger.error("Reached full IP ratelimit!"); - PlatformNetworking.serverRateLimit = EnumServerRateLimit.BLOCKED; - } else if (arg0.equalsIgnoreCase("LOCKED")) { - logger.error("Reached full IP ratelimit lockout!"); - PlatformNetworking.serverRateLimit = EnumServerRateLimit.LOCKED_OUT; + clientObj.playConnectState = EnumEaglerConnectionState.CONNECTED; + DesktopWebSocketClient.logger.info("Connection opened: {}", this.uri.toString()); + synchronized(clientObj.connectOpenMutex) { + clientObj.connectOpenMutex.notifyAll(); } } + @Override + public void onClose(int arg0, String arg1, boolean arg2) { + DesktopWebSocketClient.logger.info("Connection closed: {}", this.uri.toString()); + if(clientObj.playConnectState != EnumEaglerConnectionState.FAILED) { + clientObj.playConnectState = EnumEaglerConnectionState.CLOSED; + } + } + + @Override + public void onError(Exception arg0) { + DesktopWebSocketClient.logger.error("Exception thrown by websocket \"" + this.getURI().toString() + "\"!"); + DesktopWebSocketClient.logger.error(arg0); + if(clientObj.playConnectState == EnumEaglerConnectionState.CONNECTING) { + clientObj.playConnectState = EnumEaglerConnectionState.FAILED; + } + } + + @Override + public void onMessage(String arg0) { + clientObj.handleString(arg0); + } + @Override public void onMessage(ByteBuffer arg0) { - PlatformNetworking.recievedPlayPacket(arg0.array()); + clientObj.handleBytes(arg0.array()); } } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java index 99bcd9b..8981e80 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java @@ -10,24 +10,16 @@ import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.DesktopIntegratedS import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection; /** - * Copyright (c) 2023-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -36,19 +28,19 @@ public class ClientPlatformSingleplayer { private static CrashScreenPopup crashOverlay = null; - public static void startIntegratedServer() { + public static void startIntegratedServer(boolean forceSingleThread) { DesktopIntegratedServer.startIntegratedServer(); } public static void sendPacket(IPCPacketData packet) { - synchronized (MemoryConnection.clientToServerQueue) { + synchronized(MemoryConnection.clientToServerQueue) { MemoryConnection.clientToServerQueue.add(packet); } } public static IPCPacketData recievePacket() { - synchronized (MemoryConnection.serverToClientQueue) { - if (MemoryConnection.serverToClientQueue.size() > 0) { + synchronized(MemoryConnection.serverToClientQueue) { + if(MemoryConnection.serverToClientQueue.size() > 0) { return MemoryConnection.serverToClientQueue.remove(0); } } @@ -56,11 +48,11 @@ public class ClientPlatformSingleplayer { } public static List recieveAllPacket() { - synchronized (MemoryConnection.serverToClientQueue) { - if (MemoryConnection.serverToClientQueue.size() == 0) { + synchronized(MemoryConnection.serverToClientQueue) { + if(MemoryConnection.serverToClientQueue.size() == 0) { return null; - } else { - List ret = new ArrayList(MemoryConnection.serverToClientQueue); + }else { + List ret = new ArrayList<>(MemoryConnection.serverToClientQueue); MemoryConnection.serverToClientQueue.clear(); return ret; } @@ -79,8 +71,16 @@ public class ClientPlatformSingleplayer { return false; } + public static boolean isSingleThreadModeSupported() { + return false; + } + + public static void updateSingleThreadMode() { + + } + public static void showCrashReportOverlay(String report, int x, int y, int w, int h) { - if (crashOverlay == null) { + if(crashOverlay == null) { crashOverlay = new CrashScreenPopup(); } int[] wx = new int[1]; diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java index 0f77083..82b31ed 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java @@ -1,71 +1,90 @@ -package net.lax1dude.eaglercraft.v1_8.sp.server.internal; - -import java.util.ArrayList; -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; -import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; -import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter; -import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection; - -/** - * Copyright (c) 2023-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ServerPlatformSingleplayer { - - public static void initializeContext() { - PlatformFilesystem.initialize(); - } - - public static void sendPacket(IPCPacketData packet) { - synchronized (MemoryConnection.serverToClientQueue) { - MemoryConnection.serverToClientQueue.add(packet); - } - } - - public static IPCPacketData recievePacket() { - synchronized (MemoryConnection.clientToServerQueue) { - if (MemoryConnection.clientToServerQueue.size() > 0) { - return MemoryConnection.clientToServerQueue.remove(0); - } - } - return null; - } - - public static List recieveAllPacket() { - synchronized (MemoryConnection.clientToServerQueue) { - if (MemoryConnection.clientToServerQueue.size() == 0) { - return null; - } else { - List ret = new ArrayList(MemoryConnection.clientToServerQueue); - MemoryConnection.clientToServerQueue.clear(); - return ret; - } - } - } - - public static IClientConfigAdapter getClientConfigAdapter() { - return DesktopClientConfigAdapter.instance; - } -} +package net.lax1dude.eaglercraft.v1_8.sp.server.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import net.lax1dude.eaglercraft.v1_8.Filesystem; +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection; + +/** + * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerPlatformSingleplayer { + + private static IEaglerFilesystem filesystem = null; + + public static void initializeContext() { + if(filesystem == null) { + filesystem = Filesystem.getHandleFor(getClientConfigAdapter().getWorldsDB()); + } + } + + public static void initializeContextSingleThread(Consumer packetSendCallback) { + throw new UnsupportedOperationException(); + } + + public static IEaglerFilesystem getWorldsDatabase() { + return filesystem; + } + + public static void sendPacket(IPCPacketData packet) { + synchronized(MemoryConnection.serverToClientQueue) { + MemoryConnection.serverToClientQueue.add(packet); + } + } + + public static IPCPacketData recievePacket() { + synchronized(MemoryConnection.clientToServerQueue) { + if(MemoryConnection.clientToServerQueue.size() > 0) { + return MemoryConnection.clientToServerQueue.remove(0); + } + } + return null; + } + + public static List recieveAllPacket() { + synchronized(MemoryConnection.clientToServerQueue) { + if(MemoryConnection.clientToServerQueue.size() == 0) { + return null; + }else { + List ret = new ArrayList<>(MemoryConnection.clientToServerQueue); + MemoryConnection.clientToServerQueue.clear(); + return ret; + } + } + } + + public static IClientConfigAdapter getClientConfigAdapter() { + return DesktopClientConfigAdapter.instance; + } + + public static void immediateContinue() { + + } + + public static void platformShutdown() { + filesystem = null; + } + + public static boolean isSingleThreadMode() { + return false; + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/lwjgl/MemoryConnection.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/lwjgl/MemoryConnection.java index 3398d3d..e77091c 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/lwjgl/MemoryConnection.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/lwjgl/MemoryConnection.java @@ -22,7 +22,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; */ public class MemoryConnection { - public static final List clientToServerQueue = new LinkedList(); - public static final List serverToClientQueue = new LinkedList(); + public static final List clientToServerQueue = new LinkedList<>(); + public static final List serverToClientQueue = new LinkedList<>(); } diff --git a/src/main/java/com/google/common/base/CharMatcher.java b/src/main/java/com/google/common/base/CharMatcher.java index e84449d..97f9b3a 100644 --- a/src/main/java/com/google/common/base/CharMatcher.java +++ b/src/main/java/com/google/common/base/CharMatcher.java @@ -140,9 +140,9 @@ public abstract class CharMatcher implements Predicate { } // Must be in ascending order. - private static final String ZEROES = "0\u0660\u06f0\u07c0\u0966\u09e6\u0a66\u0ae6\u0b66\u0be6" - + "\u0c66\u0ce6\u0d66\u0e50\u0ed0\u0f20\u1040\u1090\u17e0\u1810\u1946\u19d0\u1b50\u1bb0" - + "\u1c40\u1c50\ua620\ua8d0\ua900\uaa50\uff10"; + private static final String ZEROES = new String(new char[] { '0', 0x0660, 0x06f0, 0x07c0, 0x0966, 0x09e6, 0x0a66, + 0x0ae6, 0x0b66, 0x0be6, 0x0c66, 0x0ce6, 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x1090, 0x17e0, 0x1810, + 0x1946, 0x19d0, 0x1b50, 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900, 0xaa50, 0xff10 }); private static final String NINES; static { @@ -234,10 +234,10 @@ public abstract class CharMatcher implements Predicate { * FORMAT, SURROGATE, and PRIVATE_USE according to ICU4J. */ public static final CharMatcher INVISIBLE = new RangesMatcher("CharMatcher.INVISIBLE", - ("\u0000\u007f\u00ad\u0600\u061c\u06dd\u070f\u1680\u180e\u2000\u2028\u205f\u2066\u2067\u2068" - + "\u2069\u206a\u3000\ud800\ufeff\ufff9\ufffa").toCharArray(), - ("\u0020\u00a0\u00ad\u0604\u061c\u06dd\u070f\u1680\u180e\u200f\u202f\u2064\u2066\u2067\u2068" - + "\u2069\u206f\u3000\uf8ff\ufeff\ufff9\ufffb").toCharArray()); + new char[] { 0x0000, 0x007f, 0x00ad, 0x0600, 0x061c, 0x06dd, 0x070f, 0x1680, 0x180e, 0x2000, 0x2028, 0x205f, + 0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x3000, 0xd800, 0xfeff, 0xfff9, 0xfffa }, + new char[] { 0x0020, 0x00a0, 0x00ad, 0x0604, 0x061c, 0x06dd, 0x070f, 0x1680, 0x180e, 0x200f, 0x202f, 0x2064, + 0x2066, 0x2067, 0x2068, 0x2069, 0x206f, 0x3000, 0xf8ff, 0xfeff, 0xfff9, 0xfffb }); private static String showCharacter(char c) { String hex = "0123456789ABCDEF"; @@ -260,8 +260,8 @@ public abstract class CharMatcher implements Predicate { * keep it up to date. */ public static final CharMatcher SINGLE_WIDTH = new RangesMatcher("CharMatcher.SINGLE_WIDTH", - "\u0000\u05be\u05d0\u05f3\u0600\u0750\u0e00\u1e00\u2100\ufb50\ufe70\uff61".toCharArray(), - "\u04f9\u05be\u05ea\u05f4\u06ff\u077f\u0e7f\u20af\u213a\ufdff\ufeff\uffdc".toCharArray()); + new char[] { 0x0000, 0x05be, 0x05d0, 0x05f3, 0x0600, 0x0750, 0x0e00, 0x1e00, 0x2100, 0xfb50, 0xfe70, 0xff61 }, + new char[] { 0x04f9, 0x05be, 0x05ea, 0x05f4, 0x06ff, 0x077f, 0x0e7f, 0x20af, 0x213a, 0xfdff, 0xfeff, 0xffdc }); /** Matches any character. */ public static final CharMatcher ANY = new FastMatcher("CharMatcher.ANY") { @@ -1474,9 +1474,9 @@ public abstract class CharMatcher implements Predicate { return description; } - static final String WHITESPACE_TABLE = "" + "\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000" - + "\u2029\u000B\u3000\u2008\u2003\u205F\u3000\u1680" + "\u0009\u0020\u2006\u2001\u202F\u00A0\u000C\u2009" - + "\u3000\u2004\u3000\u3000\u2028\n\u2007\u3000"; + static final String WHITESPACE_TABLE = new String(new char[] { 0x2002, 0x3000, '\r', 0x0085, 0x200A, 0x2005, 0x2000, + 0x3000, 0x2029, 0x000B, 0x3000, 0x2008, 0x2003, 0x205F, 0x3000, 0x1680, 0x0009, 0x0020, 0x2006, 0x2001, + 0x202F, 0x00A0, 0x000C, 0x2009, 0x3000, 0x2004, 0x3000, 0x3000, 0x2028, '\n', 0x2007, 0x3000 }); static final int WHITESPACE_MULTIPLIER = 1682554634; static final int WHITESPACE_SHIFT = Integer.numberOfLeadingZeros(WHITESPACE_TABLE.length() - 1); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Base64.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Base64.java index 7fcf7cf..13cf6d0 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Base64.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Base64.java @@ -1,857 +1,865 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.lax1dude.eaglercraft.v1_8; - -import java.math.BigInteger; -import java.nio.charset.Charset; - -/** - * Provides Base64 encoding and decoding as defined by - * RFC 2045. - * - *

- * This class implements section 6.8. Base64 - * Content-Transfer-Encoding from RFC 2045 Multipurpose Internet - * Mail Extensions (MIME) Part One: Format of Internet Message Bodies by - * Freed and Borenstein. - *

- *

- * The class can be parameterized in the following manner with various - * constructors: - *

- *
    - *
  • URL-safe mode: Default off.
  • - *
  • Line length: Default 76. Line length that aren't multiples of 4 will - * still essentially end up being multiples of 4 in the encoded data. - *
  • Line separator: Default is CRLF ("\r\n")
  • - *
- *

- * The URL-safe parameter is only applied to encode operations. Decoding - * seamlessly handles both modes. - *

- *

- * Since this class operates directly on byte streams, and not character - * streams, it is hard-coded to only encode/decode character encodings which are - * compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, - * etc). - *

- *

- * This class is thread-safe. - *

- * - * @see RFC 2045 - * @since 1.0 - */ -public class Base64 extends BaseNCodec { - - /** - * BASE32 characters are 6 bits in length. They are formed by taking a block of - * 3 octets to form a 24-bit string, which is converted into 4 BASE64 - * characters. - */ - private static final int BITS_PER_ENCODED_BYTE = 6; - private static final int BYTES_PER_UNENCODED_BLOCK = 3; - private static final int BYTES_PER_ENCODED_BLOCK = 4; - - /** - * This array is a lookup table that translates 6-bit positive integer index - * values into their "Base64 Alphabet" equivalents as specified in Table 1 of - * RFC 2045. - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] STANDARD_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', - '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; - - /** - * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed - * to - and _ to make the encoded Base64 results more URL-SAFE. This table is - * only used when the Base64's mode is set to URL-SAFE. - */ - private static final byte[] URL_SAFE_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', - '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; - - /** - * This array is a lookup table that translates Unicode characters drawn from - * the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into their 6-bit - * positive integer equivalents. Characters that are not in the Base64 alphabet - * but fall within the bounds of the array are translated to -1. - * - * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This - * means decoder seamlessly handles both URL_SAFE and STANDARD base64. (The - * encoder, on the other hand, needs to know ahead of time what to emit). - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] DECODE_TABLE = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - / - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9 - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _ - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z - }; - - /** - * Base64 uses 6-bit fields. - */ - /** Mask used to extract 6 bits, used when encoding */ - private static final int MASK_6BITS = 0x3f; - /** Mask used to extract 4 bits, used when decoding final trailing character. */ - private static final int MASK_4BITS = 0xf; - /** Mask used to extract 2 bits, used when decoding final trailing character. */ - private static final int MASK_2BITS = 0x3; - - // The static final fields above are used for the original static byte[] methods - // on Base64. - // The private member fields below are used with the new streaming approach, - // which requires - // some state be preserved between calls of encode() and decode(). - - /** - * Decodes Base64 data into octets. - *

- * Note: this method seamlessly handles data encoded in URL-safe or - * normal mode. - *

- * - * @param base64Data Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(final byte[] base64Data) { - return new Base64().decode(base64Data); - } - - /** - * Decodes a Base64 String into octets. - *

- * Note: this method seamlessly handles data encoded in URL-safe or - * normal mode. - *

- * - * @param base64String String containing Base64 data - * @return Array containing decoded data. - * @since 1.4 - */ - public static byte[] decodeBase64(final String base64String) { - return new Base64().decode(base64String); - } - - // Implementation of integer encoding used for crypto - /** - * Decodes a byte64-encoded integer according to crypto standards such as W3C's - * XML-Signature. - * - * @param pArray a byte array containing base64 character data - * @return A BigInteger - * @since 1.4 - */ - public static BigInteger decodeInteger(final byte[] pArray) { - return new BigInteger(1, decodeBase64(pArray)); - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * @param binaryData binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - */ - public static byte[] encodeBase64(final byte[] binaryData) { - return encodeBase64(binaryData, false); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the - * output into 76 character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if {@code true} this encoder will chunk the base64 output - * into 76 character blocks - * @return Base64-encoded data. - * @throws IllegalArgumentException Thrown when the input array needs an output - * array bigger than {@link Integer#MAX_VALUE} - */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { - return encodeBase64(binaryData, isChunked, false); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the - * output into 76 character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if {@code true} this encoder will chunk the base64 output - * into 76 character blocks - * @param urlSafe if {@code true} this encoder will emit - and _ instead of - * the usual + and / characters. Note: no padding is added - * when encoding using the URL-safe alphabet. - * @return Base64-encoded data. - * @throws IllegalArgumentException Thrown when the input array needs an output - * array bigger than {@link Integer#MAX_VALUE} - * @since 1.4 - */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { - return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the - * output into 76 character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if {@code true} this encoder will chunk the base64 - * output into 76 character blocks - * @param urlSafe if {@code true} this encoder will emit - and _ instead - * of the usual + and / characters. Note: no padding is - * added when encoding using the URL-safe alphabet. - * @param maxResultSize The maximum result size to accept. - * @return Base64-encoded data. - * @throws IllegalArgumentException Thrown when the input array needs an output - * array bigger than maxResultSize - * @since 1.4 - */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe, - final int maxResultSize) { - if (binaryData == null || binaryData.length == 0) { - return binaryData; - } - - // Create this so can use the super-class method - // Also ensures that the same roundings are performed by the ctor and the code - final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); - final long len = b64.getEncodedLength(binaryData); - if (len > maxResultSize) { - throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + len - + ") than the specified maximum size of " + maxResultSize); - } - - return b64.encode(binaryData); - } - - /** - * Encodes binary data using the base64 algorithm and chunks the encoded output - * into 76 character blocks - * - * @param binaryData binary data to encode - * @return Base64 characters chunked in 76 character blocks - */ - public static byte[] encodeBase64Chunked(final byte[] binaryData) { - return encodeBase64(binaryData, true); - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * NOTE: We changed the behavior of this method from multi-line chunking - * (commons-codec-1.4) to single-line non-chunking (commons-codec-1.5). - * - * @param binaryData binary data to encode - * @return String containing Base64 characters. - * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). - */ - public static String encodeBase64String(final byte[] binaryData) { - return new String(encodeBase64(binaryData, false), Charset.forName("UTF-8")); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but - * does not chunk the output. The url-safe variation emits - and _ instead of + - * and / characters. Note: no padding is added. - * - * @param binaryData binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - * @since 1.4 - */ - public static byte[] encodeBase64URLSafe(final byte[] binaryData) { - return encodeBase64(binaryData, false, true); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but - * does not chunk the output. The url-safe variation emits - and _ instead of + - * and / characters. Note: no padding is added. - * - * @param binaryData binary data to encode - * @return String containing Base64 characters - * @since 1.4 - */ - public static String encodeBase64URLSafeString(final byte[] binaryData) { - return new String(encodeBase64(binaryData, false, true), Charset.forName("UTF-8")); - } - - /** - * Encodes to a byte64-encoded integer according to crypto standards such as - * W3C's XML-Signature. - * - * @param bigInteger a BigInteger - * @return A byte array containing base64 character data - * @throws NullPointerException if null is passed in - * @since 1.4 - */ - public static byte[] encodeInteger(final BigInteger bigInteger) { - return encodeBase64(toIntegerBytes(bigInteger), false); - } - - /** - * Tests a given byte array to see if it contains only valid characters within - * the Base64 alphabet. Currently the method treats whitespace as valid. - * - * @param arrayOctet byte array to test - * @return {@code true} if all bytes are valid characters in the Base64 alphabet - * or if the byte array is empty; {@code false}, otherwise - * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. - */ - @Deprecated - public static boolean isArrayByteBase64(final byte[] arrayOctet) { - return isBase64(arrayOctet); - } - - /** - * Returns whether or not the {@code octet} is in the base 64 alphabet. - * - * @param octet The value to test - * @return {@code true} if the value is defined in the the base 64 alphabet, - * {@code false} otherwise. - * @since 1.4 - */ - public static boolean isBase64(final byte octet) { - return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); - } - - /** - * Tests a given byte array to see if it contains only valid characters within - * the Base64 alphabet. Currently the method treats whitespace as valid. - * - * @param arrayOctet byte array to test - * @return {@code true} if all bytes are valid characters in the Base64 alphabet - * or if the byte array is empty; {@code false}, otherwise - * @since 1.5 - */ - public static boolean isBase64(final byte[] arrayOctet) { - for (int i = 0; i < arrayOctet.length; i++) { - if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { - return false; - } - } - return true; - } - - /** - * Tests a given String to see if it contains only valid characters within the - * Base64 alphabet. Currently the method treats whitespace as valid. - * - * @param base64 String to test - * @return {@code true} if all characters in the String are valid characters in - * the Base64 alphabet or if the String is empty; {@code false}, - * otherwise - * @since 1.5 - */ - public static boolean isBase64(final String base64) { - return isBase64(base64.getBytes(Charset.forName("UTF-8"))); - } - - /** - * Returns a byte-array representation of a {@code BigInteger} without sign bit. - * - * @param bigInt {@code BigInteger} to be converted - * @return a byte array representation of the BigInteger parameter - */ - static byte[] toIntegerBytes(final BigInteger bigInt) { - int bitlen = bigInt.bitLength(); - // round bitlen - bitlen = ((bitlen + 7) >> 3) << 3; - final byte[] bigBytes = bigInt.toByteArray(); - - if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { - return bigBytes; - } - // set up params for copying everything but sign bit - int startSrc = 0; - int len = bigBytes.length; - - // if bigInt is exactly byte-aligned, just skip signbit in copy - if ((bigInt.bitLength() % 8) == 0) { - startSrc = 1; - len--; - } - final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec - final byte[] resizedBytes = new byte[bitlen / 8]; - System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); - return resizedBytes; - } - - /** - * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE - * above remains static because it is able to decode both STANDARD and URL_SAFE - * streams, but the encodeTable must be a member variable so we can switch - * between the two modes. - */ - private final byte[] encodeTable; - - // Only one decode table currently; keep for consistency with Base32 code - private final byte[] decodeTable = DECODE_TABLE; - - /** - * Line separator for encoding. Not used when decoding. Only used if lineLength - * > 0. - */ - private final byte[] lineSeparator; - - /** - * Convenience variable to help us determine when our buffer is going to run out - * of room and needs resizing. {@code decodeSize = 3 + lineSeparator.length;} - */ - private final int decodeSize; - - /** - * Convenience variable to help us determine when our buffer is going to run out - * of room and needs resizing. {@code encodeSize = 4 + lineSeparator.length;} - */ - private final int encodeSize; - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in - * URL-unsafe mode. - *

- * When encoding the line length is 0 (no chunking), and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- */ - public Base64() { - this(0); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in the - * given URL-safe mode. - *

- * When encoding the line length is 76, the line separator is CRLF, and the - * encoding table is STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- * - * @param urlSafe if {@code true}, URL-safe encoding is used. In most cases this - * should be set to {@code false}. - * @since 1.4 - */ - public Base64(final boolean urlSafe) { - this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in - * URL-unsafe mode. - *

- * When encoding the line length is given in the constructor, the line separator - * is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being - * multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given - * length (rounded down to nearest multiple of 4). If - * lineLength <= 0, then the output will not be divided - * into lines (chunks). Ignored when decoding. - * @since 1.4 - */ - public Base64(final int lineLength) { - this(lineLength, CHUNK_SEPARATOR); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in - * URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the - * constructor, and the encoding table is STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being - * multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given - * length (rounded down to nearest multiple of 4). If - * lineLength <= 0, then the output will not be divided - * into lines (chunks). Ignored when decoding. - * @param lineSeparator Each line of encoded data will end with this sequence of - * bytes. - * @throws IllegalArgumentException Thrown when the provided lineSeparator - * included some base64 characters. - * @since 1.4 - */ - public Base64(final int lineLength, final byte[] lineSeparator) { - this(lineLength, lineSeparator, false); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in - * URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the - * constructor, and the encoding table is STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being - * multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given - * length (rounded down to nearest multiple of 4). If - * lineLength <= 0, then the output will not be divided - * into lines (chunks). Ignored when decoding. - * @param lineSeparator Each line of encoded data will end with this sequence of - * bytes. - * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' - * respectively. urlSafe is only applied to encode - * operations. Decoding seamlessly handles both modes. - * Note: no padding is added when using the URL-safe - * alphabet. - * @throws IllegalArgumentException Thrown when the {@code lineSeparator} - * contains Base64 characters. - * @since 1.4 - */ - public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { - this(lineLength, lineSeparator, urlSafe, CodecPolicy.LENIANT); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in - * URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the - * constructor, and the encoding table is STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being - * multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given - * length (rounded down to nearest multiple of 4). If - * lineLength <= 0, then the output will not be divided - * into lines (chunks). Ignored when decoding. - * @param lineSeparator Each line of encoded data will end with this sequence - * of bytes. - * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' - * respectively. urlSafe is only applied to encode - * operations. Decoding seamlessly handles both modes. - * Note: no padding is added when using the URL-safe - * alphabet. - * @param decodingPolicy The decoding policy. - * @throws IllegalArgumentException Thrown when the {@code lineSeparator} - * contains Base64 characters. - * @since 1.15 - */ - public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, - final CodecPolicy decodingPolicy) { - super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, lineLength, - lineSeparator == null ? 0 : lineSeparator.length, PAD_DEFAULT, decodingPolicy); - // TODO could be simplified if there is no requirement to reject invalid line - // sep when length <=0 - // @see test case Base64Test.testConstructors() - if (lineSeparator != null) { - if (containsAlphabetOrPad(lineSeparator)) { - final String sep = new String(lineSeparator, Charset.forName("UTF-8")); - throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); - } - if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE - this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; - this.lineSeparator = new byte[lineSeparator.length]; - System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); - } else { - this.encodeSize = BYTES_PER_ENCODED_BLOCK; - this.lineSeparator = null; - } - } else { - this.encodeSize = BYTES_PER_ENCODED_BLOCK; - this.lineSeparator = null; - } - this.decodeSize = this.encodeSize - 1; - this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; - } - - // Implementation of the Encoder Interface - - /** - *

- * Decodes all of the provided data, starting at inPos, for inAvail bytes. - * Should be called at least twice: once with the data to decode, and once with - * inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" call - * is not necessary when decoding, but it doesn't hurt, either. - *

- *

- * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) - * data is handled, since CR and LF are silently ignored, but has implications - * for other bytes, too. This method subscribes to the garbage-in, garbage-out - * philosophy: it will not check the provided data for validity. - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and - * general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in byte[] array of ascii data to base64 decode. - * @param inPos Position to start reading data from. - * @param inAvail Amount of bytes available from input for decoding. - * @param context the context to be used - */ - @Override - void decode(final byte[] in, int inPos, final int inAvail, final Context context) { - if (context.eof) { - return; - } - if (inAvail < 0) { - context.eof = true; - } - for (int i = 0; i < inAvail; i++) { - final byte[] buffer = ensureBufferSize(decodeSize, context); - final byte b = in[inPos++]; - if (b == pad) { - // We're done. - context.eof = true; - break; - } - if (b >= 0 && b < DECODE_TABLE.length) { - final int result = DECODE_TABLE[b]; - if (result >= 0) { - context.modulus = (context.modulus + 1) % BYTES_PER_ENCODED_BLOCK; - context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; - if (context.modulus == 0) { - buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); - buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); - buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); - } - } - } - } - - // Two forms of EOF as far as base64 decoder is concerned: actual - // EOF (-1) and first time '=' character is encountered in stream. - // This approach makes the '=' padding characters completely optional. - if (context.eof && context.modulus != 0) { - final byte[] buffer = ensureBufferSize(decodeSize, context); - - // We have some spare bits remaining - // Output all whole multiples of 8 bits and ignore the rest - switch (context.modulus) { -// case 0 : // impossible, as excluded above - case 1: // 6 bits - either ignore entirely, or raise an exception - validateTrailingCharacter(); - break; - case 2: // 12 bits = 8 + 4 - validateCharacter(MASK_4BITS, context); - context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits - buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); - break; - case 3: // 18 bits = 8 + 8 + 2 - validateCharacter(MASK_2BITS, context); - context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits - buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); - buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); - break; - default: - throw new IllegalStateException("Impossible modulus " + context.modulus); - } - } - } - - /** - *

- * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must - * be called at least twice: once with the data to encode, and once with inAvail - * set to "-1" to alert encoder that EOF has been reached, to flush last - * remaining bytes (if not multiple of 3). - *

- *

- * Note: no padding is added when encoding using the URL-safe alphabet. - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and - * general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in byte[] array of binary data to base64 encode. - * @param inPos Position to start reading data from. - * @param inAvail Amount of bytes available from input for encoding. - * @param context the context to be used - */ - @Override - void encode(final byte[] in, int inPos, final int inAvail, final Context context) { - if (context.eof) { - return; - } - // inAvail < 0 is how we're informed of EOF in the underlying data we're - // encoding. - if (inAvail < 0) { - context.eof = true; - if (0 == context.modulus && lineLength == 0) { - return; // no leftovers to process and not using chunking - } - final byte[] buffer = ensureBufferSize(encodeSize, context); - final int savedPos = context.pos; - switch (context.modulus) { // 0-2 - case 0: // nothing to do here - break; - case 1: // 8 bits = 6 + 2 - // top 6 bits: - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS]; - // remaining 2: - buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[context.pos++] = pad; - buffer[context.pos++] = pad; - } - break; - - case 2: // 16 bits = 6 + 6 + 4 - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[context.pos++] = pad; - } - break; - default: - throw new IllegalStateException("Impossible modulus " + context.modulus); - } - context.currentLinePos += context.pos - savedPos; // keep track of current line position - // if currentPos == 0 we are at the start of a line, so don't add CRLF - if (lineLength > 0 && context.currentLinePos > 0) { - System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); - context.pos += lineSeparator.length; - } - } else { - for (int i = 0; i < inAvail; i++) { - final byte[] buffer = ensureBufferSize(encodeSize, context); - context.modulus = (context.modulus + 1) % BYTES_PER_UNENCODED_BLOCK; - int b = in[inPos++]; - if (b < 0) { - b += 256; - } - context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE - if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS]; - context.currentLinePos += BYTES_PER_ENCODED_BLOCK; - if (lineLength > 0 && lineLength <= context.currentLinePos) { - System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); - context.pos += lineSeparator.length; - context.currentLinePos = 0; - } - } - } - } - } - - /** - * Returns whether or not the {@code octet} is in the Base64 alphabet. - * - * @param octet The value to test - * @return {@code true} if the value is defined in the the Base64 alphabet - * {@code false} otherwise. - */ - @Override - protected boolean isInAlphabet(final byte octet) { - return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; - } - - /** - * Returns our current encode mode. True if we're URL-SAFE, false otherwise. - * - * @return true if we're in URL-SAFE mode, false otherwise. - * @since 1.4 - */ - public boolean isUrlSafe() { - return this.encodeTable == URL_SAFE_ENCODE_TABLE; - } - - /** - * Validates whether decoding the final trailing character is possible in the - * context of the set of possible base 64 values. - * - *

- * The character is valid if the lower bits within the provided mask are zero. - * This is used to test the final trailing base-64 digit is zero in the bits - * that will be discarded. - * - * @param emptyBitsMask The mask of the lower bits that should be empty - * @param context the context to be used - * - * @throws IllegalArgumentException if the bits being checked contain any - * non-zero value - */ - private void validateCharacter(final int emptyBitsMask, final Context context) { - if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) { - throw new IllegalArgumentException( - "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " - + "Expected the discarded bits from the character to be zero."); - } - } - - /** - * Validates whether decoding allows an entire final trailing character that - * cannot be used for a complete byte. - * - * @throws IllegalArgumentException if strict decoding is enabled - */ - private void validateTrailingCharacter() { - if (isStrictDecoding()) { - throw new IllegalArgumentException( - "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " - + "Decoding requires at least two trailing 6-bit characters to create bytes."); - } - } - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.lax1dude.eaglercraft.v1_8; + +import java.math.BigInteger; +import java.nio.charset.Charset; + +/** + * Provides Base64 encoding and decoding as defined by + * RFC 2045. + * + *

+ * This class implements section 6.8. Base64 + * Content-Transfer-Encoding from RFC 2045 Multipurpose Internet + * Mail Extensions (MIME) Part One: Format of Internet Message Bodies by + * Freed and Borenstein. + *

+ *

+ * The class can be parameterized in the following manner with various + * constructors: + *

+ *
    + *
  • URL-safe mode: Default off.
  • + *
  • Line length: Default 76. Line length that aren't multiples of 4 will + * still essentially end up being multiples of 4 in the encoded data. + *
  • Line separator: Default is CRLF ("\r\n")
  • + *
+ *

+ * The URL-safe parameter is only applied to encode operations. Decoding + * seamlessly handles both modes. + *

+ *

+ * Since this class operates directly on byte streams, and not character + * streams, it is hard-coded to only encode/decode character encodings which are + * compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, + * etc). + *

+ *

+ * This class is thread-safe. + *

+ * + * @see RFC 2045 + * @since 1.0 + */ +public class Base64 extends BaseNCodec { + + /** + * BASE32 characters are 6 bits in length. They are formed by taking a block of + * 3 octets to form a 24-bit string, which is converted into 4 BASE64 + * characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 6; + private static final int BYTES_PER_UNENCODED_BLOCK = 3; + private static final int BYTES_PER_ENCODED_BLOCK = 4; + + /** + * This array is a lookup table that translates 6-bit positive integer index + * values into their "Base64 Alphabet" equivalents as specified in Table 1 of + * RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed + * to - and _ to make the encoded Base64 results more URL-SAFE. This table is + * only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; + + /** + * This array is a lookup table that translates Unicode characters drawn from + * the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into their 6-bit + * positive integer equivalents. Characters that are not in the Base64 alphabet + * but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This + * means decoder seamlessly handles both URL_SAFE and STANDARD base64. (The + * encoder, on the other hand, needs to know ahead of time what to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - / + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z + }; + + public static int lookupCharInt(char c) { + return c < 123 ? DECODE_TABLE[c] : -1; + } + + public static char lookupIntChar(int i) { + return (char)STANDARD_ENCODE_TABLE[i]; + } + + /** + * Base64 uses 6-bit fields. + */ + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + /** Mask used to extract 4 bits, used when decoding final trailing character. */ + private static final int MASK_4BITS = 0xf; + /** Mask used to extract 2 bits, used when decoding final trailing character. */ + private static final int MASK_2BITS = 0x3; + + // The static final fields above are used for the original static byte[] methods + // on Base64. + // The private member fields below are used with the new streaming approach, + // which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Decodes Base64 data into octets. + *

+ * Note: this method seamlessly handles data encoded in URL-safe or + * normal mode. + *

+ * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(final byte[] base64Data) { + return new Base64().decode(base64Data); + } + + /** + * Decodes a Base64 String into octets. + *

+ * Note: this method seamlessly handles data encoded in URL-safe or + * normal mode. + *

+ * + * @param base64String String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(final String base64String) { + return new Base64().decode(base64String); + } + + // Implementation of integer encoding used for crypto + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's + * XML-Signature. + * + * @param pArray a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(final byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(final byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the + * output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output + * into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output + * array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the + * output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output + * into 76 character blocks + * @param urlSafe if {@code true} this encoder will emit - and _ instead of + * the usual + and / characters. Note: no padding is added + * when encoding using the URL-safe alphabet. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output + * array bigger than {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the + * output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 + * output into 76 character blocks + * @param urlSafe if {@code true} this encoder will emit - and _ instead + * of the usual + and / characters. Note: no padding is + * added when encoding using the URL-safe alphabet. + * @param maxResultSize The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output + * array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe, + final int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + // Create this so can use the super-class method + // Also ensures that the same roundings are performed by the ctor and the code + final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); + final long len = b64.getEncodedLength(binaryData); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + len + + ") than the specified maximum size of " + maxResultSize); + } + + return b64.encode(binaryData); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output + * into 76 character blocks + * + * @param binaryData binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(final byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * NOTE: We changed the behavior of this method from multi-line chunking + * (commons-codec-1.4) to single-line non-chunking (commons-codec-1.5). + * + * @param binaryData binary data to encode + * @return String containing Base64 characters. + * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). + */ + public static String encodeBase64String(final byte[] binaryData) { + return new String(encodeBase64(binaryData, false), Charset.forName("UTF-8")); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but + * does not chunk the output. The url-safe variation emits - and _ instead of + + * and / characters. Note: no padding is added. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(final byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but + * does not chunk the output. The url-safe variation emits - and _ instead of + + * and / characters. Note: no padding is added. + * + * @param binaryData binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(final byte[] binaryData) { + return new String(encodeBase64(binaryData, false, true), Charset.forName("UTF-8")); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as + * W3C's XML-Signature. + * + * @param bigInteger a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(final BigInteger bigInteger) { + return encodeBase64(toIntegerBytes(bigInteger), false); + } + + /** + * Tests a given byte array to see if it contains only valid characters within + * the Base64 alphabet. Currently the method treats whitespace as valid. + * + * @param arrayOctet byte array to test + * @return {@code true} if all bytes are valid characters in the Base64 alphabet + * or if the byte array is empty; {@code false}, otherwise + * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. + */ + @Deprecated + public static boolean isArrayByteBase64(final byte[] arrayOctet) { + return isBase64(arrayOctet); + } + + /** + * Returns whether or not the {@code octet} is in the base 64 alphabet. + * + * @param octet The value to test + * @return {@code true} if the value is defined in the the base 64 alphabet, + * {@code false} otherwise. + * @since 1.4 + */ + public static boolean isBase64(final byte octet) { + return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given byte array to see if it contains only valid characters within + * the Base64 alphabet. Currently the method treats whitespace as valid. + * + * @param arrayOctet byte array to test + * @return {@code true} if all bytes are valid characters in the Base64 alphabet + * or if the byte array is empty; {@code false}, otherwise + * @since 1.5 + */ + public static boolean isBase64(final byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Tests a given String to see if it contains only valid characters within the + * Base64 alphabet. Currently the method treats whitespace as valid. + * + * @param base64 String to test + * @return {@code true} if all characters in the String are valid characters in + * the Base64 alphabet or if the String is empty; {@code false}, + * otherwise + * @since 1.5 + */ + public static boolean isBase64(final String base64) { + return isBase64(base64.getBytes(Charset.forName("UTF-8"))); + } + + /** + * Returns a byte-array representation of a {@code BigInteger} without sign bit. + * + * @param bigInt {@code BigInteger} to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(final BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + final byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + final byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE + * above remains static because it is able to decode both STANDARD and URL_SAFE + * streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + // Only one decode table currently; keep for consistency with Base32 code + private final byte[] decodeTable = DECODE_TABLE; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength + * > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out + * of room and needs resizing. {@code decodeSize = 3 + lineSeparator.length;} + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out + * of room and needs resizing. {@code encodeSize = 4 + lineSeparator.length;} + */ + private final int encodeSize; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length is 0 (no chunking), and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ */ + public Base64() { + this(0); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the + * given URL-safe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the + * encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ * + * @param urlSafe if {@code true}, URL-safe encoding is used. In most cases this + * should be set to {@code false}. + * @since 1.4 + */ + public Base64(final boolean urlSafe) { + this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length is given in the constructor, the line separator + * is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @since 1.4 + */ + public Base64(final int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the + * constructor, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of + * bytes. + * @throws IllegalArgumentException Thrown when the provided lineSeparator + * included some base64 characters. + * @since 1.4 + */ + public Base64(final int lineLength, final byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the + * constructor, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of + * bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' + * respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * Note: no padding is added when using the URL-safe + * alphabet. + * @throws IllegalArgumentException Thrown when the {@code lineSeparator} + * contains Base64 characters. + * @since 1.4 + */ + public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { + this(lineLength, lineSeparator, urlSafe, CodecPolicy.LENIANT); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the + * constructor, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence + * of bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' + * respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * Note: no padding is added when using the URL-safe + * alphabet. + * @param decodingPolicy The decoding policy. + * @throws IllegalArgumentException Thrown when the {@code lineSeparator} + * contains Base64 characters. + * @since 1.15 + */ + public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, + final CodecPolicy decodingPolicy) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, lineLength, + lineSeparator == null ? 0 : lineSeparator.length, PAD_DEFAULT, decodingPolicy); + // TODO could be simplified if there is no requirement to reject invalid line + // sep when length <=0 + // @see test case Base64Test.testConstructors() + if (lineSeparator != null) { + if (containsAlphabetOrPad(lineSeparator)) { + final String sep = new String(lineSeparator, Charset.forName("UTF-8")); + throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); + } + if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + // Implementation of the Encoder Interface + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. + * Should be called at least twice: once with the data to decode, and once with + * inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" call + * is not necessary when decoding, but it doesn't hurt, either. + *

+ *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) + * data is handled, since CR and LF are silently ignored, but has implications + * for other bytes, too. This method subscribes to the garbage-in, garbage-out + * philosophy: it will not check the provided data for validity. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and + * general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of ascii data to base64 decode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for decoding. + * @param context the context to be used + */ + @Override + void decode(final byte[] in, int inPos, final int inAvail, final Context context) { + if (context.eof) { + return; + } + if (inAvail < 0) { + context.eof = true; + } + for (int i = 0; i < inAvail; i++) { + final byte[] buffer = ensureBufferSize(decodeSize, context); + final byte b = in[inPos++]; + if (b == pad) { + // We're done. + context.eof = true; + break; + } + if (b >= 0 && b < DECODE_TABLE.length) { + final int result = DECODE_TABLE[b]; + if (result >= 0) { + context.modulus = (context.modulus + 1) % BYTES_PER_ENCODED_BLOCK; + context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; + if (context.modulus == 0) { + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); + buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (context.eof && context.modulus != 0) { + final byte[] buffer = ensureBufferSize(decodeSize, context); + + // We have some spare bits remaining + // Output all whole multiples of 8 bits and ignore the rest + switch (context.modulus) { +// case 0 : // impossible, as excluded above + case 1: // 6 bits - either ignore entirely, or raise an exception + validateTrailingCharacter(); + break; + case 2: // 12 bits = 8 + 4 + validateCharacter(MASK_4BITS, context); + context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits + buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); + break; + case 3: // 18 bits = 8 + 8 + 2 + validateCharacter(MASK_2BITS, context); + context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); + buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); + break; + default: + throw new IllegalStateException("Impossible modulus " + context.modulus); + } + } + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must + * be called at least twice: once with the data to encode, and once with inAvail + * set to "-1" to alert encoder that EOF has been reached, to flush last + * remaining bytes (if not multiple of 3). + *

+ *

+ * Note: no padding is added when encoding using the URL-safe alphabet. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and + * general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of binary data to base64 encode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + * @param context the context to be used + */ + @Override + void encode(final byte[] in, int inPos, final int inAvail, final Context context) { + if (context.eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + context.eof = true; + if (0 == context.modulus && lineLength == 0) { + return; // no leftovers to process and not using chunking + } + final byte[] buffer = ensureBufferSize(encodeSize, context); + final int savedPos = context.pos; + switch (context.modulus) { // 0-2 + case 0: // nothing to do here + break; + case 1: // 8 bits = 6 + 2 + // top 6 bits: + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS]; + // remaining 2: + buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[context.pos++] = pad; + buffer[context.pos++] = pad; + } + break; + + case 2: // 16 bits = 6 + 6 + 4 + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[context.pos++] = pad; + } + break; + default: + throw new IllegalStateException("Impossible modulus " + context.modulus); + } + context.currentLinePos += context.pos - savedPos; // keep track of current line position + // if currentPos == 0 we are at the start of a line, so don't add CRLF + if (lineLength > 0 && context.currentLinePos > 0) { + System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); + context.pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + final byte[] buffer = ensureBufferSize(encodeSize, context); + context.modulus = (context.modulus + 1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE + if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS]; + context.currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= context.currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); + context.pos += lineSeparator.length; + context.currentLinePos = 0; + } + } + } + } + } + + /** + * Returns whether or not the {@code octet} is in the Base64 alphabet. + * + * @param octet The value to test + * @return {@code true} if the value is defined in the the Base64 alphabet + * {@code false} otherwise. + */ + @Override + protected boolean isInAlphabet(final byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + * Validates whether decoding the final trailing character is possible in the + * context of the set of possible base 64 values. + * + *

+ * The character is valid if the lower bits within the provided mask are zero. + * This is used to test the final trailing base-64 digit is zero in the bits + * that will be discarded. + * + * @param emptyBitsMask The mask of the lower bits that should be empty + * @param context the context to be used + * + * @throws IllegalArgumentException if the bits being checked contain any + * non-zero value + */ + private void validateCharacter(final int emptyBitsMask, final Context context) { + if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) { + throw new IllegalArgumentException( + "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " + + "Expected the discarded bits from the character to be zero."); + } + } + + /** + * Validates whether decoding allows an entire final trailing character that + * cannot be used for a complete byte. + * + * @throws IllegalArgumentException if strict decoding is enabled + */ + private void validateTrailingCharacter() { + if (isStrictDecoding()) { + throw new IllegalArgumentException( + "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " + + "Decoding requires at least two trailing 6-bit characters to create bytes."); + } + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java new file mode 100755 index 0000000..0cc95af --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java @@ -0,0 +1,152 @@ +package net.lax1dude.eaglercraft.v1_8; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetOtherClientUUIDV4EAG; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClientUUIDLoadingCache { + + private static final Logger logger = LogManager.getLogger("ClientUUIDLoadingCache"); + + public static final EaglercraftUUID NULL_UUID = new EaglercraftUUID(0l, 0l); + public static final EaglercraftUUID PENDING_UUID = new EaglercraftUUID(0x6969696969696969l, 0x6969696969696969l); + public static final EaglercraftUUID VANILLA_UUID = new EaglercraftUUID(0x1DCE015CD384374El, 0x85030A4DE95E5736l); + + /** + * For client devs, allows you to get EaglercraftVersion.clientBrandUUID of + * other players on a server, to detect other players who also use your client. + * + * Requires EaglerXBungee 1.3.0 or EaglerXVelocity 1.1.0 + * + * @return NULL_UUID if not found, PENDING_UUID if pending, + * VANILLA_UUID if vanilla, or the remote player's + * client's EaglercraftVersion.clientBrandUUID + */ + public static EaglercraftUUID getPlayerClientBrandUUID(EntityPlayer player) { + EaglercraftUUID ret = null; + if(player instanceof AbstractClientPlayer) { + ret = ((AbstractClientPlayer)player).clientBrandUUIDCache; + if(ret == null) { + Minecraft mc = Minecraft.getMinecraft(); + if(mc != null && mc.thePlayer != null && mc.thePlayer.sendQueue.getEaglerMessageProtocol().ver >= 4) { + ret = PENDING_UUID; + EaglercraftUUID playerUUID = player.getUniqueID(); + if(!waitingUUIDs.containsKey(playerUUID) && !evictedUUIDs.containsKey(playerUUID)) { + int reqID = ++requestId & 0x3FFF; + WaitingLookup newLookup = new WaitingLookup(reqID, playerUUID, EagRuntime.steadyTimeMillis(), + (AbstractClientPlayer) player); + waitingIDs.put(reqID, newLookup); + waitingUUIDs.put(playerUUID, newLookup); + mc.thePlayer.sendQueue.sendEaglerMessage( + new CPacketGetOtherClientUUIDV4EAG(reqID, newLookup.uuid.msb, newLookup.uuid.lsb)); + } + } + } + }else if(player instanceof EntityPlayerMP) { + ret = ((EntityPlayerMP)player).clientBrandUUID; + } + if(ret == null) { + ret = NULL_UUID; + } + return ret; + } + + private static final Map waitingIDs = new HashMap<>(); + private static final Map waitingUUIDs = new HashMap<>(); + private static final Map evictedUUIDs = new HashMap<>(); + + private static int requestId = 0; + private static long lastFlushReq = EagRuntime.steadyTimeMillis(); + private static long lastFlushEvict = EagRuntime.steadyTimeMillis(); + + public static void update() { + long timestamp = EagRuntime.steadyTimeMillis(); + if(timestamp - lastFlushReq > 5000l) { + lastFlushReq = timestamp; + if(!waitingIDs.isEmpty()) { + Iterator itr = waitingIDs.values().iterator(); + while(itr.hasNext()) { + WaitingLookup lookup = itr.next(); + if(timestamp - lookup.timestamp > 15000l) { + itr.remove(); + waitingUUIDs.remove(lookup.uuid); + } + } + } + } + if(timestamp - lastFlushEvict > 1000l) { + lastFlushEvict = timestamp; + if(!evictedUUIDs.isEmpty()) { + Iterator evictItr = evictedUUIDs.values().iterator(); + while(evictItr.hasNext()) { + if(timestamp - evictItr.next().longValue() > 3000l) { + evictItr.remove(); + } + } + } + } + } + + public static void flushRequestCache() { + waitingIDs.clear(); + waitingUUIDs.clear(); + evictedUUIDs.clear(); + } + + public static void handleResponse(int requestId, EaglercraftUUID clientId) { + WaitingLookup lookup = waitingIDs.remove(requestId); + if(lookup != null) { + lookup.player.clientBrandUUIDCache = clientId; + waitingUUIDs.remove(lookup.uuid); + }else { + logger.warn("Unsolicited client brand UUID lookup response #{} recieved! (Brand UUID: {})", requestId, clientId); + } + } + + public static void evict(EaglercraftUUID clientId) { + evictedUUIDs.put(clientId, Long.valueOf(EagRuntime.steadyTimeMillis())); + WaitingLookup lk = waitingUUIDs.remove(clientId); + if(lk != null) { + waitingIDs.remove(lk.reqID); + } + } + + private static class WaitingLookup { + + private final int reqID; + private final EaglercraftUUID uuid; + private final long timestamp; + private final AbstractClientPlayer player; + + public WaitingLookup(int reqID, EaglercraftUUID uuid, long timestamp, AbstractClientPlayer player) { + this.reqID = reqID; + this.uuid = uuid; + this.timestamp = timestamp; + this.player = player; + } + + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java index 11b73cd..2d81f65 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java @@ -1,98 +1,133 @@ -package net.lax1dude.eaglercraft.v1_8; - -import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class Display { - - private static long lastSwap = 0l; - - public static int getWidth() { - return PlatformInput.getWindowWidth(); - } - - public static int getHeight() { - return PlatformInput.getWindowHeight(); - } - - public static boolean isActive() { - return PlatformInput.getWindowFocused(); - } - - public static void create() { - - } - - public static void setTitle(String string) { - - } - - public static boolean isCloseRequested() { - return PlatformInput.isCloseRequested(); - } - - public static void setVSync(boolean enable) { - PlatformInput.setVSync(enable); - } - - public static boolean isVSyncSupported() { - return PlatformInput.isVSyncSupported(); - } - - public static void update() { - PlatformInput.update(); - } - - public static void sync(int limitFramerate) { - boolean limitFPS = limitFramerate > 0 && limitFramerate < 1000; - - if (limitFPS) { - long millis = System.currentTimeMillis(); - long frameMillis = (1000l / limitFramerate) - (millis - lastSwap); - if (frameMillis > 0l) { - EagUtils.sleep(frameMillis); - } - } - - lastSwap = System.currentTimeMillis(); - } - - public static boolean contextLost() { - return PlatformInput.contextLost(); - } - - public static boolean wasResized() { - return PlatformInput.wasResized(); - } - - public static boolean isFullscreen() { - return PlatformInput.isFullscreen(); - } - - public static void toggleFullscreen() { - PlatformInput.toggleFullscreen(); - } - -} +package net.lax1dude.eaglercraft.v1_8; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +/** + * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class Display { + + private static long lastSwap = 0l; + private static long lastDPIUpdate = -250l; + private static float cacheDPI = 1.0f; + + public static int getWidth() { + return PlatformInput.getWindowWidth(); + } + + public static int getHeight() { + return PlatformInput.getWindowHeight(); + } + + public static int getVisualViewportX() { + return PlatformInput.getVisualViewportX(); + } + + public static int getVisualViewportY() { + return PlatformInput.getVisualViewportY(); + } + + public static int getVisualViewportW() { + return PlatformInput.getVisualViewportW(); + } + + public static int getVisualViewportH() { + return PlatformInput.getVisualViewportH(); + } + + public static boolean isActive() { + return PlatformInput.getWindowFocused(); + } + + public static void create() { + + } + + public static void setTitle(String string) { + + } + + public static boolean isCloseRequested() { + return PlatformInput.isCloseRequested(); + } + + public static void setVSync(boolean enable) { + PlatformInput.setVSync(enable); + } + + public static boolean isVSyncSupported() { + return PlatformInput.isVSyncSupported(); + } + + public static void update() { + PlatformInput.update(); + } + + public static void sync(int limitFramerate) { + boolean limitFPS = limitFramerate > 0 && limitFramerate < 1000; + + if(limitFPS) { + long millis = EagRuntime.steadyTimeMillis(); + long frameMillis = (1000l / limitFramerate) - (millis - lastSwap); + if (frameMillis > 0l) { + EagUtils.sleep(frameMillis); + } + } + + lastSwap = EagRuntime.steadyTimeMillis(); + } + + public static boolean contextLost() { + return PlatformInput.contextLost(); + } + + public static boolean wasResized() { + return PlatformInput.wasResized(); + } + + public static boolean wasVisualViewportResized() { + return PlatformInput.wasVisualViewportResized(); + } + + public static boolean supportsFullscreen() { + return PlatformInput.supportsFullscreen(); + } + + public static boolean isFullscreen() { + return PlatformInput.isFullscreen(); + } + + public static void toggleFullscreen() { + PlatformInput.toggleFullscreen(); + } + + public static float getDPI() { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastDPIUpdate > 250l) { + lastDPIUpdate = millis; + cacheDPI = PlatformInput.getDPI(); + } + return cacheDPI; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java index 84e1e22..501a1a7 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java @@ -1,343 +1,384 @@ -package net.lax1dude.eaglercraft.v1_8; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import java.nio.charset.StandardCharsets; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.function.Consumer; - -import net.hoosiertransfer.EaglerLUpdateThread; -import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE; -import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformAgent; -import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformOS; -import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; -import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; -import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; - -/** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EagRuntime { - - private static final Logger logger = LogManager.getLogger("EagRuntime"); - private static final Logger exceptionLogger = LogManager.getLogger("Exception"); - private static boolean ssl = false; - private static boolean offlineDownloadURL = false; - private static EnumPlatformAgent userAgent = null; - private static String userAgentString = null; - private static EnumPlatformOS operatingSystem = null; - private static EnumPlatformANGLE angleBackend = null; - - public static String getVersion() { - return "EagRuntimeX 1.0"; - } - - public static void create() { - logger.info("Version: {}", getVersion()); - PlatformRuntime.create(); - ssl = PlatformRuntime.requireSSL(); - offlineDownloadURL = PlatformRuntime.isOfflineDownloadURL(); - userAgent = PlatformRuntime.getPlatformAgent(); - userAgentString = PlatformRuntime.getUserAgentString(); - operatingSystem = PlatformRuntime.getPlatformOS(); - angleBackend = PlatformRuntime.getPlatformANGLE(); - UpdateService.initialize(); - EaglerXBungeeVersion.initialize(); - EaglercraftGPU.warmUpCache(); - List urls = EaglercraftVersion.enableUpdateService ? EaglercraftVersion.updateURLs : null; - EaglerLUpdateThread.startClientUpdate(urls); - } - - public static void destroy() { - PlatformRuntime.destroy(); - } - - public static EnumPlatformType getPlatformType() { - return PlatformRuntime.getPlatformType(); - } - - public static EnumPlatformAgent getPlatformAgent() { - return userAgent; - } - - public static String getUserAgentString() { - return userAgentString; - } - - public static EnumPlatformOS getPlatformOS() { - return operatingSystem; - } - - public static EnumPlatformANGLE getPlatformANGLE() { - return angleBackend; - } - - public static ByteBuffer allocateByteBuffer(int length) { - return PlatformRuntime.allocateByteBuffer(length); - } - - public static IntBuffer allocateIntBuffer(int length) { - return PlatformRuntime.allocateIntBuffer(length); - } - - public static FloatBuffer allocateFloatBuffer(int length) { - return PlatformRuntime.allocateFloatBuffer(length); - } - - public static void freeByteBuffer(ByteBuffer floatBuffer) { - PlatformRuntime.freeByteBuffer(floatBuffer); - } - - public static void freeIntBuffer(IntBuffer intBuffer) { - PlatformRuntime.freeIntBuffer(intBuffer); - } - - public static void freeFloatBuffer(FloatBuffer byteBuffer) { - PlatformRuntime.freeFloatBuffer(byteBuffer); - } - - public static byte[] getResourceBytes(String path) { - return PlatformAssets.getResourceBytes(path); - } - - public static InputStream getResourceStream(String path) { - byte[] b = PlatformAssets.getResourceBytes(path); - if (b != null) { - return new EaglerInputStream(b); - } else { - return null; - } - } - - public static String getResourceString(String path) { - byte[] bytes = PlatformAssets.getResourceBytes(path); - return bytes != null ? new String(bytes, StandardCharsets.UTF_8) : null; - } - - public static List getResourceLines(String path) { - byte[] bytes = PlatformAssets.getResourceBytes(path); - if (bytes != null) { - List ret = new ArrayList(); - try { - BufferedReader rd = new BufferedReader(new StringReader(path)); - String s; - while ((s = rd.readLine()) != null) { - ret.add(s); - } - } catch (IOException ex) { - // ?? - } - return ret; - } else { - return null; - } - } - - public static void debugPrintStackTraceToSTDERR(Throwable t) { - debugPrintStackTraceToSTDERR0("", t); - Throwable c = t.getCause(); - while (c != null) { - debugPrintStackTraceToSTDERR0("Caused by: ", c); - c = c.getCause(); - } - } - - private static void debugPrintStackTraceToSTDERR0(String pfx, Throwable t) { - System.err.println(pfx + t.toString()); - if (!PlatformRuntime.printJSExceptionIfBrowser(t)) { - getStackTrace(t, (s) -> { - System.err.println(" at " + s); - }); - } - } - - public static void getStackTrace(Throwable t, Consumer ret) { - PlatformRuntime.getStackTrace(t, ret); - } - - public static String[] getStackTraceElements(Throwable t) { - List lst = new ArrayList(); - PlatformRuntime.getStackTrace(t, (s) -> { - lst.add(s); - }); - return lst.toArray(new String[lst.size()]); - } - - public static String getStackTrace(Throwable t) { - StringBuilder sb = new StringBuilder(); - getStackTrace0(t, sb); - Throwable c = t.getCause(); - while (c != null) { - sb.append("\nCaused by: "); - getStackTrace0(c, sb); - c = c.getCause(); - } - return sb.toString(); - } - - private static void getStackTrace0(Throwable t, StringBuilder sb) { - sb.append(t.toString()); - getStackTrace(t, (s) -> { - sb.append('\n').append(" at ").append(s); - }); - } - - public static void debugPrintStackTrace(Throwable t) { - exceptionLogger.error(t); - } - - public static void dumpStack() { - try { - throw new Exception("Stack Trace"); - } catch (Exception ex) { - exceptionLogger.error(ex); - } - } - - public static void exit() { - PlatformRuntime.exit(); - } - - public static long maxMemory() { - return PlatformRuntime.maxMemory(); - } - - /** - * Note to skids: This doesn't do anything in TeaVM runtime! - */ - public static long totalMemory() { - return PlatformRuntime.totalMemory(); - } - - public static long freeMemory() { - return PlatformRuntime.freeMemory(); - } - - public static boolean requireSSL() { - return ssl; - } - - public static boolean isOfflineDownloadURL() { - return offlineDownloadURL; - } - - public static void showPopup(String msg) { - PlatformApplication.showPopup(msg); - } - - public static String getClipboard() { - return PlatformApplication.getClipboard(); - } - - public static void setClipboard(String text) { - PlatformApplication.setClipboard(text); - } - - public static void openLink(String url) { - PlatformApplication.openLink(url); - } - - public static void displayFileChooser(String mime, String ext) { - PlatformApplication.displayFileChooser(mime, ext); - } - - public static boolean fileChooserHasResult() { - return PlatformApplication.fileChooserHasResult(); - } - - public static FileChooserResult getFileChooserResult() { - return PlatformApplication.getFileChooserResult(); - } - - public static void clearFileChooserResult() { - PlatformApplication.clearFileChooserResult(); - } - - public static void setStorage(String name, byte[] data) { - PlatformApplication.setLocalStorage(name, data); - } - - public static byte[] getStorage(String data) { - return PlatformApplication.getLocalStorage(data); - } - - public static IClientConfigAdapter getConfiguration() { - return PlatformRuntime.getClientConfigAdapter(); - } - - public static String getRecText() { - return PlatformRuntime.getRecText(); - } - - public static void toggleRec() { - PlatformRuntime.toggleRec(); - } - - public static boolean recSupported() { - return PlatformRuntime.recSupported(); - } - - public static void openCreditsPopup(String text) { - PlatformApplication.openCreditsPopup(text); - } - - public static void downloadFileWithName(String fileName, byte[] fileContents) { - PlatformApplication.downloadFileWithName(fileName, fileContents); - } - - public static String currentThreadName() { - return PlatformRuntime.currentThreadName(); - } - - public static void showDebugConsole() { - PlatformApplication.showDebugConsole(); - } - - public static Calendar getLocaleCalendar() { - return Calendar.getInstance(); // TODO: fix teavm calendar's time zone offset - } - - public static T fixDateFormat(T input) { - input.setCalendar(getLocaleCalendar()); - return input; - } - - public static void setMCServerWindowGlobal(String url) { - PlatformApplication.setMCServerWindowGlobal(url); - } -} +package net.lax1dude.eaglercraft.v1_8; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import net.hoosiertransfer.EaglerLUpdateThread; +import net.lax1dude.eaglercraft.v1_8.internal.EaglerMissingResourceException; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformAgent; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformOS; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; +import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.recording.ScreenRecordingController; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; + +/** + * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EagRuntime { + + private static final Logger logger = LogManager.getLogger("EagRuntime"); + private static final Logger exceptionLogger = LogManager.getLogger("Exception"); + private static boolean ssl = false; + private static boolean offlineDownloadURL = false; + private static EnumPlatformAgent userAgent = null; + private static String userAgentString = null; + private static EnumPlatformOS operatingSystem = null; + private static EnumPlatformANGLE angleBackend = null; + + public static String getVersion() { + return "EagRuntimeX 1.0"; + } + + public static void create() { + logger.info("Version: {}", getVersion()); + PlatformRuntime.create(); + ssl = PlatformRuntime.requireSSL(); + offlineDownloadURL = PlatformRuntime.isOfflineDownloadURL(); + userAgent = PlatformRuntime.getPlatformAgent(); + userAgentString = PlatformRuntime.getUserAgentString(); + operatingSystem = PlatformRuntime.getPlatformOS(); + angleBackend = PlatformRuntime.getPlatformANGLE(); + UpdateService.initialize(); + EaglerXBungeeVersion.initialize(); + EaglercraftGPU.warmUpCache(); + List urls = EaglercraftVersion.enableUpdateService ? EaglercraftVersion.updateURLs : null; + EaglerLUpdateThread.startClientUpdate(urls); + ScreenRecordingController.initialize(); + PlatformRuntime.postCreate(); + } + + public static void destroy() { + PlatformRuntime.destroy(); + } + + public static EnumPlatformType getPlatformType() { + return PlatformRuntime.getPlatformType(); + } + + public static EnumPlatformAgent getPlatformAgent() { + return userAgent; + } + + public static String getUserAgentString() { + return userAgentString; + } + + public static EnumPlatformOS getPlatformOS() { + return operatingSystem; + } + + public static EnumPlatformANGLE getPlatformANGLE() { + return angleBackend; + } + + public static ByteBuffer allocateByteBuffer(int length) { + return PlatformRuntime.allocateByteBuffer(length); + } + + public static IntBuffer allocateIntBuffer(int length) { + return PlatformRuntime.allocateIntBuffer(length); + } + + public static FloatBuffer allocateFloatBuffer(int length) { + return PlatformRuntime.allocateFloatBuffer(length); + } + + public static void freeByteBuffer(ByteBuffer floatBuffer) { + PlatformRuntime.freeByteBuffer(floatBuffer); + } + + public static void freeIntBuffer(IntBuffer intBuffer) { + PlatformRuntime.freeIntBuffer(intBuffer); + } + + public static void freeFloatBuffer(FloatBuffer byteBuffer) { + PlatformRuntime.freeFloatBuffer(byteBuffer); + } + + public static boolean getResourceExists(String path) { + return PlatformAssets.getResourceExists(path); + } + + public static byte[] getResourceBytes(String path) { + return PlatformAssets.getResourceBytes(path); + } + + public static byte[] getRequiredResourceBytes(String path) { + byte[] ret = PlatformAssets.getResourceBytes(path); + if(ret == null) { + throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path); + } + return ret; + } + + public static InputStream getResourceStream(String path) { + byte[] b = PlatformAssets.getResourceBytes(path); + if (b != null) { + return new EaglerInputStream(b); + } else { + return null; + } + } + + public static InputStream getRequiredResourceStream(String path) { + byte[] ret = PlatformAssets.getResourceBytes(path); + if(ret == null) { + throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path); + } + return new EaglerInputStream(ret); + } + + public static String getResourceString(String path) { + byte[] bytes = PlatformAssets.getResourceBytes(path); + return bytes != null ? new String(bytes, StandardCharsets.UTF_8) : null; + } + + public static String getRequiredResourceString(String path) { + byte[] ret = PlatformAssets.getResourceBytes(path); + if(ret == null) { + throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path); + } + return new String(ret, StandardCharsets.UTF_8); + } + + public static List getResourceLines(String path) { + byte[] bytes = PlatformAssets.getResourceBytes(path); + if(bytes != null) { + List ret = new ArrayList<>(); + try { + BufferedReader rd = new BufferedReader(new InputStreamReader(new EaglerInputStream(bytes), StandardCharsets.UTF_8)); + String s; + while ((s = rd.readLine()) != null) { + ret.add(s); + } + } catch (IOException ex) { + // ?? + return null; + } + return ret; + } else { + return null; + } + } + + public static List getRequiredResourceLines(String path) { + List ret = getResourceLines(path); + if(ret == null) { + throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path); + } + return ret; + } + + public static void debugPrintStackTraceToSTDERR(Throwable t) { + debugPrintStackTraceToSTDERR0("", t); + Throwable c = t.getCause(); + while (c != null) { + debugPrintStackTraceToSTDERR0("Caused by: ", c); + c = c.getCause(); + } + } + + private static void debugPrintStackTraceToSTDERR0(String pfx, Throwable t) { + System.err.println(pfx + t.toString()); + if (!PlatformRuntime.printJSExceptionIfBrowser(t)) { + getStackTrace(t, (s) -> { + System.err.println(" at " + s); + }); + } + } + + public static void getStackTrace(Throwable t, Consumer ret) { + PlatformRuntime.getStackTrace(t, ret); + } + + public static String[] getStackTraceElements(Throwable t) { + List lst = new ArrayList<>(); + PlatformRuntime.getStackTrace(t, (s) -> { + lst.add(s); + }); + return lst.toArray(new String[lst.size()]); + } + + public static String getStackTrace(Throwable t) { + StringBuilder sb = new StringBuilder(); + getStackTrace0(t, sb); + Throwable c = t.getCause(); + while (c != null) { + sb.append("\nCaused by: "); + getStackTrace0(c, sb); + c = c.getCause(); + } + return sb.toString(); + } + + private static void getStackTrace0(Throwable t, StringBuilder sb) { + sb.append(t.toString()); + getStackTrace(t, (s) -> { + sb.append('\n').append(" at ").append(s); + }); + } + + public static void debugPrintStackTrace(Throwable t) { + exceptionLogger.error(t); + } + + public static void dumpStack() { + try { + throw new Exception("Stack Trace"); + } catch (Exception ex) { + exceptionLogger.error(ex); + } + } + + public static void exit() { + PlatformRuntime.exit(); + } + + /** + * Note to skids: This doesn't do anything in javascript runtime! + */ + public static long maxMemory() { + return PlatformRuntime.maxMemory(); + } + + /** + * Note to skids: This doesn't do anything in javascript runtime! + */ + public static long totalMemory() { + return PlatformRuntime.totalMemory(); + } + + /** + * Note to skids: This doesn't do anything in javascript runtime! + */ + public static long freeMemory() { + return PlatformRuntime.freeMemory(); + } + + public static boolean requireSSL() { + return ssl; + } + + public static boolean isOfflineDownloadURL() { + return offlineDownloadURL; + } + + public static void showPopup(String msg) { + PlatformApplication.showPopup(msg); + } + + public static String getClipboard() { + return PlatformApplication.getClipboard(); + } + + public static void setClipboard(String text) { + PlatformApplication.setClipboard(text); + } + + public static void openLink(String url) { + PlatformApplication.openLink(url); + } + + public static void displayFileChooser(String mime, String ext) { + PlatformApplication.displayFileChooser(mime, ext); + } + + public static boolean fileChooserHasResult() { + return PlatformApplication.fileChooserHasResult(); + } + + public static FileChooserResult getFileChooserResult() { + return PlatformApplication.getFileChooserResult(); + } + + public static void clearFileChooserResult() { + PlatformApplication.clearFileChooserResult(); + } + + public static void setStorage(String name, byte[] data) { + PlatformApplication.setLocalStorage(name, data); + } + + public static byte[] getStorage(String data) { + return PlatformApplication.getLocalStorage(data); + } + + public static IClientConfigAdapter getConfiguration() { + return PlatformRuntime.getClientConfigAdapter(); + } + + public static void openCreditsPopup(String text) { + PlatformApplication.openCreditsPopup(text); + } + + public static void downloadFileWithName(String fileName, byte[] fileContents) { + PlatformApplication.downloadFileWithName(fileName, fileContents); + } + + public static String currentThreadName() { + return PlatformRuntime.currentThreadName(); + } + + public static void showDebugConsole() { + PlatformApplication.showDebugConsole(); + } + + public static void setDisplayBootMenuNextRefresh(boolean en) { + PlatformRuntime.setDisplayBootMenuNextRefresh(en); + } + + public static void setMCServerWindowGlobal(String url) { + PlatformApplication.setMCServerWindowGlobal(url); + } + + public static long steadyTimeMillis() { + return PlatformRuntime.steadyTimeMillis(); + } + + public static long nanoTime() { + return PlatformRuntime.nanoTime(); + } + + public static void immediateContinue() { + PlatformRuntime.immediateContinue(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagUtils.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagUtils.java index 7ba9fd2..808aa6c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagUtils.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagUtils.java @@ -1,40 +1,33 @@ package net.lax1dude.eaglercraft.v1_8; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class EagUtils { - + private static final String hex = "0123456789ABCDEF"; public static final Pattern splitPattern = Pattern.compile("(\\r\\n|\\n|\\r)"); - + public static String hexString(int value, int digits) { String ret = ""; - for (int i = 0, l = digits << 2; i < l; i += 4) { + for(int i = 0, l = digits << 2; i < l; i += 4) { ret = hex.charAt((value >> i) & 0xF) + ret; } return ret; @@ -43,54 +36,62 @@ public class EagUtils { public static String[] linesArray(String input) { return splitPattern.split(input); } - + public static List linesList(String input) { return Arrays.asList(splitPattern.split(input)); } - + public static int decodeHex(CharSequence num) { int ret = 0; - for (int i = 0, l = num.length(); i < l; ++i) { + for(int i = 0, l = num.length(); i < l; ++i) { ret = ret << 4; int v = hex.indexOf(num.charAt(i)); - if (v >= 0) { + if(v >= 0) { ret |= v; } } return ret; } - + public static int decodeHexByte(CharSequence str, int off) { return str.length() < off + 2 ? decodeHex(str.subSequence(off, 2)) : 0; } - + public static void sleep(long millis) { try { Thread.sleep(millis); - } catch (InterruptedException ex) { + }catch(InterruptedException ex) { } } - + public static String toASCIIEagler(String str) { char[] ascii = new char[str.length()]; - for (int i = 0; i < ascii.length; ++i) { - int c = (int) str.charAt(i); - if (c < 32 || c > 126) { + for(int i = 0; i < ascii.length; ++i) { + int c = (int)str.charAt(i); + if(c < 32 || c > 126) { ascii[i] = '_'; - } else { - ascii[i] = (char) c; + }else { + ascii[i] = (char)c; } } return new String(ascii); } - + public static void validateASCIIEagler(String str) { - for (int i = 0, l = str.length(); i < l; ++i) { - int c = (int) str.charAt(i); - if (c < 32 || c > 126) { + for(int i = 0, l = str.length(); i < l; ++i) { + int c = (int)str.charAt(i); + if(c < 32 || c > 126) { throw new IllegalArgumentException("invalid ascii"); } } } + public static EaglercraftUUID makeClientBrandUUID(String name) { + return EaglercraftUUID.nameUUIDFromBytes(("EaglercraftXClient:" + name).getBytes(StandardCharsets.UTF_8)); + } + + public static EaglercraftUUID makeClientBrandUUIDLegacy(String name) { + return EaglercraftUUID.nameUUIDFromBytes(("EaglercraftXClientOld:" + name).getBytes(StandardCharsets.UTF_8)); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerOutputStream.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerOutputStream.java index a39c268..64dab6d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerOutputStream.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerOutputStream.java @@ -1,79 +1,84 @@ -package net.lax1dude.eaglercraft.v1_8; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglerOutputStream extends OutputStream { - - protected byte buf[]; - protected int count; - - public EaglerOutputStream() { - this(32); - } - - public EaglerOutputStream(int size) { - if (size < 0) { - throw new IllegalArgumentException("Negative initial size: " + size); - } - buf = new byte[size]; - } - - private void ensureCapacity(int minCapacity) { - if (buf.length < minCapacity) { - minCapacity = Math.max(minCapacity, buf.length * 3 / 2); - buf = Arrays.copyOf(buf, minCapacity); - } - } - - public void write(int b) { - ensureCapacity(count + 1); - buf[count] = (byte) b; - count += 1; - } - - public void write(byte b[], int off, int len) { - ensureCapacity(count + len); - System.arraycopy(b, off, buf, count, len); - count += len; - } - - public void writeBytes(byte b[]) { - write(b, 0, b.length); - } - - public void writeTo(OutputStream out) throws IOException { - out.write(buf, 0, count); - } - - public void reset() { - count = 0; - } - - public byte[] toByteArray() { - return Arrays.copyOf(buf, count); - } - - public int size() { - return count; - } - - public void close() throws IOException { - } -} +package net.lax1dude.eaglercraft.v1_8; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerOutputStream extends OutputStream { + + protected byte buf[]; + protected int count; + + public EaglerOutputStream() { + this(32); + } + + public EaglerOutputStream(int size) { + if (size < 0) { + throw new IllegalArgumentException("Negative initial size: " + size); + } + buf = new byte[size]; + } + + private void ensureCapacity(int minCapacity) { + if (buf.length < minCapacity) { + minCapacity = Math.max(minCapacity, buf.length * 3 / 2); + buf = Arrays.copyOf(buf, minCapacity); + } + } + + public void write(int b) { + ensureCapacity(count + 1); + buf[count] = (byte) b; + count += 1; + } + + public void write(byte b[], int off, int len) { + ensureCapacity(count + len); + System.arraycopy(b, off, buf, count, len); + count += len; + } + + public void writeBytes(byte b[]) { + write(b, 0, b.length); + } + + public void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); + } + + public void reset() { + count = 0; + } + + public byte[] toByteArray() { + return Arrays.copyOf(buf, count); + } + + public int size() { + return count; + } + + public void skipBytes(int num) { + ensureCapacity(count + num); + count += num; + } + + public void close() throws IOException { + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java index 9a53c77..d1bd0fd 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java @@ -28,10 +28,7 @@ public class EaglerXBungeeVersion { private static String pluginFilename = null; public static void initialize() { - String pluginVersionJson = EagRuntime.getResourceString("plugin_version.json"); - if(pluginVersionJson == null) { - throw new RuntimeException("File \"plugin_version.json\" is missing in the epk!"); - } + String pluginVersionJson = EagRuntime.getRequiredResourceString("plugin_version.json"); JSONObject json = new JSONObject(pluginVersionJson); pluginName = json.getString("pluginName"); pluginVersion = json.getString("pluginVersion"); @@ -76,11 +73,7 @@ public class EaglerXBungeeVersion { } public static byte[] getPluginDownload() { - byte[] ret = EagRuntime.getResourceBytes(pluginFileEPK); - if(ret == null) { - throw new RuntimeException("File \"" + pluginFileEPK + "\" is missing in the epk!"); - } - return ret; + return EagRuntime.getRequiredResourceBytes(pluginFileEPK); } public static void startPluginDownload() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftSoundManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftSoundManager.java index d2c0e59..86fff43 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftSoundManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftSoundManager.java @@ -25,49 +25,41 @@ import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class EaglercraftSoundManager { - + protected static class ActiveSoundEvent { protected final EaglercraftSoundManager manager; - + protected final ISound soundInstance; protected final SoundCategory soundCategory; protected final SoundPoolEntry soundConfig; protected IAudioHandle soundHandle; - + protected float activeX; protected float activeY; protected float activeZ; - + protected float activePitch; protected float activeGain; - + protected int repeatCounter = 0; protected boolean paused = false; - + protected ActiveSoundEvent(EaglercraftSoundManager manager, ISound soundInstance, SoundCategory soundCategory, SoundPoolEntry soundConfig, IAudioHandle soundHandle) { this.manager = manager; @@ -81,50 +73,49 @@ public class EaglercraftSoundManager { this.activePitch = soundInstance.getPitch(); this.activeGain = soundInstance.getVolume(); } - + protected void updateLocation() { float x = soundInstance.getXPosF(); float y = soundInstance.getYPosF(); float z = soundInstance.getZPosF(); float pitch = soundInstance.getPitch(); float gain = soundInstance.getVolume(); - if (x != activeX || y != activeY || z != activeZ) { + if(x != activeX || y != activeY || z != activeZ) { soundHandle.move(x, y, z); activeX = x; activeY = y; activeZ = z; } - if (pitch != activePitch) { - soundHandle.pitch(MathHelper.clamp_float(pitch * (float) soundConfig.getPitch(), 0.5f, 2.0f)); + if(pitch != activePitch) { + soundHandle.pitch(MathHelper.clamp_float(pitch * (float)soundConfig.getPitch(), 0.5f, 2.0f)); activePitch = pitch; } - if (gain != activeGain) { + if(gain != activeGain) { float attenuatedGain = gain * manager.categoryVolumes[SoundCategory.MASTER.getCategoryId()] * - (soundCategory == SoundCategory.MASTER ? 1.0f - : manager.categoryVolumes[soundCategory.getCategoryId()]) - * (float) soundConfig.getVolume(); + (soundCategory == SoundCategory.MASTER ? 1.0f : manager.categoryVolumes[soundCategory.getCategoryId()]) + * (float)soundConfig.getVolume(); soundHandle.gain(MathHelper.clamp_float(attenuatedGain, 0.0f, 1.0f)); activeGain = gain; } } - + } - + protected static class WaitingSoundEvent { - + protected final ISound playSound; protected int playTicks; protected boolean paused = false; - + private WaitingSoundEvent(ISound playSound, int playTicks) { this.playSound = playSound; this.playTicks = playTicks; } - + } - + private static final Logger logger = LogManager.getLogger("SoundManager"); - + private final GameSettings settings; private final SoundHandler handler; private final float[] categoryVolumes; @@ -139,150 +130,148 @@ public class EaglercraftSoundManager { settings.getSoundLevel(SoundCategory.RECORDS), settings.getSoundLevel(SoundCategory.WEATHER), settings.getSoundLevel(SoundCategory.BLOCKS), settings.getSoundLevel(SoundCategory.MOBS), settings.getSoundLevel(SoundCategory.ANIMALS), settings.getSoundLevel(SoundCategory.PLAYERS), - settings.getSoundLevel(SoundCategory.AMBIENT), settings.getSoundLevel(SoundCategory.VOICE) + settings.getSoundLevel(SoundCategory.AMBIENT) }; - activeSounds = new LinkedList(); - queuedSounds = new LinkedList(); + activeSounds = new LinkedList<>(); + queuedSounds = new LinkedList<>(); } public void unloadSoundSystem() { // handled by PlatformApplication } - + public void reloadSoundSystem() { PlatformAudio.flushAudioCache(); } - + public void setSoundCategoryVolume(SoundCategory category, float volume) { categoryVolumes[category.getCategoryId()] = volume; Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if ((category == SoundCategory.MASTER || evt.soundCategory == category) + if((category == SoundCategory.MASTER || evt.soundCategory == category) && !evt.soundHandle.shouldFree()) { - float newVolume = (evt.activeGain = evt.soundInstance.getVolume()) - * categoryVolumes[SoundCategory.MASTER.getCategoryId()] * - (evt.soundCategory == SoundCategory.MASTER ? 1.0f - : categoryVolumes[evt.soundCategory.getCategoryId()]) - * (float) evt.soundConfig.getVolume(); + float newVolume = (evt.activeGain = evt.soundInstance.getVolume()) * categoryVolumes[SoundCategory.MASTER.getCategoryId()] * + (evt.soundCategory == SoundCategory.MASTER ? 1.0f : categoryVolumes[evt.soundCategory.getCategoryId()]) + * (float)evt.soundConfig.getVolume(); newVolume = MathHelper.clamp_float(newVolume, 0.0f, 1.0f); - if (newVolume > 0.0f) { + if(newVolume > 0.0f) { evt.soundHandle.gain(newVolume); - } else { + }else { evt.soundHandle.end(); soundItr.remove(); } } } } - + public void stopAllSounds() { Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if (!evt.soundHandle.shouldFree()) { + if(!evt.soundHandle.shouldFree()) { evt.soundHandle.end(); } } activeSounds.clear(); } - + public void pauseAllSounds() { Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if (!evt.soundHandle.shouldFree()) { + if(!evt.soundHandle.shouldFree()) { evt.soundHandle.pause(true); evt.paused = true; } } Iterator soundItr2 = queuedSounds.iterator(); - while (soundItr2.hasNext()) { + while(soundItr2.hasNext()) { soundItr2.next().paused = true; } } - + public void resumeAllSounds() { Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if (!evt.soundHandle.shouldFree()) { + if(!evt.soundHandle.shouldFree()) { evt.soundHandle.pause(false); evt.paused = false; } } Iterator soundItr2 = queuedSounds.iterator(); - while (soundItr2.hasNext()) { + while(soundItr2.hasNext()) { soundItr2.next().paused = false; } } - + public void updateAllSounds() { Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if (!evt.paused && (evt.soundInstance instanceof ITickable)) { + if(!evt.paused && (evt.soundInstance instanceof ITickable)) { boolean destroy = false; try { - ((ITickable) evt.soundInstance).update(); + ((ITickable)evt.soundInstance).update(); if ((evt.soundInstance instanceof ITickableSound) && ((ITickableSound) evt.soundInstance).isDonePlaying()) { destroy = true; } - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Error ticking sound: {}", t.toString()); logger.error(t); destroy = true; } - if (destroy) { - if (!evt.soundHandle.shouldFree()) { + if(destroy) { + if(!evt.soundHandle.shouldFree()) { evt.soundHandle.end(); } soundItr.remove(); } } - if (evt.soundHandle.shouldFree()) { - if (evt.soundInstance.canRepeat()) { - if (!evt.paused && ++evt.repeatCounter > evt.soundInstance.getRepeatDelay()) { + if(evt.soundHandle.shouldFree()) { + if(evt.soundInstance.canRepeat()) { + if(!evt.paused && ++evt.repeatCounter > evt.soundInstance.getRepeatDelay()) { evt.repeatCounter = 0; evt.updateLocation(); evt.soundHandle.restart(); } - } else { + }else { soundItr.remove(); } - } else { + }else { evt.updateLocation(); } } Iterator soundItr2 = queuedSounds.iterator(); - while (soundItr2.hasNext()) { + while(soundItr2.hasNext()) { WaitingSoundEvent evt = soundItr2.next(); - if (!evt.paused && --evt.playTicks <= 0) { + if(!evt.paused && --evt.playTicks <= 0) { soundItr2.remove(); playSound(evt.playSound); } } PlatformAudio.clearAudioCache(); } - + public boolean isSoundPlaying(ISound sound) { Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if (evt.soundInstance == sound) { + if(evt.soundInstance == sound) { return !evt.soundHandle.shouldFree(); } } return false; } - + public void stopSound(ISound sound) { Iterator soundItr = activeSounds.iterator(); - while (soundItr.hasNext()) { + while(soundItr.hasNext()) { ActiveSoundEvent evt = soundItr.next(); - if (evt.soundInstance == sound) { - if (!evt.soundHandle.shouldFree()) { + if(evt.soundInstance == sound) { + if(!evt.soundHandle.shouldFree()) { evt.soundHandle.end(); soundItr.remove(); return; @@ -290,8 +279,8 @@ public class EaglercraftSoundManager { } } Iterator soundItr2 = queuedSounds.iterator(); - while (soundItr2.hasNext()) { - if (soundItr2.next().playSound == sound) { + while(soundItr2.hasNext()) { + if(soundItr2.next().playSound == sound) { soundItr2.remove(); } } @@ -301,59 +290,54 @@ public class EaglercraftSoundManager { try { return EaglerInputStream.inputStreamToBytesQuiet(Minecraft.getMinecraft().getResourceManager() .getResource(new ResourceLocation(filename)).getInputStream()); - } catch (Throwable t) { + }catch(Throwable t) { return null; } }; public void playSound(ISound sound) { - if (!PlatformAudio.available()) { + if(!PlatformAudio.available()) { return; } - if (sound != null && categoryVolumes[SoundCategory.MASTER.getCategoryId()] > 0.0f) { + if(sound != null && categoryVolumes[SoundCategory.MASTER.getCategoryId()] > 0.0f) { SoundEventAccessorComposite accessor = handler.getSound(sound.getSoundLocation()); - if (accessor == null) { + if(accessor == null) { logger.warn("Unable to play unknown soundEvent(1): {}", sound.getSoundLocation().toString()); - } else { + }else { SoundPoolEntry etr = accessor.cloneEntry(); if (etr == SoundHandler.missing_sound) { logger.warn("Unable to play empty soundEvent(2): {}", etr.getSoundPoolEntryLocation().toString()); - } else { + }else { ResourceLocation lc = etr.getSoundPoolEntryLocation(); IAudioResource trk; - if (EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP) { - trk = PlatformAudio.loadAudioDataNew(lc.toString(), !etr.isStreamingSound(), - browserResourcePackLoader); - } else { + if(EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP) { + trk = PlatformAudio.loadAudioDataNew(lc.toString(), !etr.isStreamingSound(), browserResourcePackLoader); + }else { trk = PlatformAudio.loadAudioData( - "/assets/" + lc.getResourceDomain() + "/" + lc.getResourcePath(), - !etr.isStreamingSound()); + "/assets/" + lc.getResourceDomain() + "/" + lc.getResourcePath(), !etr.isStreamingSound()); } - if (trk == null) { + if(trk == null) { logger.warn("Unable to play unknown soundEvent(3): {}", sound.getSoundLocation().toString()); - } else { - - ActiveSoundEvent newSound = new ActiveSoundEvent(this, sound, accessor.getSoundCategory(), etr, - null); - - float pitch = MathHelper.clamp_float(newSound.activePitch * (float) etr.getPitch(), 0.5f, 2.0f); - float attenuatedGain = newSound.activeGain - * categoryVolumes[SoundCategory.MASTER.getCategoryId()] * - (accessor.getSoundCategory() == SoundCategory.MASTER ? 1.0f - : categoryVolumes[accessor.getSoundCategory().getCategoryId()]) - * (float) etr.getVolume(); + }else { + + ActiveSoundEvent newSound = new ActiveSoundEvent(this, sound, accessor.getSoundCategory(), etr, null); + float pitch = MathHelper.clamp_float(newSound.activePitch * (float)etr.getPitch(), 0.5f, 2.0f); + float attenuatedGain = newSound.activeGain * categoryVolumes[SoundCategory.MASTER.getCategoryId()] * + (accessor.getSoundCategory() == SoundCategory.MASTER ? 1.0f : + categoryVolumes[accessor.getSoundCategory().getCategoryId()]) * (float)etr.getVolume(); + AttenuationType tp = sound.getAttenuationType(); - if (tp == AttenuationType.LINEAR) { + if(tp == AttenuationType.LINEAR) { newSound.soundHandle = PlatformAudio.beginPlayback(trk, newSound.activeX, newSound.activeY, newSound.activeZ, attenuatedGain, pitch); - } else { + }else { newSound.soundHandle = PlatformAudio.beginPlaybackStatic(trk, attenuatedGain, pitch); } - - if (newSound.soundHandle == null) { + + if(newSound.soundHandle == null) { logger.error("Unable to play soundEvent(4): {}", sound.getSoundLocation().toString()); - } else { + }else { activeSounds.add(newSound); } } @@ -361,29 +345,28 @@ public class EaglercraftSoundManager { } } } - + public void playDelayedSound(ISound sound, int delay) { queuedSounds.add(new WaitingSoundEvent(sound, delay)); } - + public void setListener(EntityPlayer player, float partialTicks) { - if (!PlatformAudio.available()) { + if(!PlatformAudio.available()) { return; } - if (player != null) { + if(player != null) { try { float f = player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * partialTicks; float f1 = player.prevRotationYaw + (player.rotationYaw - player.prevRotationYaw) * partialTicks; double d0 = player.prevPosX + (player.posX - player.prevPosX) * (double) partialTicks; - double d1 = player.prevPosY + (player.posY - player.prevPosY) * (double) partialTicks - + (double) player.getEyeHeight(); + double d1 = player.prevPosY + (player.posY - player.prevPosY) * (double) partialTicks + (double) player.getEyeHeight(); double d2 = player.prevPosZ + (player.posZ - player.prevPosZ) * (double) partialTicks; - PlatformAudio.setListener((float) d0, (float) d1, (float) d2, f, f1); - } catch (Throwable t) { + PlatformAudio.setListener((float)d0, (float)d1, (float)d2, f, f1); + }catch(Throwable t) { // eaglercraft 1.5.2 had Infinity/NaN crashes for this function which // couldn't be resolved via if statement checks in the above variables } } } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftUUID.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftUUID.java index d3d771d..2130625 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftUUID.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftUUID.java @@ -1,244 +1,254 @@ -package net.lax1dude.eaglercraft.v1_8; - -import net.lax1dude.eaglercraft.v1_8.crypto.MD5Digest; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglercraftUUID implements Comparable { - - public final long msb; - public final long lsb; - - public EaglercraftUUID(long msb, long lsb) { - this.msb = msb; - this.lsb = lsb; - } - - public EaglercraftUUID(byte[] uuid) { - long msb = 0; - long lsb = 0; - for (int i = 0; i < 8; i++) - msb = (msb << 8) | (uuid[i] & 0xff); - for (int i = 8; i < 16; i++) - lsb = (lsb << 8) | (uuid[i] & 0xff); - this.msb = msb; - this.lsb = lsb; - } - - public EaglercraftUUID(String uuid) { - String[] components = uuid.split("-"); - if (components.length != 5) - throw new IllegalArgumentException("Invalid UUID string: " + uuid); - for (int i = 0; i < 5; i++) - components[i] = "0x" + components[i]; - - long mostSigBits = Long.decode(components[0]).longValue(); - mostSigBits <<= 16; - mostSigBits |= Long.decode(components[1]).longValue(); - mostSigBits <<= 16; - mostSigBits |= Long.decode(components[2]).longValue(); - - long leastSigBits = Long.decode(components[3]).longValue(); - leastSigBits <<= 48; - leastSigBits |= Long.decode(components[4]).longValue(); - - this.msb = mostSigBits; - this.lsb = leastSigBits; - } - - private static byte long7(long x) { - return (byte) (x >> 56); - } - - private static byte long6(long x) { - return (byte) (x >> 48); - } - - private static byte long5(long x) { - return (byte) (x >> 40); - } - - private static byte long4(long x) { - return (byte) (x >> 32); - } - - private static byte long3(long x) { - return (byte) (x >> 24); - } - - private static byte long2(long x) { - return (byte) (x >> 16); - } - - private static byte long1(long x) { - return (byte) (x >> 8); - } - - private static byte long0(long x) { - return (byte) (x); - } - - public byte[] getBytes() { - byte[] ret = new byte[16]; - ret[0] = long7(msb); - ret[1] = long6(msb); - ret[2] = long5(msb); - ret[3] = long4(msb); - ret[4] = long3(msb); - ret[5] = long2(msb); - ret[6] = long1(msb); - ret[7] = long0(msb); - ret[8] = long7(lsb); - ret[9] = long6(lsb); - ret[10] = long5(lsb); - ret[11] = long4(lsb); - ret[12] = long3(lsb); - ret[13] = long2(lsb); - ret[14] = long1(lsb); - ret[15] = long0(lsb); - return ret; - } - - @Override - public String toString() { - return (digits(msb >> 32, 8) + "-" + digits(msb >> 16, 4) + "-" + digits(msb, 4) + "-" + digits(lsb >> 48, 4) - + "-" + digits(lsb, 12)); - } - - private static String digits(long val, int digits) { - long hi = 1L << (digits * 4); - return Long.toHexString(hi | (val & (hi - 1))).substring(1); - } - - @Override - public int hashCode() { - long hilo = msb ^ lsb; - return ((int) (hilo >> 32)) ^ (int) hilo; - } - - @Override - public boolean equals(Object o) { - return (o instanceof EaglercraftUUID) && ((EaglercraftUUID) o).lsb == lsb && ((EaglercraftUUID) o).msb == msb; - } - - public long getMostSignificantBits() { - return msb; - } - - public long getLeastSignificantBits() { - return lsb; - } - - private static final String HEX = "0123456789ABCDEF"; - - private static int nibbleValue(char c) { - int v = HEX.indexOf(Character.toUpperCase(c)); - if (v == -1) { - return 0; - } else { - return v; - } - } - - private static long parse4Nibbles(String name, int pos) { - int ch1 = nibbleValue(name.charAt(pos)); - int ch2 = nibbleValue(name.charAt(pos + 1)); - int ch3 = nibbleValue(name.charAt(pos + 2)); - int ch4 = nibbleValue(name.charAt(pos + 3)); - return (ch1 << 12) | (ch2 << 8) | (ch3 << 4) | ch4; - } - - public static EaglercraftUUID fromString(String name) { - if (name.length() == 36) { - char ch1 = name.charAt(8); - char ch2 = name.charAt(13); - char ch3 = name.charAt(18); - char ch4 = name.charAt(23); - if (ch1 == '-' && ch2 == '-' && ch3 == '-' && ch4 == '-') { - long msb1 = parse4Nibbles(name, 0); - long msb2 = parse4Nibbles(name, 4); - long msb3 = parse4Nibbles(name, 9); - long msb4 = parse4Nibbles(name, 14); - long lsb1 = parse4Nibbles(name, 19); - long lsb2 = parse4Nibbles(name, 24); - long lsb3 = parse4Nibbles(name, 28); - long lsb4 = parse4Nibbles(name, 32); - if ((msb1 | msb2 | msb3 | msb4 | lsb1 | lsb2 | lsb3 | lsb4) >= 0) { - return new EaglercraftUUID(msb1 << 48 | msb2 << 32 | msb3 << 16 | msb4, - lsb1 << 48 | lsb2 << 32 | lsb3 << 16 | lsb4); - } - } - } - return fromString1(name); - } - - private static EaglercraftUUID fromString1(String name) { - int len = name.length(); - if (len > 36) { - throw new IllegalArgumentException("UUID string too large"); - } - - int dash1 = name.indexOf('-', 0); - int dash2 = name.indexOf('-', dash1 + 1); - int dash3 = name.indexOf('-', dash2 + 1); - int dash4 = name.indexOf('-', dash3 + 1); - int dash5 = name.indexOf('-', dash4 + 1); - - if (dash4 < 0 || dash5 >= 0) { - throw new IllegalArgumentException("Invalid UUID string: " + name); - } - - long mostSigBits = JDKBackports.parseLong(name, 0, dash1, 16) & 0xffffffffL; - mostSigBits <<= 16; - mostSigBits |= JDKBackports.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL; - mostSigBits <<= 16; - mostSigBits |= JDKBackports.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL; - long leastSigBits = JDKBackports.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL; - leastSigBits <<= 48; - leastSigBits |= JDKBackports.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL; - - return new EaglercraftUUID(mostSigBits, leastSigBits); - } - - public static EaglercraftUUID nameUUIDFromBytes(byte[] bytes) { - MD5Digest dg = new MD5Digest(); - dg.update(bytes, 0, bytes.length); - byte[] md5Bytes = new byte[16]; - dg.doFinal(md5Bytes, 0); - md5Bytes[6] &= 0x0f; - md5Bytes[6] |= 0x30; - md5Bytes[8] &= 0x3f; - md5Bytes[8] |= 0x80; - return new EaglercraftUUID(md5Bytes); - } - - public static EaglercraftUUID randomUUID() { - byte[] randomBytes = new byte[16]; - (new EaglercraftRandom()).nextBytes(randomBytes); - randomBytes[6] &= 0x0f; /* clear version */ - randomBytes[6] |= 0x40; /* set to version 4 */ - randomBytes[8] &= 0x3f; /* clear variant */ - randomBytes[8] |= 0x80; /* set to IETF variant */ - return new EaglercraftUUID(randomBytes); - } - - @Override - public int compareTo(EaglercraftUUID val) { - return (this.msb < val.msb ? -1 - : (this.msb > val.msb ? 1 : (this.lsb < val.lsb ? -1 : (this.lsb > val.lsb ? 1 : 0)))); - } - +package net.lax1dude.eaglercraft.v1_8; + +import net.lax1dude.eaglercraft.v1_8.crypto.MD5Digest; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglercraftUUID implements Comparable { + + public final long msb; + public final long lsb; + private int hash = 0; + private boolean hasHash; + + public EaglercraftUUID(long msb, long lsb) { + this.msb = msb; + this.lsb = lsb; + } + + public EaglercraftUUID(byte[] uuid) { + long msb = 0; + long lsb = 0; + for (int i = 0; i < 8; i++) + msb = (msb << 8) | (uuid[i] & 0xff); + for (int i = 8; i < 16; i++) + lsb = (lsb << 8) | (uuid[i] & 0xff); + this.msb = msb; + this.lsb = lsb; + } + + public EaglercraftUUID(String uuid) { + String[] components = uuid.split("-"); + if (components.length != 5) + throw new IllegalArgumentException("Invalid UUID string: " + uuid); + for (int i = 0; i < 5; i++) + components[i] = "0x" + components[i]; + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + this.msb = mostSigBits; + this.lsb = leastSigBits; + } + + private static byte long7(long x) { + return (byte) (x >> 56); + } + + private static byte long6(long x) { + return (byte) (x >> 48); + } + + private static byte long5(long x) { + return (byte) (x >> 40); + } + + private static byte long4(long x) { + return (byte) (x >> 32); + } + + private static byte long3(long x) { + return (byte) (x >> 24); + } + + private static byte long2(long x) { + return (byte) (x >> 16); + } + + private static byte long1(long x) { + return (byte) (x >> 8); + } + + private static byte long0(long x) { + return (byte) (x); + } + + public byte[] getBytes() { + byte[] ret = new byte[16]; + ret[0] = long7(msb); + ret[1] = long6(msb); + ret[2] = long5(msb); + ret[3] = long4(msb); + ret[4] = long3(msb); + ret[5] = long2(msb); + ret[6] = long1(msb); + ret[7] = long0(msb); + ret[8] = long7(lsb); + ret[9] = long6(lsb); + ret[10] = long5(lsb); + ret[11] = long4(lsb); + ret[12] = long3(lsb); + ret[13] = long2(lsb); + ret[14] = long1(lsb); + ret[15] = long0(lsb); + return ret; + } + + @Override + public String toString() { + return (digits(msb >> 32, 8) + "-" + digits(msb >> 16, 4) + "-" + digits(msb, 4) + "-" + digits(lsb >> 48, 4) + + "-" + digits(lsb, 12)); + } + + private static String digits(long val, int digits) { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + @Override + public int hashCode() { + if(hash == 0 && !hasHash) { + long hilo = msb ^ lsb; + hash = ((int) (hilo >> 32)) ^ (int) hilo; + hasHash = true; + } + return hash; + } + + @Override + public boolean equals(Object o) { + if(!(o instanceof EaglercraftUUID)) return false; + EaglercraftUUID oo = (EaglercraftUUID)o; + return (hasHash && oo.hasHash) + ? (hash == oo.hash && msb == oo.msb && lsb == oo.lsb) + : (msb == oo.msb && lsb == oo.lsb); + } + + public long getMostSignificantBits() { + return msb; + } + + public long getLeastSignificantBits() { + return lsb; + } + + private static final String HEX = "0123456789ABCDEF"; + + private static int nibbleValue(char c) { + int v = HEX.indexOf(Character.toUpperCase(c)); + if (v == -1) { + return 0; + } else { + return v; + } + } + + private static long parse4Nibbles(String name, int pos) { + int ch1 = nibbleValue(name.charAt(pos)); + int ch2 = nibbleValue(name.charAt(pos + 1)); + int ch3 = nibbleValue(name.charAt(pos + 2)); + int ch4 = nibbleValue(name.charAt(pos + 3)); + return (ch1 << 12) | (ch2 << 8) | (ch3 << 4) | ch4; + } + + public static EaglercraftUUID fromString(String name) { + if (name.length() == 36) { + char ch1 = name.charAt(8); + char ch2 = name.charAt(13); + char ch3 = name.charAt(18); + char ch4 = name.charAt(23); + if (ch1 == '-' && ch2 == '-' && ch3 == '-' && ch4 == '-') { + long msb1 = parse4Nibbles(name, 0); + long msb2 = parse4Nibbles(name, 4); + long msb3 = parse4Nibbles(name, 9); + long msb4 = parse4Nibbles(name, 14); + long lsb1 = parse4Nibbles(name, 19); + long lsb2 = parse4Nibbles(name, 24); + long lsb3 = parse4Nibbles(name, 28); + long lsb4 = parse4Nibbles(name, 32); + if ((msb1 | msb2 | msb3 | msb4 | lsb1 | lsb2 | lsb3 | lsb4) >= 0) { + return new EaglercraftUUID(msb1 << 48 | msb2 << 32 | msb3 << 16 | msb4, + lsb1 << 48 | lsb2 << 32 | lsb3 << 16 | lsb4); + } + } + } + return fromString1(name); + } + + private static EaglercraftUUID fromString1(String name) { + int len = name.length(); + if (len > 36) { + throw new IllegalArgumentException("UUID string too large"); + } + + int dash1 = name.indexOf('-', 0); + int dash2 = name.indexOf('-', dash1 + 1); + int dash3 = name.indexOf('-', dash2 + 1); + int dash4 = name.indexOf('-', dash3 + 1); + int dash5 = name.indexOf('-', dash4 + 1); + + if (dash4 < 0 || dash5 >= 0) { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + + long mostSigBits = JDKBackports.parseLong(name, 0, dash1, 16) & 0xffffffffL; + mostSigBits <<= 16; + mostSigBits |= JDKBackports.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL; + mostSigBits <<= 16; + mostSigBits |= JDKBackports.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL; + long leastSigBits = JDKBackports.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL; + leastSigBits <<= 48; + leastSigBits |= JDKBackports.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL; + + return new EaglercraftUUID(mostSigBits, leastSigBits); + } + + public static EaglercraftUUID nameUUIDFromBytes(byte[] bytes) { + MD5Digest dg = new MD5Digest(); + dg.update(bytes, 0, bytes.length); + byte[] md5Bytes = new byte[16]; + dg.doFinal(md5Bytes, 0); + md5Bytes[6] &= 0x0f; + md5Bytes[6] |= 0x30; + md5Bytes[8] &= 0x3f; + md5Bytes[8] |= 0x80; + return new EaglercraftUUID(md5Bytes); + } + + public static EaglercraftUUID randomUUID() { + byte[] randomBytes = new byte[16]; + (new EaglercraftRandom()).nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new EaglercraftUUID(randomBytes); + } + + @Override + public int compareTo(EaglercraftUUID val) { + return (this.msb < val.msb ? -1 + : (this.msb > val.msb ? 1 : (this.lsb < val.lsb ? -1 : (this.lsb > val.lsb ? 1 : 0)))); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index 5a5880a..7c281d7 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java @@ -1,67 +1,67 @@ -package net.lax1dude.eaglercraft.v1_8; - -import java.math.BigInteger; -import java.util.List; - -public class EaglercraftVersion { - - ////////////////////////////////////////////////////////////////////// - - /// Customize these to fit your fork: - - public static final String projectForkName = "Eaglercraft Lambda"; - public static final String projectForkVersion = "0.6.1"; - public static final String projectForkVendor = "HoosierTransfer"; - - public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; - - ////////////////////////////////////////////////////////////////////// - - public static final String projectOriginName = "EaglercraftX"; - public static final String projectOriginAuthor = "lax1dude"; - public static final String projectOriginRevision = "1.8"; - public static final String projectOriginVersion = "u36"; - - public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace - - // Updating configuration - - public static final boolean enableUpdateService = true; - - public static final List updateURLs = List.of( - "https://update.temuzx.xyz/cert", - "https://update.eagler.xyz/cert", - "https://update.hoosiertransfer.xyz/cert"); - - public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client"; - public static final int updateBundlePackageVersionInt = 3; - - public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; - - // public key modulus for official 1.8 updates - public static final BigInteger updateSignatureModulus = new BigInteger( - "9934844152704206425984038360710846195785255499658630347555679233517037320419089417353684680137701223265944443284321705629368787894053054988233896240570752560271396448764359123258518818693879688207544671033079915029195517675413202427147319375331350380376604266826560830299822638274516845927247015696509586216934495843289602444650044805651410710164106192952455213102521880119736500301420208590760465989706511018182601545217390196438291842825959549203290633490664834390313090964927686415922400638755956780717898579080985306487440294133874610155281675147758926351882699414541707391045631732309999601681661304360813629331"); - - // Miscellaneous variables: - - public static final String mainMenuStringA = "Minecraft 1.9.4"; - public static final String mainMenuStringB = projectOriginName + " " + - projectOriginRevision + "-" + projectOriginVersion + " ultimate"; - public static final String mainMenuStringC = ""; - public static final String mainMenuStringD = "Resources Copyright Mojang AB"; - - public static final String mainMenuStringE = projectForkName + " " + projectForkVersion; - public static final String mainMenuStringF = "Made by " + projectForkVendor; - - public static final String mainMenuStringG = "Collector's Edition"; - public static final String mainMenuStringH = "PBR Shaders"; - - public static final long demoWorldSeed = (long) "North Carolina".hashCode(); - - public static final boolean mainMenuEnableGithubButton = false; - - public static final boolean forceDemoMode = false; - - public static final String localStorageNamespace = "_eaglercraftX"; - -} +package net.lax1dude.eaglercraft.v1_8; + +import java.math.BigInteger; +import java.util.List; + +public class EaglercraftVersion { + + ////////////////////////////////////////////////////////////////////// + + /// Customize these to fit your fork: + + public static final String projectForkName = "Eaglercraft Lambda"; + public static final String projectForkVersion = "0.6.1"; + public static final String projectForkVendor = "HoosierTransfer"; + + public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; + + ////////////////////////////////////////////////////////////////////// + + public static final String projectOriginName = "EaglercraftX"; + public static final String projectOriginAuthor = "lax1dude"; + public static final String projectOriginRevision = "1.8"; + public static final String projectOriginVersion = "u37"; + + public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace + + // Updating configuration + + public static final boolean enableUpdateService = true; + + public static final List updateURLs = List.of( + "https://update.temuzx.xyz/cert", + "https://update.eagler.xyz/cert", + "https://update.hoosiertransfer.xyz/cert"); + + public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client"; + public static final int updateBundlePackageVersionInt = 3; + + public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; + + // public key modulus for official 1.8 updates + public static final BigInteger updateSignatureModulus = new BigInteger( + "9934844152704206425984038360710846195785255499658630347555679233517037320419089417353684680137701223265944443284321705629368787894053054988233896240570752560271396448764359123258518818693879688207544671033079915029195517675413202427147319375331350380376604266826560830299822638274516845927247015696509586216934495843289602444650044805651410710164106192952455213102521880119736500301420208590760465989706511018182601545217390196438291842825959549203290633490664834390313090964927686415922400638755956780717898579080985306487440294133874610155281675147758926351882699414541707391045631732309999601681661304360813629331"); + + // Miscellaneous variables: + + public static final String mainMenuStringA = "Minecraft 1.9.4"; + public static final String mainMenuStringB = projectOriginName + " " + + projectOriginRevision + "-" + projectOriginVersion + " ultimate"; + public static final String mainMenuStringC = ""; + public static final String mainMenuStringD = "Resources Copyright Mojang AB"; + + public static final String mainMenuStringE = projectForkName + " " + projectForkVersion; + public static final String mainMenuStringF = "Made by " + projectForkVendor; + + public static final String mainMenuStringG = "Collector's Edition"; + public static final String mainMenuStringH = "PBR Shaders"; + + public static final long demoWorldSeed = (long) "North Carolina".hashCode(); + + public static final boolean mainMenuEnableGithubButton = false; + + public static final boolean forceDemoMode = false; + + public static final String localStorageNamespace = "_eaglercraftX"; + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Filesystem.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Filesystem.java new file mode 100755 index 0000000..6918699 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Filesystem.java @@ -0,0 +1,158 @@ +package net.lax1dude.eaglercraft.v1_8; + +import java.util.HashMap; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.RamdiskFilesystemImpl; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class Filesystem { + + private static final Logger logger = LogManager.getLogger("PlatformFilesystem"); + + private static final Map openFilesystems = new HashMap<>(); + + public static IEaglerFilesystem getHandleFor(String dbName) { + FilesystemHandle handle = openFilesystems.get(dbName); + if(handle != null) { + ++handle.refCount; + return new FilesystemHandleWrapper(handle); + } + IEaglerFilesystem handleImpl = null; + if(!EagRuntime.getConfiguration().isRamdiskMode()) { + handleImpl = PlatformFilesystem.initializePersist(dbName); + } + if(handleImpl == null) { + handleImpl = new RamdiskFilesystemImpl(dbName); + } + if(handleImpl.isRamdisk()) { + logger.warn("Using RAMDisk filesystem for database \"{}\", data will not be saved to local storage!", dbName); + } + handle = new FilesystemHandle(handleImpl); + openFilesystems.put(dbName, handle); + return new FilesystemHandleWrapper(handle); + } + + public static void closeAllHandles() { + for(FilesystemHandle handle : openFilesystems.values()) { + handle.refCount = 0; + handle.handle.closeHandle(); + } + openFilesystems.clear(); + } + + private static class FilesystemHandle { + + private final IEaglerFilesystem handle; + private int refCount; + + private FilesystemHandle(IEaglerFilesystem handle) { + this.handle = handle; + this.refCount = 1; + } + + } + + private static class FilesystemHandleWrapper implements IEaglerFilesystem { + + private final FilesystemHandle handle; + private final IEaglerFilesystem handleImpl; + private boolean closed; + + private FilesystemHandleWrapper(FilesystemHandle handle) { + this.handle = handle; + this.handleImpl = handle.handle; + this.closed = false; + } + + @Override + public String getFilesystemName() { + return handleImpl.getFilesystemName(); + } + + @Override + public String getInternalDBName() { + return handleImpl.getInternalDBName(); + } + + @Override + public boolean isRamdisk() { + return handleImpl.isRamdisk(); + } + + @Override + public boolean eaglerDelete(String pathName) { + return handleImpl.eaglerDelete(pathName); + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + return handleImpl.eaglerRead(pathName); + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + handleImpl.eaglerWrite(pathName, data); + } + + @Override + public boolean eaglerExists(String pathName) { + return handleImpl.eaglerExists(pathName); + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + return handleImpl.eaglerMove(pathNameOld, pathNameNew); + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + return handleImpl.eaglerCopy(pathNameOld, pathNameNew); + } + + @Override + public int eaglerSize(String pathName) { + return handleImpl.eaglerSize(pathName); + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + handleImpl.eaglerIterate(pathName, itr, recursive); + } + + @Override + public void closeHandle() { + if(!closed && handle.refCount > 0) { + closed = true; + --handle.refCount; + if(handle.refCount <= 0) { + logger.info("Releasing filesystem handle for: \"{}\"", handleImpl.getFilesystemName()); + handleImpl.closeHandle(); + openFilesystems.remove(handleImpl.getFilesystemName()); + } + } + } + + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Gamepad.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Gamepad.java new file mode 100755 index 0000000..68cc6f2 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Gamepad.java @@ -0,0 +1,115 @@ +package net.lax1dude.eaglercraft.v1_8; + +import java.util.LinkedList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.internal.GamepadConstants; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class Gamepad { + + private static final boolean[] buttonsLastState = new boolean[24]; + private static final List buttonEvents = new LinkedList<>(); + private static VirtualButtonEvent currentVEvent = null; + + private static class VirtualButtonEvent { + + private final int button; + private final boolean state; + + public VirtualButtonEvent(int button, boolean state) { + this.button = button; + this.state = state; + } + + } + + public static int getValidDeviceCount() { + return PlatformInput.gamepadGetValidDeviceCount(); + } + + public static String getDeviceName(int deviceId) { + return PlatformInput.gamepadGetDeviceName(deviceId); + } + + public static void setSelectedDevice(int deviceId) { + PlatformInput.gamepadSetSelectedDevice(deviceId); + } + + public static void update() { + PlatformInput.gamepadUpdate(); + if(isValid()) { + for(int i = 0; i < buttonsLastState.length; ++i) { + boolean b = PlatformInput.gamepadGetButtonState(i); + if(b != buttonsLastState[i]) { + buttonsLastState[i] = b; + buttonEvents.add(new VirtualButtonEvent(i, b)); + if(buttonEvents.size() > 64) { + buttonEvents.remove(0); + } + } + } + }else { + for(int i = 0; i < buttonsLastState.length; ++i) { + buttonsLastState[i] = false; + } + } + } + + public static boolean next() { + currentVEvent = null; + return !buttonEvents.isEmpty() && (currentVEvent = buttonEvents.remove(0)) != null; + } + + public static int getEventButton() { + return currentVEvent != null ? currentVEvent.button : -1; + } + + public static boolean getEventButtonState() { + return currentVEvent != null ? currentVEvent.state : false; + } + + public static boolean isValid() { + return PlatformInput.gamepadIsValid(); + } + + public static String getName() { + return PlatformInput.gamepadGetName(); + } + + public static boolean getButtonState(int button) { + return PlatformInput.gamepadGetButtonState(button); + } + + public static String getButtonName(int button) { + return GamepadConstants.getButtonName(button); + } + + public static float getAxis(int axis) { + return PlatformInput.gamepadGetAxis(axis); + } + + public static String getAxisName(int button) { + return GamepadConstants.getAxisName(button); + } + + public static void clearEventBuffer() { + buttonEvents.clear(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/HashKey.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/HashKey.java new file mode 100755 index 0000000..fdb56fb --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/HashKey.java @@ -0,0 +1,54 @@ +package net.lax1dude.eaglercraft.v1_8; + +import java.util.Arrays; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class HashKey { + + public final byte[] key; + public final int hash; + + public HashKey(byte[] key, int hashCode) { + this.key = key; + this.hash = hashCode; + } + + public HashKey(byte[] key) { + this.key = key; + this.hash = Arrays.hashCode(key); + } + + public Object clone() { + return new HashKey(key, hash); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || !(obj instanceof HashKey)) + return false; + HashKey other = (HashKey) obj; + return hash == other.hash && Arrays.equals(key, other.key); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java index e42561f..8365573 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java @@ -32,7 +32,7 @@ public class IOUtils { return Arrays.asList( new String(((EaglerInputStream) parInputStream).getAsArray(), charset).split("(\\r\\n|\\n|\\r)")); }else { - List ret = new ArrayList(); + List ret = new ArrayList<>(); try(InputStream is = parInputStream) { BufferedReader rd = new BufferedReader(new InputStreamReader(is, charset)); String s; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Keyboard.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Keyboard.java index b9123ce..dabb472 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Keyboard.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Keyboard.java @@ -1,5 +1,6 @@ package net.lax1dude.eaglercraft.v1_8; +import net.lax1dude.eaglercraft.v1_8.internal.EnumFireKeyboardEvent; import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; @@ -68,4 +69,8 @@ public class Keyboard { return PlatformInput.keyboardIsRepeatEvent(); } + public static void fireEvent(EnumFireKeyboardEvent eventType, int eagKey, char keyChar) { + PlatformInput.keyboardFireEvent(eventType, eagKey, keyChar); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Mouse.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Mouse.java index 222cac3..9947bb7 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Mouse.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Mouse.java @@ -1,130 +1,147 @@ -package net.lax1dude.eaglercraft.v1_8; - -import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; - -/** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -// HoosierTransfer mod here -public class Mouse { - - public static int getEventDWheel() { - return PlatformInput.mouseGetEventDWheel(); - } - - public static int getX() { - return (int) (PlatformInput.mouseGetX()); - } - - public static int getY() { - return (int) (PlatformInput.mouseGetY()); - } - - public static boolean getEventButtonState() { - return PlatformInput.mouseGetEventButtonState(); - } - - public static boolean isCreated() { - return true; - } - - public static boolean next() { - return PlatformInput.mouseNext(); - } - - public static int getEventX() { - return (int) (PlatformInput.mouseGetEventX()); - } - - public static int getEventY() { - return (int) (PlatformInput.mouseGetEventY()); - } - - public static int getEventButton() { - return PlatformInput.mouseGetEventButton(); - } - - public static boolean isButtonDown(int i) { - return PlatformInput.mouseIsButtonDown(i); - } - - public static int getDWheel() { - return PlatformInput.mouseGetDWheel(); - } - - public static void setGrabbed(boolean grab) { - PlatformInput.mouseSetGrabbed(grab); - } - - public static int getDX() { - return PlatformInput.mouseGetDX(); - } - - public static int getDY() { - return PlatformInput.mouseGetDY(); - } - - public static void setCursorPosition(int x, int y) { - PlatformInput.mouseSetCursorPosition(x, y); - } - - public static boolean isInsideWindow() { - return PlatformInput.mouseIsInsideWindow(); - } - - public static boolean isActuallyGrabbed() { - return PlatformInput.isPointerLocked(); - } - - public static boolean isMouseGrabbed() { - return PlatformInput.isMouseGrabbed(); - } - - private static int customCursorCounter = 0; - private static EnumCursorType currentCursorType = EnumCursorType.DEFAULT; - - public static void showCursor(EnumCursorType cursor) { - if (EagRuntime.getConfiguration().useSpecialCursors()) { - customCursorCounter = 2; - if (currentCursorType != cursor) { - PlatformInput.showCursor(cursor); - currentCursorType = cursor; - } - } - } - - public static void tickCursorShape() { - if (EagRuntime.getConfiguration().useSpecialCursors()) { - if (customCursorCounter > 0) { - if (--customCursorCounter == 0) { - if (currentCursorType != EnumCursorType.DEFAULT) { - PlatformInput.showCursor(EnumCursorType.DEFAULT); - currentCursorType = EnumCursorType.DEFAULT; - } - } - } - } - } -} +package net.lax1dude.eaglercraft.v1_8; + +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.internal.EnumFireMouseEvent; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +/** + * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +// HoosierTransfer mod here +public class Mouse { + + public static int getEventDWheel() { + return PlatformInput.mouseGetEventDWheel(); + } + + public static int getX() { + return (int) (PlatformInput.mouseGetX()); + } + + public static int getY() { + return (int) (PlatformInput.mouseGetY()); + } + + public static boolean getEventButtonState() { + return PlatformInput.mouseGetEventButtonState(); + } + + public static boolean isCreated() { + return true; + } + + public static boolean next() { + return PlatformInput.mouseNext(); + } + + public static int getEventX() { + return (int) (PlatformInput.mouseGetEventX()); + } + + public static int getEventY() { + return (int) (PlatformInput.mouseGetEventY()); + } + + public static int getEventButton() { + return PlatformInput.mouseGetEventButton(); + } + + public static boolean isButtonDown(int i) { + return PlatformInput.mouseIsButtonDown(i); + } + + public static int getDWheel() { + return PlatformInput.mouseGetDWheel(); + } + + public static void setGrabbed(boolean grab) { + PlatformInput.mouseSetGrabbed(grab); + } + + public static int getDX() { + return PlatformInput.mouseGetDX(); + } + + public static int getDY() { + return PlatformInput.mouseGetDY(); + } + + public static void setCursorPosition(int x, int y) { + PlatformInput.mouseSetCursorPosition(x, y); + } + + public static boolean isInsideWindow() { + return PlatformInput.mouseIsInsideWindow(); + } + + public static boolean isActuallyGrabbed() { + return PlatformInput.isPointerLocked(); + } + + public static boolean isMouseGrabbed() { + return PlatformInput.isMouseGrabbed(); + } + + public static boolean isMouseGrabSupported() { + return PlatformInput.mouseGrabSupported(); + } + + public static void fireMoveEvent(EnumFireMouseEvent eventType, int posX, int posY) { + PlatformInput.mouseFireMoveEvent(eventType, posX, posY); + } + + public static void fireButtonEvent(EnumFireMouseEvent eventType, int posX, int posY, int button) { + PlatformInput.mouseFireButtonEvent(eventType, posX, posY, button); + } + + public static void fireWheelEvent(EnumFireMouseEvent eventType, int posX, int posY, float wheel) { + PlatformInput.mouseFireWheelEvent(eventType, posX, posY, wheel); + } + + private static int customCursorCounter = 0; + private static EnumCursorType currentCursorType = EnumCursorType.DEFAULT; + + public static void showCursor(EnumCursorType cursor) { + if (EagRuntime.getConfiguration().useSpecialCursors()) { + customCursorCounter = 2; + if (currentCursorType != cursor) { + PlatformInput.showCursor(cursor); + currentCursorType = cursor; + } + } + } + + public static void tickCursorShape() { + if (EagRuntime.getConfiguration().useSpecialCursors()) { + if (customCursorCounter > 0) { + if (--customCursorCounter == 0) { + if (currentCursorType != EnumCursorType.DEFAULT) { + PlatformInput.showCursor(EnumCursorType.DEFAULT); + currentCursorType = EnumCursorType.DEFAULT; + } + } + } + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java new file mode 100755 index 0000000..e0c67d3 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java @@ -0,0 +1,257 @@ +package net.lax1dude.eaglercraft.v1_8; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketCustomizePauseMenuV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PauseMenuCustomizeState { + + private static final Logger logger = LogManager.getLogger("PauseMenuCustomizeState"); + + public static ResourceLocation icon_title_L; + public static float icon_title_L_aspect = 1.0f; + public static ResourceLocation icon_title_R; + public static float icon_title_R_aspect = 1.0f; + public static ResourceLocation icon_backToGame_L; + public static float icon_backToGame_L_aspect = 1.0f; + public static ResourceLocation icon_backToGame_R; + public static float icon_backToGame_R_aspect = 1.0f; + public static ResourceLocation icon_achievements_L; + public static float icon_achievements_L_aspect = 1.0f; + public static ResourceLocation icon_achievements_R; + public static float icon_achievements_R_aspect = 1.0f; + public static ResourceLocation icon_statistics_L; + public static float icon_statistics_L_aspect = 1.0f; + public static ResourceLocation icon_statistics_R; + public static float icon_statistics_R_aspect = 1.0f; + public static ResourceLocation icon_serverInfo_L; + public static float icon_serverInfo_L_aspect = 1.0f; + public static ResourceLocation icon_serverInfo_R; + public static float icon_serverInfo_R_aspect = 1.0f; + public static ResourceLocation icon_options_L; + public static float icon_options_L_aspect = 1.0f; + public static ResourceLocation icon_options_R; + public static float icon_options_R_aspect = 1.0f; + public static ResourceLocation icon_discord_L; + public static float icon_discord_L_aspect = 1.0f; + public static ResourceLocation icon_discord_R; + public static float icon_discord_R_aspect = 1.0f; + public static ResourceLocation icon_disconnect_L; + public static float icon_disconnect_L_aspect = 1.0f; + public static ResourceLocation icon_disconnect_R; + public static float icon_disconnect_R_aspect = 1.0f; + public static ResourceLocation icon_background_pause; + public static float icon_background_pause_aspect = 1.0f; + public static ResourceLocation icon_background_all; + public static float icon_background_all_aspect = 1.0f; + public static ResourceLocation icon_watermark_pause; + public static float icon_watermark_pause_aspect = 1.0f; + public static ResourceLocation icon_watermark_all; + public static float icon_watermark_all_aspect = 1.0f; + + public static final int SERVER_INFO_MODE_NONE = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE; + public static final int SERVER_INFO_MODE_EXTERNAL_URL = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_EXTERNAL_URL; + public static final int SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP; + public static final int SERVER_INFO_MODE_SHOW_EMBED_OVER_WS = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS; + + public static final int SERVER_INFO_EMBED_PERMS_JAVASCRIPT = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_JAVASCRIPT; + public static final int SERVER_INFO_EMBED_PERMS_MESSAGE_API = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_MESSAGE_API; + public static final int SERVER_INFO_EMBED_PERMS_STRICT_CSP = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_STRICT_CSP; + + public static final int DISCORD_MODE_NONE = SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_NONE; + public static final int DISCORD_MODE_INVITE_URL = SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_INVITE_URL; + + public static int serverInfoMode; + public static int serverInfoEmbedPerms = SERVER_INFO_EMBED_PERMS_STRICT_CSP; + public static String serverInfoEmbedTitle; + public static String serverInfoButtonText; + public static String serverInfoURL; + public static byte[] serverInfoHash; + + public static int discordButtonMode; + public static String discordButtonText; + public static String discordInviteURL; + + private static final List toFree = new ArrayList<>(); + + private static int textureId = 0; + + private static class PauseMenuSprite { + + private final ResourceLocation loc; + private final EaglerSkinTexture tex; + + public PauseMenuSprite(EaglerSkinTexture tex) { + this.loc = newLoc(); + this.tex = tex; + } + + } + + public static void loadPacket(SPacketCustomizePauseMenuV4EAG packet) { + reset(); + + serverInfoMode = packet.serverInfoMode; + switch(packet.serverInfoMode) { + case SERVER_INFO_MODE_NONE: + default: + serverInfoButtonText = null; + serverInfoURL = null; + serverInfoHash = null; + break; + case SERVER_INFO_MODE_EXTERNAL_URL: + serverInfoButtonText = packet.serverInfoButtonText; + serverInfoURL = packet.serverInfoURL; + break; + case SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP: + serverInfoButtonText = packet.serverInfoButtonText; + serverInfoEmbedPerms = packet.serverInfoEmbedPerms; + serverInfoURL = packet.serverInfoURL; + serverInfoEmbedTitle = packet.serverInfoEmbedTitle; + break; + case SERVER_INFO_MODE_SHOW_EMBED_OVER_WS: + serverInfoButtonText = packet.serverInfoButtonText; + serverInfoEmbedPerms = packet.serverInfoEmbedPerms; + serverInfoHash = packet.serverInfoHash; + serverInfoEmbedTitle = packet.serverInfoEmbedTitle; + break; + } + + discordButtonMode = packet.discordButtonMode; + switch(packet.discordButtonMode) { + case DISCORD_MODE_NONE: + default: + discordButtonText = null; + serverInfoURL = null; + serverInfoHash = null; + break; + case DISCORD_MODE_INVITE_URL: + discordButtonText = packet.discordButtonText; + discordInviteURL = packet.discordInviteURL; + break; + } + + if(packet.imageMappings != null) { + Map spriteCache = new HashMap<>(); + icon_title_L = cacheLoadHelperFunction("icon_title_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_title_L_aspect = a); + icon_title_R = cacheLoadHelperFunction("icon_title_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_title_R_aspect = a); + icon_backToGame_L = cacheLoadHelperFunction("icon_backToGame_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_backToGame_L_aspect = a); + icon_backToGame_R = cacheLoadHelperFunction("icon_backToGame_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_backToGame_R_aspect = a); + icon_achievements_L = cacheLoadHelperFunction("icon_achievements_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_achievements_L_aspect = a); + icon_achievements_R = cacheLoadHelperFunction("icon_achievements_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_achievements_R_aspect = a); + icon_statistics_L = cacheLoadHelperFunction("icon_statistics_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_statistics_L_aspect = a); + icon_statistics_R = cacheLoadHelperFunction("icon_statistics_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_statistics_R_aspect = a); + icon_serverInfo_L = cacheLoadHelperFunction("icon_serverInfo_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_serverInfo_L_aspect = a); + icon_serverInfo_R = cacheLoadHelperFunction("icon_serverInfo_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_serverInfo_R_aspect = a); + icon_options_L = cacheLoadHelperFunction("icon_options_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_options_L_aspect = a); + icon_options_R = cacheLoadHelperFunction("icon_options_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_options_R_aspect = a); + icon_discord_L = cacheLoadHelperFunction("icon_discord_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_discord_L_aspect = a); + icon_discord_R = cacheLoadHelperFunction("icon_discord_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_discord_R_aspect = a); + icon_disconnect_L = cacheLoadHelperFunction("icon_disconnect_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_disconnect_L_aspect = a); + icon_disconnect_R = cacheLoadHelperFunction("icon_disconnect_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_disconnect_R_aspect = a); + icon_background_pause = cacheLoadHelperFunction("icon_background_pause", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_background_pause_aspect = a); + icon_background_all = cacheLoadHelperFunction("icon_background_all", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_background_all_aspect = a); + icon_watermark_pause = cacheLoadHelperFunction("icon_watermark_pause", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_watermark_pause_aspect = a); + icon_watermark_all = cacheLoadHelperFunction("icon_watermark_all", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_watermark_all_aspect = a); + } + } + + private static ResourceLocation cacheLoadHelperFunction(String name, Map lookup, + Map spriteCache, + List sourceData, Consumer aspectCB) { + Integer i = lookup.get(name); + if(i == null) { + return null; + } + PauseMenuSprite ret = spriteCache.get(i); + if(ret != null) { + if(name.startsWith("icon_background_") && ImageData.isNPOTStatic(ret.tex.getWidth(), ret.tex.getHeight())) { + logger.warn("An NPOT (non-power-of-two) texture was used for \"{}\", this texture's width and height must be powers of two for this texture to display properly on all hardware"); + } + aspectCB.accept((float)ret.tex.getWidth() / ret.tex.getHeight()); + return ret.loc; + } + int ii = i.intValue(); + if(ii < 0 || ii >= sourceData.size()) { + return null; + } + PacketImageData data = sourceData.get(ii); + ret = new PauseMenuSprite(new EaglerSkinTexture(ImageData.swapRB(data.rgba), data.width, data.height)); + Minecraft.getMinecraft().getTextureManager().loadTexture(ret.loc, ret.tex); + spriteCache.put(i, ret); + toFree.add(ret); + aspectCB.accept((float)data.width / data.height); + return ret.loc; + } + + private static ResourceLocation newLoc() { + return new ResourceLocation("eagler:gui/server/custom_pause_menu/tex_" + textureId++); + } + + public static void reset() { + icon_title_L = icon_title_R = null; + icon_backToGame_L = icon_backToGame_R = null; + icon_achievements_L = icon_achievements_R = null; + icon_statistics_L = icon_statistics_R = null; + icon_serverInfo_L = icon_serverInfo_R = null; + icon_options_L = icon_options_R = null; + icon_discord_L = icon_discord_R = null; + icon_disconnect_L = icon_disconnect_R = null; + icon_background_pause = icon_background_all = null; + icon_watermark_pause = icon_watermark_all = null; + icon_title_L_aspect = icon_title_R_aspect = 1.0f; + icon_backToGame_L_aspect = icon_backToGame_R_aspect = 1.0f; + icon_achievements_L_aspect = icon_achievements_R_aspect = 1.0f; + icon_statistics_L_aspect = icon_statistics_R_aspect = 1.0f; + icon_serverInfo_L_aspect = icon_serverInfo_R_aspect = 1.0f; + icon_options_L_aspect = icon_options_R_aspect = 1.0f; + icon_discord_L_aspect = icon_discord_R_aspect = 1.0f; + icon_disconnect_L_aspect = icon_disconnect_R_aspect = 1.0f; + icon_background_pause_aspect = icon_background_all_aspect = 1.0f; + icon_watermark_pause_aspect = icon_watermark_all_aspect = 1.0f; + serverInfoMode = 0; + serverInfoEmbedPerms = SERVER_INFO_EMBED_PERMS_STRICT_CSP; + serverInfoButtonText = null; + serverInfoURL = null; + serverInfoHash = null; + serverInfoEmbedTitle = null; + discordButtonMode = 0; + discordButtonText = null; + discordInviteURL = null; + if(!toFree.isEmpty()) { + TextureManager mgr = Minecraft.getMinecraft().getTextureManager(); + for(PauseMenuSprite rc : toFree) { + mgr.deleteTexture(rc.loc); + } + toFree.clear(); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/PointerInputAbstraction.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/PointerInputAbstraction.java new file mode 100755 index 0000000..5f82346 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/PointerInputAbstraction.java @@ -0,0 +1,227 @@ +package net.lax1dude.eaglercraft.v1_8; + +import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchControls; +import net.minecraft.client.Minecraft; + +/** + * Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PointerInputAbstraction { + + protected static Minecraft mc; + protected static int oldMX = -1; + protected static int oldMY = -1; + protected static int oldTX = -1; + protected static int oldTY = -1; + protected static int dragStartX = -1; + protected static int dragStartY = -1; + protected static int dragStepStartX = -1; + protected static int dragStepStartY = -1; + protected static int dragUID = -1; + + protected static int cursorX = -1; + protected static int cursorY = -1; + protected static int cursorDX = 0; + protected static int cursorDY = 0; + protected static boolean touchingScreen = false; + protected static boolean touchingScreenNotButton = false; + protected static boolean draggingNotTouching = false; + protected static boolean touchMode = false; + + public static void init(Minecraft mcIn) { + mc = mcIn; + oldMX = -1; + oldMY = -1; + oldTX = -1; + oldTY = -1; + dragStartX = -1; + dragStartY = -1; + dragStepStartX = -1; + dragStepStartY = -1; + dragUID = -1; + cursorX = -1; + cursorY = -1; + cursorDX = 0; + cursorDY = 0; + touchingScreen = false; + touchingScreenNotButton = false; + draggingNotTouching = false; + touchMode = !mcIn.mouseGrabSupported; + } + + public static void runGameLoop() { + if(touchMode) { + runTouchUpdate(); + }else { + oldTX = -1; + oldTY = -1; + cursorX = oldMX = Mouse.getX(); + cursorY = oldMY = Mouse.getY(); + cursorDX += Mouse.getDX(); + cursorDY += Mouse.getDY(); + } + } + + private static void runTouchUpdate() { + int tc = Touch.touchPointCount(); + if (tc > 0) { + TouchControls.update(true); + touchingScreen = true; + for(int i = 0; i < tc; ++i) { + int uid = Touch.touchPointUID(i); + if(TouchControls.touchControls.containsKey(uid)) { + continue; + } + int tx = Touch.touchPointX(i); + int ty = Touch.touchPointY(i); + if(TouchControls.overlappingControl(tx, ty) != null) { + continue; + } + if(mc.currentScreen == null && mc.ingameGUI.isTouchOverlapEagler(uid, tx, ty)) { + continue; + } + cursorX = oldTX = tx; + cursorY = oldTY = ty; + oldMX = Mouse.getX(); + oldMY = Mouse.getY(); + touchingScreenNotButton = true; + runTouchDeltaUpdate(uid); + return; + } + touchingScreenNotButton = false; + } else { + TouchControls.update(false); + touchingScreen = false; + touchingScreenNotButton = false; + dragStepStartX = -1; + dragStepStartY = -1; + dragStartX = -1; + dragStartY = -1; + dragUID = -1; + final int tmp = Mouse.getX(); + final int tmp2 = Mouse.getY(); + if(oldTX == -1 || oldTY == -1) { + cursorX = oldMX = tmp; + cursorY = oldMY = tmp2; + cursorDX += Mouse.getDX(); + cursorDY += Mouse.getDY(); + return; + } + if (oldMX == -1 || oldMY == -1) { + oldMX = tmp; + oldMY = tmp2; + } + if (oldMX == tmp && oldMY == tmp2) { + cursorX = oldTX; + cursorY = oldTY; + }else { + cursorX = oldMX = tmp; + cursorY = oldMY = tmp2; + cursorDX += Mouse.getDX(); + cursorDY += Mouse.getDY(); + } + } + } + + private static void runTouchDeltaUpdate(int uid) { + if(uid != dragUID) { + dragStartX = oldTX; + dragStartY = oldTY; + dragStepStartX = -1; + dragStepStartY = -1; + dragUID = uid; + draggingNotTouching = false; + return; + } + if(dragStepStartX != -1) { + cursorDX += oldTX - dragStepStartX; + } + dragStepStartX = oldTX; + if(dragStepStartY != -1) { + cursorDY += oldTY - dragStepStartY; + } + dragStepStartY = oldTY; + if(dragStartX != -1 && dragStartY != -1) { + int dx = oldTX - dragStartX; + int dy = oldTY - dragStartY; + int len = dx * dx + dy * dy; + int dm = Math.max((int)(6 * Display.getDPI()), 2); + if(len > dm * dm) { + draggingNotTouching = true; + } + } + } + + public static boolean isTouchMode() { + return touchMode; + } + + public static boolean isTouchingScreen() { + return touchingScreen; + } + + public static boolean isTouchingScreenNotButton() { + return touchingScreenNotButton; + } + + public static boolean isDraggingNotTouching() { + return draggingNotTouching; + } + + public static void enterTouchModeHook() { + if(!touchMode) { + touchMode = true; + if(mc.mouseGrabSupported) { + mc.mouseHelper.ungrabMouseCursor(); + } + } + } + + public static void enterMouseModeHook() { + if(touchMode) { + touchMode = false; + touchingScreen = false; + touchingScreenNotButton = false; + if(mc.inGameHasFocus && mc.mouseGrabSupported) { + mc.mouseHelper.grabMouseCursor(); + } + } + } + + public static int getVCursorX() { + return cursorX; + } + + public static int getVCursorY() { + return cursorY; + } + + public static int getVCursorDX() { + int tmp = cursorDX; + cursorDX = 0; + return tmp; + } + + public static int getVCursorDY() { + int tmp = cursorDY; + cursorDY = 0; + return tmp; + } + + public static boolean getVCursorButtonDown(int bt) { + return (touchingScreenNotButton && bt == 0) || Mouse.isButtonDown(bt); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Touch.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Touch.java new file mode 100755 index 0000000..f82bd53 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Touch.java @@ -0,0 +1,134 @@ +package net.lax1dude.eaglercraft.v1_8; + +import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class Touch { + + public static boolean next() { + return PlatformInput.touchNext(); + } + + public static EnumTouchEvent getEventType() { + return PlatformInput.touchGetEventType(); + } + + public static int getEventTouchPointCount() { + return PlatformInput.touchGetEventTouchPointCount(); + } + + public static int getEventTouchX(int pointId) { + return PlatformInput.touchGetEventTouchX(pointId); + } + + public static int getEventTouchY(int pointId) { + return PlatformInput.touchGetEventTouchY(pointId); + } + + public static float getEventTouchRadiusX(int pointId) { + return PlatformInput.touchGetEventTouchRadiusX(pointId); + } + + public static float getEventTouchRadiusY(int pointId) { + return PlatformInput.touchGetEventTouchRadiusY(pointId); + } + + public static float getEventTouchRadiusMixed(int pointId) { + return PlatformInput.touchGetEventTouchRadiusMixed(pointId); + } + + public static float getEventTouchForce(int pointId) { + return PlatformInput.touchGetEventTouchForce(pointId); + } + + public static int getEventTouchPointUID(int pointId) { + return PlatformInput.touchGetEventTouchPointUID(pointId); + } + + public static int touchPointCount() { + return PlatformInput.touchPointCount(); + } + + public static int touchPointX(int pointId) { + return PlatformInput.touchPointX(pointId); + } + + public static int touchPointY(int pointId) { + return PlatformInput.touchPointY(pointId); + } + + public static float touchPointRadiusX(int pointId) { + return PlatformInput.touchRadiusX(pointId); + } + + public static float touchPointRadiusY(int pointId) { + return PlatformInput.touchRadiusY(pointId); + } + + public static float touchPointRadiusMixed(int pointId) { + return PlatformInput.touchRadiusMixed(pointId); + } + + public static float touchPointForce(int pointId) { + return PlatformInput.touchForce(pointId); + } + + public static int touchPointUID(int pointId) { + return PlatformInput.touchPointUID(pointId); + } + + public static void touchSetOpenKeyboardZone(int x, int y, int w, int h) { + PlatformInput.touchSetOpenKeyboardZone(x, y, w, h); + } + + public static void closeDeviceKeyboard() { + PlatformInput.touchCloseDeviceKeyboard(); + } + + public static boolean isDeviceKeyboardOpenMAYBE() { + return PlatformInput.touchIsDeviceKeyboardOpenMAYBE(); + } + + public static String getPastedString() { + return PlatformInput.touchGetPastedString(); + } + + public static boolean checkPointTouching(int uid) { + int cnt = PlatformInput.touchPointCount(); + if(cnt > 0) { + for(int i = 0; i < cnt; ++i) { + if(PlatformInput.touchPointUID(i) == uid) { + return true; + } + } + } + return false; + } + + public static int fetchPointIdx(int uid) { + int cnt = PlatformInput.touchPointCount(); + if(cnt > 0) { + for(int i = 0; i < cnt; ++i) { + if(PlatformInput.touchPointUID(i) == uid) { + return i; + } + } + } + return -1; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/boot_menu/GuiScreenEnterBootMenu.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/boot_menu/GuiScreenEnterBootMenu.java new file mode 100755 index 0000000..eefb309 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/boot_menu/GuiScreenEnterBootMenu.java @@ -0,0 +1,54 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenEnterBootMenu extends GuiScreen { + + private final GuiScreen parent; + + public GuiScreenEnterBootMenu(GuiScreen parent) { + this.parent = parent; + } + + public void initGui() { + EagRuntime.setDisplayBootMenuNextRefresh(true); + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("gui.cancel"))); + } + + public void onGuiClosed() { + EagRuntime.setDisplayBootMenuNextRefresh(false); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("enterBootMenu.title"), this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, I18n.format("enterBootMenu.text0"), this.width / 2, 90, 16777215); + super.drawScreen(par1, par2, par3); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + this.mc.displayGuiScreen(parent); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cache/EaglerLoadingCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cache/EaglerLoadingCache.java index 73af3c1..d95c9e9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/cache/EaglerLoadingCache.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cache/EaglerLoadingCache.java @@ -33,7 +33,7 @@ public class EaglerLoadingCache { public EaglerLoadingCache(EaglerCacheProvider provider) { this.provider = provider; - this.cacheMap = new HashMap(); + this.cacheMap = new HashMap<>(); } public V get(K key) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenInspectSessionToken.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenInspectSessionToken.java new file mode 100755 index 0000000..7606777 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenInspectSessionToken.java @@ -0,0 +1,84 @@ +package net.lax1dude.eaglercraft.v1_8.cookie; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore.ServerCookie; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenInspectSessionToken extends GuiScreen { + + private final GuiScreen parent; + private final ServerCookie cookie; + + public GuiScreenInspectSessionToken(GuiScreenRevokeSessionToken parent, ServerCookie cookie) { + this.parent = parent; + this.cookie = cookie; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 106, I18n.format("gui.done"))); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + String[][] toDraw = new String[][] { + { + I18n.format("inspectSessionToken.details.server"), + I18n.format("inspectSessionToken.details.expires"), + I18n.format("inspectSessionToken.details.length") + }, + { + cookie.server.length() > 32 ? cookie.server.substring(0, 30) + "..." : cookie.server, + (new SimpleDateFormat("M/d/yyyy h:mm aa")).format(new Date(cookie.expires)), + Integer.toString(cookie.cookie.length) + } + }; + int[] maxWidth = new int[2]; + for(int i = 0; i < 2; ++i) { + String[] strs = toDraw[i]; + int w = 0; + for(int j = 0; j < strs.length; ++j) { + int k = fontRendererObj.getStringWidth(strs[j]); + if(k > w) { + w = k; + } + } + maxWidth[i] = w + 10; + } + int totalWidth = maxWidth[0] + maxWidth[1]; + this.drawCenteredString(fontRendererObj, I18n.format("inspectSessionToken.title"), this.width / 2, 70, 16777215); + this.drawString(fontRendererObj, toDraw[0][0], (this.width - totalWidth) / 2, 90, 11184810); + this.drawString(fontRendererObj, toDraw[0][1], (this.width - totalWidth) / 2, 104, 11184810); + this.drawString(fontRendererObj, toDraw[0][2], (this.width - totalWidth) / 2, 118, 11184810); + this.drawString(fontRendererObj, toDraw[1][0], (this.width - totalWidth) / 2 + maxWidth[0], 90, 11184810); + this.drawString(fontRendererObj, toDraw[1][1], (this.width - totalWidth) / 2 + maxWidth[0], 104, 11184810); + this.drawString(fontRendererObj, toDraw[1][2], (this.width - totalWidth) / 2 + maxWidth[0], 118, 11184810); + super.drawScreen(par1, par2, par3); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + this.mc.displayGuiScreen(parent); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenRevokeSessionToken.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenRevokeSessionToken.java new file mode 100755 index 0000000..ae4c099 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenRevokeSessionToken.java @@ -0,0 +1,146 @@ +package net.lax1dude.eaglercraft.v1_8.cookie; + +import java.io.IOException; +import java.util.Collections; + +import com.google.common.collect.Lists; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSlot; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenRevokeSessionToken extends GuiScreen { + protected GuiScreen parentScreen; + private GuiScreenRevokeSessionToken.List list; + private GuiButton inspectButton; + private GuiButton revokeButton; + + public GuiScreenRevokeSessionToken(GuiScreen parent) { + this.parentScreen = parent; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(this.inspectButton = new GuiButton(10, this.width / 2 - 154, this.height - 38, 100, 20, I18n.format("revokeSessionToken.inspect"))); + this.buttonList.add(this.revokeButton = new GuiButton(9, this.width / 2 - 50, this.height - 38, 100, 20, I18n.format("revokeSessionToken.revoke"))); + this.buttonList.add(new GuiButton(6, this.width / 2 + 54, this.height - 38, 100, 20, I18n.format("gui.done"))); + this.list = new GuiScreenRevokeSessionToken.List(this.mc); + this.list.registerScrollButtons(7, 8); + updateButtons(); + } + + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + this.list.handleMouseInput(); + } + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.list.handleTouchInput(); + } + + protected void actionPerformed(GuiButton parGuiButton) { + if (parGuiButton.enabled) { + switch (parGuiButton.id) { + case 6: + this.mc.displayGuiScreen(this.parentScreen); + break; + case 9: + String s1 = list.getSelectedItem(); + if(s1 != null) { + ServerCookieDataStore.ServerCookie cookie = ServerCookieDataStore.loadCookie(s1); + if(cookie != null) { + this.mc.displayGuiScreen(new GuiScreenSendRevokeRequest(this, cookie)); + }else { + this.initGui(); + } + } + break; + case 10: + String s2 = list.getSelectedItem(); + if(s2 != null) { + ServerCookieDataStore.ServerCookie cookie = ServerCookieDataStore.loadCookie(s2); + if(cookie != null) { + this.mc.displayGuiScreen(new GuiScreenInspectSessionToken(this, cookie)); + }else { + this.initGui(); + } + } + break; + default: + this.list.actionPerformed(parGuiButton); + } + + } + } + + protected void updateButtons() { + inspectButton.enabled = revokeButton.enabled = list.getSelectedItem() != null; + } + + public void drawScreen(int i, int j, float f) { + this.list.drawScreen(i, j, f); + this.drawCenteredString(this.fontRendererObj, I18n.format("revokeSessionToken.title"), this.width / 2, 16, 16777215); + this.drawCenteredString(this.fontRendererObj, I18n.format("revokeSessionToken.note.0"), this.width / 2, this.height - 66, 8421504); + this.drawCenteredString(this.fontRendererObj, I18n.format("revokeSessionToken.note.1"), this.width / 2, this.height - 56, 8421504); + super.drawScreen(i, j, f); + } + + class List extends GuiSlot { + private final java.util.List cookieNames = Lists.newArrayList(); + + public List(Minecraft mcIn) { + super(mcIn, GuiScreenRevokeSessionToken.this.width, GuiScreenRevokeSessionToken.this.height, 32, GuiScreenRevokeSessionToken.this.height - 75 + 4, 18); + ServerCookieDataStore.flush(); + cookieNames.addAll(ServerCookieDataStore.getRevokableServers()); + Collections.sort(cookieNames); + } + + protected int getSize() { + return this.cookieNames.size(); + } + + protected void elementClicked(int i, boolean var2, int var3, int var4) { + selectedElement = i; + GuiScreenRevokeSessionToken.this.updateButtons(); + } + + protected boolean isSelected(int i) { + return selectedElement == i; + } + + protected String getSelectedItem() { + return selectedElement == -1 ? null : cookieNames.get(selectedElement); + } + + protected int getContentHeight() { + return this.getSize() * 18; + } + + protected void drawBackground() { + GuiScreenRevokeSessionToken.this.drawDefaultBackground(); + } + + protected void drawSlot(int i, int var2, int j, int var4, int var5, int var6) { + GuiScreenRevokeSessionToken.this.drawCenteredString(GuiScreenRevokeSessionToken.this.fontRendererObj, + this.cookieNames.get(i), this.width / 2, j + 1, 16777215); + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenSendRevokeRequest.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenSendRevokeRequest.java new file mode 100755 index 0000000..3c6dfdb --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/GuiScreenSendRevokeRequest.java @@ -0,0 +1,177 @@ +package net.lax1dude.eaglercraft.v1_8.cookie; + +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore.ServerCookie; +import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery; +import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.lax1dude.eaglercraft.v1_8.socket.ServerQueryDispatch; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenSendRevokeRequest extends GuiScreen { + + private static final Logger logger = LogManager.getLogger("SessionRevokeRequest"); + + private GuiScreen parent; + private ServerCookie cookie; + private String title; + private String message; + private int timer = 0; + private boolean cancelRequested = false; + private IServerQuery query = null; + private boolean hasSentPacket = false; + + public GuiScreenSendRevokeRequest(GuiScreen parent, ServerCookie cookie) { + this.parent = parent; + this.cookie = cookie; + this.title = I18n.format("revokeSendingScreen.title"); + this.message = I18n.format("revokeSendingScreen.message.opening", cookie.server); + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("gui.cancel"))); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, title, this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, message, this.width / 2, 90, 16777215); + super.drawScreen(par1, par2, par3); + } + + public void updateScreen() { + ++timer; + if (timer > 1) { + if(query == null) { + logger.info("Attempting to revoke session tokens for: {}", cookie.server); + query = ServerQueryDispatch.sendServerQuery(cookie.server, "revoke_session_token"); + if(query == null) { + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.connectionError", parent)); + return; + } + }else { + query.update(); + QueryResponse resp = query.getResponse(); + if(resp != null) { + if(resp.responseType.equalsIgnoreCase("revoke_session_token") && (hasSentPacket ? resp.isResponseJSON() : resp.isResponseString())) { + if(!hasSentPacket) { + String str = resp.getResponseString(); + if("ready".equalsIgnoreCase(str)) { + hasSentPacket = true; + message = I18n.format("revokeSendingScreen.message.sending"); + query.send(cookie.cookie); + return; + }else { + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent)); + return; + } + }else { + JSONObject json = resp.getResponseJSON(); + String stat = json.optString("status"); + if("ok".equalsIgnoreCase(stat)) { + if(hasSentPacket) { + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeSuccess.title", "revokeSuccess.desc", parent)); + ServerCookieDataStore.clearCookie(cookie.server); + return; + }else { + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent)); + return; + } + }else if("error".equalsIgnoreCase(stat)) { + int code = json.optInt("code", -1); + if(code == -1) { + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent)); + return; + }else { + String key; + switch(code) { + case 1: + key = "revokeFailure.desc.notSupported"; + break; + case 2: + key = "revokeFailure.desc.notAllowed"; + break; + case 3: + key = "revokeFailure.desc.notFound"; + break; + case 4: + key = "revokeFailure.desc.serverError"; + break; + default: + key = "revokeFailure.desc.genericCode"; + break; + } + logger.error("Recieved error code {}! ({})", code, key); + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", key, parent)); + if(json.optBoolean("delete", false)) { + ServerCookieDataStore.clearCookie(cookie.server); + } + return; + } + }else { + logger.error("Recieved unknown status \"{}\"!", stat); + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent)); + return; + } + } + }else { + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent)); + return; + } + } + if(query.isClosed()) { + if(!hasSentPacket || query.responsesAvailable() == 0) { + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.connectionError", parent)); + return; + } + }else { + if(timer > 400) { + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.connectionError", parent)); + return; + } + } + if(cancelRequested) { + query.close(); + this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.cancelled", parent)); + return; + } + } + } + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + cancelRequested = true; + par1GuiButton.enabled = false; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java new file mode 100755 index 0000000..fffbac9 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java @@ -0,0 +1,294 @@ +package net.lax1dude.eaglercraft.v1_8.cookie; + +import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.DrawUtils; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GLSLHeader; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.lax1dude.eaglercraft.v1_8.opengl.VSHInputLayoutParser; +import net.minecraft.client.renderer.texture.TextureUtil; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ExtGLEnums.*; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; + +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +import net.lax1dude.eaglercraft.v1_8.crypto.GeneralDigest; +import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class HardwareFingerprint { + + // This is used for generating encryption keys for storing cookies, + // its supposed to make session hijacking more difficult for skids + + private static final String fingerprintIMG = "/9j/4AAQSkZJRgABAQEBLAEsAAD//gAKZnVjayBvZmb/4gKwSUNDX1BST0ZJTEUAAQEAAAKgbGNtcwRAAABtbnRyUkdCIFhZWiAH6AAGABAAAgArACNhY3NwTVNGVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEL/2wBDAAoHBwkHBgoJCAkLCwoMDxkQDw4ODx4WFxIZJCAmJSMgIyIoLTkwKCo2KyIjMkQyNjs9QEBAJjBGS0U+Sjk/QD3/2wBDAQsLCw8NDx0QEB09KSMpPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT3/wgARCACAAIADAREAAhEBAxEB/8QAGwAAAgMBAQEAAAAAAAAAAAAAAQIABAUDBgf/xAAYAQEBAQEBAAAAAAAAAAAAAAAAAQIDBP/aAAwDAQACEAMQAAABx95IwaIRghDRCFCElZ+KQhpghGDRCFCQNEzsUjUQjBDRChCENQJn4pDXfNuSvBBUJYK52Q5ahIZ+KSxLtc9jAcbO8OpA0YENodZpdJWszsUm1z3alHnuBZurO+SA5+PVPTQ7zv0zd1MLpnGzSem49OQIPMeslCpA82ueVj05fcuWdtPCBPW8elGzlRIMFCElOPZoJe0+dx2PR8+mdYoSAFQgFHq3ZsWWa+fxZ01+O6lKgqiWhxRhBQpv6mlXgdS1Loct8kAtcjiWBRxQANHWd+vEw5M67HQghXGO4AEFLms+hrxFhrW49KliDhGAABAALes79eI1CbnHpVshzAQhCEIAmpa3nJCbPHpwsgBRBQBGICrlzd1PN1DW5bSBSgAcxElOpQLo3OtX/8QAJxAAAQQBBAEEAgMAAAAAAAAAAQACAxEQBBITICEUIjAxMkEjNFD/2gAIAQEAAQUC/wBNkZevThcDVwtXC1cAXAuAoxOCMbh2iZyPqsX8HG16lhMXTSMqHAZP6jpI/Y2DVcxCCYA9s8JgfiBtaUq+zm7hHAyLATFqo+TTIeSRUbirVq1uW5bluW4K0FCE/wAilCLmkcirV4tWrVq0HprlD9Y039iQ9JXmMtO5qLqV+MbqWmfuZ+gC4we3UPPnL27mxGkTQaOhWjdhp2Na7cre0h99HsTQT20n5JxtzPzkHksX0rV/BpT7gQcyYpEZvtHbjE8x5PlmT8ELP4ItOGuw3zD3vo32r9f/xAAeEQACAQUAAwAAAAAAAAAAAAAAEQEQIDAxUAIhQP/aAAgBAwEBPwHpoQhCEIQr4qsvjSD2TbEk3Rqr5kb+CN59D7s2RrN//8QAIhEAAQMEAgIDAAAAAAAAAAAAAQACEhARIDEDMBNAIUFQ/9oACAECAQE/Af0ybKSkVIqRU1NSUgrjIm1HuiF5SmulgSAg4UBQN8H050XMiuHA3c5P44fK4zcVBvU7o5odteFqAtg5hBuEZu2mNtUejdfXeAna9B2u/ZRHeEfe33jA77BT/8QAJxAAAQIFAwMFAQAAAAAAAAAAAQARAhAhMDEgQVESImEDUGJxgZH/2gAIAQEABj8C9zosme6yVlUMsamVLVQnzDzoMXNh0zVn0xYKY42M4R+2GK7RoPIrMC6RKD7uvOHTRPZYVUL6umTnW+5TbrurpcZVbHhQ/d6hvngByuuEvyJwnxcPzib8CJNZw3PRg4gf+y//xAAkEAEAAgIBBAIDAQEAAAAAAAABABEQITFBUWFxIIGR8PFQsf/aAAgBAQABPyH/AE39Nd4GbulPd+54X8z9jhK/zHpMWqj8wa1/Xyrenl9QAoUGKcXgwMuFQdx/6oldyOPg7hqsdI6zYXz0qBrFwl/2wF1CXvFKdw5bLfcMh3g29ywwt8SBWWRJaV6wnKc4WhppgIBy6nZwoJ054y0vnrKnqeSB7xXNhc9kKwrXjOMOkdOJK/AKwo5htQdfcXWNvbK1juVGBXYldhV7sLWYuBPTEDZLisKuxC0CfyWOOGAgysrkwFkGMqaGo2zzBjudcb4i71HwzXfZBgRiblS/toGM8GGMeJo64uE47XQR03hBILpNyObZvHXHSAXdENsE8YJu33jKM7M4l4Dg64eIABpTeIibDl65XlB8BcSoy5cOMM3bz+49wcAJq8v6VfJNx1OEvC6uauwL3tBj/9oADAMBAAIAAwAAABD9mRbSTt1t0bIAF+n8iJQX9mluqdyUVE09DAPykEK/iOdbRe8nvNsaLtpZ+CB9RFxmQADt+ChzpmW03P7QNkikzbp/AkyUoZrmRKm2JYrYU5LbJaLJU0pbLQ1Z+klOwjL/xAAeEQEAAgIDAQEBAAAAAAAAAAABABEQICEwMUFAUP/aAAgBAwEBPxD+mJlZWUlZXPaI7C3AXglaVKwhErQcXHALa/CWQVGMSsioYcS2oiQo8i3HDip81qVq5qHpHuWsT1unGHsceIW4ZyS+twtw9jK63R7GeZ+fsDj/xAAfEQEAAgIDAAMBAAAAAAAAAAABABEQISAwMUFQYXH/2gAIAQIBAT8Q+zGL8IrT+IHANyqXP2DYwu8hizh6kW6cKQOBbrC2QNYWr+Mvk1UTQxTWEGpQy7UGAVGyAKOPcKEUahh04uevG+gwW1DlXIxbNHDrIYsJs4dhipUlkoZXWYFE8MJeK6TgdhtqX4cul7PdxbyaXZ4x/8QAJRABAAICAgEEAwADAAAAAAAAAQARITFBUWEQcZGhgbHhwdHw/9oACAEBAAE/EBhCDCEGEGXBhCD6DCEIMIQYQYMGDBhB9CEGDCEGEIMIMNQYegwYTVAtL0TKZ4UEAFjdsNePmS1wj2jHgfiL6Z72lLL9xIJSLqhLcAc5fqG4MuDBiZquXoQIDCA4lXuZ8F9S/iZGJdSw8QggXzHMFjupfG/2OA2aA56fMIMJcZp3wf1+oMd63WI/LNqeSMC9yq1vqaTBxMWXS4ykS/CRRTcE4ekf3GNn/BHMGDmcKrS7yiomtzOywRtvMuubjKtmDTGl52MIthPKAjqXYTiPbtv22fEGZmkB7sxbQUuaKqADe03UvVR52BbYQGDFJOU325g2S35jtRa8wEObK4akp9T3RcfVj+G4QKyH0TwImLaIPZEBLwSagO5YbMQnnXmOKy4WW2ntLn+4sOXwn6ZgTlgVmYxYTuWbLuCByahNMWaIhBT1Oj5i5xfvASx7Z+p0AGTmFsIASbaLfMOXbcJXKHuVcWYsxgBXiFgqY8kuWM0suh49AhbwQiUp8wirdaTATjBHIf1CRqVeuf8AD5Jhihwm2wzQsuHUULmLm10cxwUDiWBGZUdzpxD2g2zyRYDxCZNGOo4gMaE+4pKuLVFMBpzoBddwA5izZ+osHLwRzzFqEIllOf8AcpK2Mrr0VI9MVANBlvzEGSUt6QOUF36AT00Udeg/S3jQx9qH5isgUZobxwlkCVPJi+o4bdR+pcCEhdwgdRYnprC1givIW1+R8So0Sq6vcCVL/wAi+DUo5ihYkuLWiOSkjcMSyxC0AlodEEfALlhHUubF/STqcT//2Q=="; + + private static final String shaderPrecision = "precision lowp int;\nprecision mediump float;\nprecision mediump sampler2D;\n"; + + private static byte[] fingerprint = null; + + public static final Logger logger = LogManager.getLogger("HardwareFingerprint"); + + public static byte[] getFingerprint() { + if(fingerprint == null) { + try { + fingerprint = generateFingerprint(); + }catch(Throwable t) { + fingerprint = new byte[0]; + } + if(fingerprint.length == 0) { + logger.error("Failed to calculate hardware fingerprint, server cookies will not be encrypted!"); + } + } + return fingerprint; + } + + private static byte[] generateFingerprint() { + ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(fingerprintIMG), "image/jpeg"); + if(img == null) { + logger.error("Input image data is corrupt!"); + return new byte[0]; + } + + int[][] mipmapLevels = TextureUtil.generateMipmapData(7, 128, + new int[][] { img.pixels, null, null, null, null, null, null, null }); + + int helperTexture = GlStateManager.generateTexture(); + GlStateManager.bindTexture(helperTexture); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + TextureUtil.allocateTextureImpl(helperTexture, 7, 128, 128); + TextureUtil.uploadTextureMipmap(mipmapLevels, 128, 128, 0, 0, false, false); + if(checkAnisotropicFilteringSupport()) { + _wglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 16.0f); + } + + IShaderGL vert; + List vertLayout; + if(DrawUtils.vshLocal != null) { + vert = DrawUtils.vshLocal; + vertLayout = DrawUtils.vshLocalLayout; + }else { + String vshLocalSrc = EagRuntime.getRequiredResourceString("/assets/eagler/glsl/local.vsh"); + vertLayout = VSHInputLayoutParser.getShaderInputs(vshLocalSrc); + vert = _wglCreateShader(GL_VERTEX_SHADER); + _wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vshLocalSrc, DrawUtils.vertexShaderPrecision)); + _wglCompileShader(vert); + if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { + _wglDeleteShader(vert); + GlStateManager.deleteTexture(helperTexture); + return new byte[0]; + } + } + + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(EagRuntime.getRequiredResourceString("/assets/eagler/glsl/hw_fingerprint.fsh"), shaderPrecision)); + _wglCompileShader(frag); + if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + _wglDeleteShader(vert); + _wglDeleteShader(frag); + GlStateManager.deleteTexture(helperTexture); + return new byte[0]; + } + + IProgramGL program = _wglCreateProgram(); + + _wglAttachShader(program, vert); + _wglAttachShader(program, frag); + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + VSHInputLayoutParser.applyLayout(program, vertLayout); + } + + _wglLinkProgram(program); + _wglDetachShader(program, vert); + _wglDetachShader(program, frag); + if(DrawUtils.vshLocal == null) { + _wglDeleteShader(vert); + } + _wglDeleteShader(frag); + + if(_wglGetProgrami(program, GL_LINK_STATUS) != GL_TRUE) { + _wglDeleteProgram(program); + GlStateManager.deleteTexture(helperTexture); + return new byte[0]; + } + + EaglercraftGPU.bindGLShaderProgram(program); + _wglUniform1i(_wglGetUniformLocation(program, "u_inputTexture"), 0); + + float fovy = 90.0f; + float aspect = 1.0f; + float zNear = 0.01f; + float zFar = 100.0f; + FloatBuffer matrixUploadBuffer = EagRuntime.allocateFloatBuffer(16); + float toRad = 0.0174532925f; + float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f); + + matrixUploadBuffer.put(cotangent / aspect); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(cotangent); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put((zFar + zNear) / (zFar - zNear)); + matrixUploadBuffer.put(2.0f * zFar * zNear / (zFar - zNear)); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(0.0f); + matrixUploadBuffer.put(-1.0f); + matrixUploadBuffer.put(0.0f); + + matrixUploadBuffer.flip(); + _wglUniformMatrix4fv(_wglGetUniformLocation(program, "u_textureMatrix"), false, matrixUploadBuffer); + EagRuntime.freeFloatBuffer(matrixUploadBuffer); + + int[] oldViewport = new int[4]; + EaglercraftGPU.glGetInteger(GL_VIEWPORT, oldViewport); + IFramebufferGL framebuffer = _wglCreateFramebuffer(); + int renderTexture = GlStateManager.generateTexture(); + GlStateManager.bindTexture(renderTexture); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + int dataLength; + int type; + if(EaglercraftGPU.checkHDRFramebufferSupport(32)) { + dataLength = 256 * 256 * 4 * 4; + type = GL_FLOAT; + EaglercraftGPU.createFramebufferHDR32FTexture(GL_TEXTURE_2D, 0, 256, 256, GL_RGBA, false); + }else if(EaglercraftGPU.checkHDRFramebufferSupport(16)) { + dataLength = 256 * 256 * 4 * 2; + type = _GL_HALF_FLOAT; + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 256, 256, GL_RGBA, false); + }else { + dataLength = 256 * 256 * 4; + type = GL_UNSIGNED_BYTE; + EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + } + + _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(renderTexture), 0); + _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); + + GlStateManager.viewport(0, 0, 256, 256); + GlStateManager.disableBlend(); + GlStateManager.bindTexture(helperTexture); + + DrawUtils.drawStandardQuad2D(); + + _wglDeleteProgram(program); + GlStateManager.deleteTexture(helperTexture); + + ByteBuffer readBuffer = EagRuntime.allocateByteBuffer(dataLength); + EaglercraftGPU.glReadPixels(0, 0, 256, 256, GL_RGBA, type, readBuffer); + + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + _wglDeleteFramebuffer(framebuffer); + GlStateManager.deleteTexture(renderTexture); + GlStateManager.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); + + SHA256Digest digest = new SHA256Digest(); + byte[] copyBuffer = new byte[1024]; + + byte[] b = ("eag" + EaglercraftGPU.glGetString(GL_VENDOR) + "; eag " + EaglercraftGPU.glGetString(GL_RENDERER)).getBytes(StandardCharsets.UTF_8); + digest.update(b, 0, b.length); + + digestInts(digest, _wglGetInteger(0x8869), _wglGetInteger(0x8DFB), _wglGetInteger(0x8B4C), _wglGetInteger(0x8DFC), copyBuffer); + digestInts(digest, _wglGetInteger(0x8DFD), _wglGetInteger(0x8872), _wglGetInteger(0x84E8), 69, copyBuffer); + digestInts(digest, _wglGetInteger(0x0D33), _wglGetInteger(0x851C), _wglGetInteger(0x8B4D), 69, copyBuffer); + + if(EaglercraftGPU.checkOpenGLESVersion() >= 300) { + digestInts(digest, _wglGetInteger(0x8B4A), _wglGetInteger(0x8A2B), _wglGetInteger(0x9122), _wglGetInteger(0x8B4B), copyBuffer); + digestInts(digest, _wglGetInteger(0x8C8A), _wglGetInteger(0x8C8B), _wglGetInteger(0x8C80), _wglGetInteger(0x8B49), copyBuffer); + digestInts(digest, _wglGetInteger(0x8A2D), _wglGetInteger(0x9125), _wglGetInteger(0x8904), _wglGetInteger(0x8905), copyBuffer); + digestInts(digest, _wglGetInteger(0x8824), _wglGetInteger(0x8073), _wglGetInteger(0x88FF), _wglGetInteger(0x84FD), copyBuffer); + digestInts(digest, _wglGetInteger(0x8CDF), _wglGetInteger(0x8A2F), _wglGetInteger(0x8A30), _wglGetInteger(0x8A34), copyBuffer); + digestInts(digest, _wglGetInteger(0x8A2E), _wglGetInteger(0x8A31), _wglGetInteger(0x8A33), _wglGetInteger(0x8D57), copyBuffer); + } + + try { + List exts = Lists.newArrayList(getAllExtensions()); + Collections.sort(exts); + EaglercraftRandom rand = new EaglercraftRandom(6942069420l + exts.size() * 69l + b.length); + for (int i = exts.size() - 1; i > 0; --i) { + int j = rand.nextInt(i + 1); + Collections.swap(exts, i, j); + } + b = String.join(":>", exts).getBytes(StandardCharsets.UTF_8); + digest.update(b, 0, b.length); + }catch(Throwable t) { + } + + int i; + while(readBuffer.hasRemaining()) { + i = Math.min(readBuffer.remaining(), copyBuffer.length); + readBuffer.get(copyBuffer, 0, i); + digest.update(copyBuffer, 0, i); + } + + EagRuntime.freeByteBuffer(readBuffer); + + byte[] hashOut = new byte[32]; + digest.doFinal(hashOut, 0); + + return hashOut; + } + + private static void digestInts(GeneralDigest digest, int i1, int i2, int i3, int i4, byte[] tmpBuffer) { + tmpBuffer[0] = (byte)(i1 >>> 24); + tmpBuffer[1] = (byte)(i1 >>> 16); + tmpBuffer[2] = (byte)(i1 >>> 8); + tmpBuffer[3] = (byte)(i1 & 0xFF); + tmpBuffer[4] = (byte)(i2 >>> 24); + tmpBuffer[5] = (byte)(i2 >>> 16); + tmpBuffer[6] = (byte)(i2 >>> 8); + tmpBuffer[7] = (byte)(i2 & 0xFF); + tmpBuffer[8] = (byte)(i3 >>> 24); + tmpBuffer[9] = (byte)(i3 >>> 16); + tmpBuffer[10] = (byte)(i3 >>> 8); + tmpBuffer[11] = (byte)(i3 & 0xFF); + tmpBuffer[12] = (byte)(i4 >>> 24); + tmpBuffer[13] = (byte)(i4 >>> 16); + tmpBuffer[14] = (byte)(i4 >>> 8); + tmpBuffer[15] = (byte)(i4 & 0xFF); + digest.update(tmpBuffer, 0, 16); + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/ServerCookieDataStore.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/ServerCookieDataStore.java new file mode 100755 index 0000000..eff0d63 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/ServerCookieDataStore.java @@ -0,0 +1,396 @@ +package net.lax1dude.eaglercraft.v1_8.cookie; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; +import net.lax1dude.eaglercraft.v1_8.crypto.AESLightEngine; +import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerCookieDataStore { + + private static final Logger logger = LogManager.getLogger("ServerCookieDataStore"); + + private static final Map dataStore = new HashMap<>(); + + public static final String localStorageKey = "c"; + + public static class ServerCookie { + + public final String server; + public final byte[] cookie; + public final long expires; + public final boolean revokeQuerySupported; + public final boolean saveCookieToDisk; + + public ServerCookie(String server, byte[] cookie, long expires, boolean revokeQuerySupported, boolean saveCookieToDisk) { + this.server = server; + this.cookie = cookie; + this.expires = expires; + this.revokeQuerySupported = revokeQuerySupported; + this.saveCookieToDisk = saveCookieToDisk; + } + + } + + public static void load() { + if(EagRuntime.getConfiguration().isEnableServerCookies()) { + loadData(HardwareFingerprint.getFingerprint()); + } + } + + public static ServerCookie loadCookie(String server) { + if(!EagRuntime.getConfiguration().isEnableServerCookies()) { + return null; + } + server = normalize(server); + ServerCookie cookie = dataStore.get(server); + if(cookie == null) { + return null; + } + long timestamp = System.currentTimeMillis(); + if(timestamp > cookie.expires) { + dataStore.remove(server); + saveData(HardwareFingerprint.getFingerprint()); + return null; + } + return cookie; + } + + public static void saveCookie(String server, long expires, byte[] data, boolean revokeQuerySupported, boolean saveCookieToDisk) { + if(!EagRuntime.getConfiguration().isEnableServerCookies()) { + return; + } + server = normalize(server); + if(expires > 604800l) { + clearCookie(server); + logger.error("Server \"{}\" tried to set a cookie for {} days! (The max is 7 days)", server, (expires / 604800l)); + return; + } + if(data.length > 255) { + clearCookie(server); + logger.error("Server \"{}\" tried to set a {} byte cookie! (The max length is 255 bytes)", server, data.length); + return; + } + if(expires < 0l || data.length == 0) { + clearCookie(server); + return; + } + long expiresRelative = System.currentTimeMillis() + expires * 1000l; + dataStore.put(server, new ServerCookie(server, data, expiresRelative, revokeQuerySupported, saveCookieToDisk)); + saveData(HardwareFingerprint.getFingerprint()); + } + + public static void clearCookie(String server) { + if(!EagRuntime.getConfiguration().isEnableServerCookies()) { + return; + } + if(dataStore.remove(normalize(server)) != null) { + saveData(HardwareFingerprint.getFingerprint()); + } + } + + public static void clearCookiesLow() { + dataStore.clear(); + } + + private static String normalize(String server) { + int j = server.indexOf('/'); + if(j != -1) { + int i = server.indexOf("://"); + if(i != -1) { + j = server.indexOf('/', i + 3); + if(j == -1) { + return server.toLowerCase(); + }else { + return server.substring(0, j).toLowerCase() + server.substring(j); + } + }else { + return server.substring(0, j).toLowerCase() + server.substring(j); + } + }else { + return server.toLowerCase(); + } + } + + public static Set getAllServers() { + return dataStore.keySet(); + } + + public static List getRevokableServers() { + List ret = new ArrayList<>(dataStore.size()); + for(ServerCookie c : dataStore.values()) { + if(c.revokeQuerySupported) { + ret.add(c.server); + } + } + return ret; + } + + public static int size() { + return dataStore.size(); + } + + public static int numRevokable() { + int i = 0; + for(ServerCookie c : dataStore.values()) { + if(c.revokeQuerySupported) { + ++i; + } + } + return i; + } + + public static void flush() { + Iterator itr = dataStore.values().iterator(); + boolean changed = false; + while(itr.hasNext()) { + long timestamp = System.currentTimeMillis(); + ServerCookie cookie = itr.next(); + if(timestamp > cookie.expires) { + itr.remove(); + changed = true; + } + } + if(changed) { + saveData(HardwareFingerprint.getFingerprint()); + } + } + + private static void loadData(byte[] key) { + dataStore.clear(); + byte[] cookiesTag = PlatformApplication.getLocalStorage(localStorageKey, false); + if(cookiesTag == null) { + return; + } + if(cookiesTag.length <= 25) { + PlatformApplication.setLocalStorage(localStorageKey, null, false); + return; + } + try { + byte[] decrypted; + int decryptedLen; + switch(cookiesTag[0]) { + case 2: + if(key == null || key.length == 0) { + throw new IOException("Data is encrypted!"); + } + decrypted = new byte[cookiesTag.length - 5]; + decryptedLen = (cookiesTag[1] << 24) | (cookiesTag[2] << 16) | (cookiesTag[3] << 8) | (cookiesTag[4] & 0xFF); + if(decryptedLen < 25) { + throw new IOException("too short!"); + } + AESLightEngine aes = new AESLightEngine(); + aes.init(false, key); + int bs = aes.getBlockSize(); + if(decrypted.length % bs != 0) { + throw new IOException("length not aligned to block size!"); + } + byte[] cbcHelper = new byte[] { (byte) 29, (byte) 163, (byte) 4, (byte) 20, (byte) 207, (byte) 26, + (byte) 140, (byte) 55, (byte) 246, (byte) 250, (byte) 141, (byte) 183, (byte) 153, (byte) 154, + (byte) 59, (byte) 4 }; + for(int i = 0; i < decryptedLen; i += bs) { + processBlockDecryptHelper(aes, cookiesTag, 5 + i, decrypted, i, bs, Math.min(decryptedLen - i, bs), cbcHelper); + } + if(decrypted[0] != (byte)0x69) { + throw new IOException("Data is corrupt!"); + } + break; + case 1: + if(key != null && key.length > 0) { + throw new IOException("Data isn't encrypted!"); + } + decrypted = cookiesTag; + decryptedLen = cookiesTag.length; + break; + default: + throw new IOException("Unknown type!"); + } + SHA1Digest digest = new SHA1Digest(); + digest.update(decrypted, 25, decryptedLen - 25); + byte[] digestOut = new byte[20]; + digest.doFinal(digestOut, 0); + for(int i = 0; i < 20; ++i) { + if(digestOut[i] != decrypted[5 + i]) { + throw new IOException("Invalid checksum!"); + } + } + int decompressedLen = (decrypted[1] << 24) | (decrypted[2] << 16) | (decrypted[3] << 8) | (decrypted[4] & 0xFF); + byte[] decompressed = new byte[decompressedLen]; + try (InputStream zstream = EaglerZLIB.newInflaterInputStream(new EaglerInputStream(decrypted, 25, decryptedLen - 25))) { + int i = 0, j; + while(i < decompressedLen && (j = zstream.read(decompressed, i, decompressedLen - i)) != -1) { + i += j; + } + if(i != decompressedLen) { + throw new IOException("Length does not match!"); + } + } + DataInputStream dis = new DataInputStream(new EaglerInputStream(decompressed)); + int readCount = dis.readInt(); + long time = System.currentTimeMillis(); + for(int i = 0; i < readCount; ++i) { + byte flags = dis.readByte(); + long expires = dis.readLong(); + int len = dis.readUnsignedShort(); + String server = dis.readUTF(); + if(len == 0) { + continue; + } + if(expires < time) { + dis.skipBytes(len); + continue; + } + byte[] cookieData = new byte[len]; + dis.readFully(cookieData); + server = normalize(server); + dataStore.put(server, new ServerCookie(server, cookieData, expires, (flags & 1) != 0, (flags & 2) != 0)); + } + if(dis.available() > 0) { + throw new IOException("Extra bytes remaining!"); + } + }catch (IOException e) { + dataStore.clear(); + PlatformApplication.setLocalStorage(localStorageKey, null, false); + return; + } + } + + private static void saveData(byte[] key) { + Iterator itr = dataStore.values().iterator(); + List toSave = new ArrayList<>(dataStore.size()); + while(itr.hasNext()) { + long timestamp = System.currentTimeMillis(); + ServerCookie cookie = itr.next(); + if(timestamp > cookie.expires || cookie.cookie.length > 255 || cookie.cookie.length == 0) { + itr.remove(); + }else if(cookie.saveCookieToDisk) { + toSave.add(cookie); + } + } + if(toSave.size() == 0) { + PlatformApplication.setLocalStorage(localStorageKey, null, false); + }else { + EaglerOutputStream bao = new EaglerOutputStream(1024); + bao.skipBytes(25); + int totalUncompressedLen; + try(DataOutputStream zstream = new DataOutputStream(EaglerZLIB.newDeflaterOutputStream(bao))) { + zstream.writeInt(dataStore.size()); + for(Entry etr : dataStore.entrySet()) { + ServerCookie cookie = etr.getValue(); + zstream.writeByte((cookie.revokeQuerySupported ? 1 : 0) | (cookie.saveCookieToDisk ? 2 : 0)); + zstream.writeLong(cookie.expires); + zstream.writeShort(cookie.cookie.length); + zstream.writeUTF(etr.getKey()); + zstream.write(cookie.cookie); + } + totalUncompressedLen = zstream.size(); + } catch (IOException e) { + logger.error("Failed to write cookies to local storage!"); + return; + } + byte[] toEncrypt = bao.toByteArray(); + SHA1Digest hash = new SHA1Digest(); + hash.update(toEncrypt, 25, toEncrypt.length - 25); + hash.doFinal(toEncrypt, 5); + toEncrypt[1] = (byte)(totalUncompressedLen >>> 24); + toEncrypt[2] = (byte)(totalUncompressedLen >>> 16); + toEncrypt[3] = (byte)(totalUncompressedLen >>> 8); + toEncrypt[4] = (byte)(totalUncompressedLen & 0xFF); + if(key != null && key.length > 0) { + toEncrypt[0] = (byte)0x69; + AESLightEngine aes = new AESLightEngine(); + aes.init(true, key); + int bs = aes.getBlockSize(); + int blockCount = (toEncrypt.length % bs) != 0 ? (toEncrypt.length / bs + 1) : (toEncrypt.length / bs); + byte[] encrypted = new byte[blockCount * bs + 5]; + encrypted[0] = (byte)2; + encrypted[1] = (byte)(toEncrypt.length >>> 24); + encrypted[2] = (byte)(toEncrypt.length >>> 16); + encrypted[3] = (byte)(toEncrypt.length >>> 8); + encrypted[4] = (byte)(toEncrypt.length & 0xFF); + byte[] cbcHelper = new byte[] { (byte) 29, (byte) 163, (byte) 4, (byte) 20, (byte) 207, (byte) 26, + (byte) 140, (byte) 55, (byte) 246, (byte) 250, (byte) 141, (byte) 183, (byte) 153, (byte) 154, + (byte) 59, (byte) 4 }; + for(int i = 0; i < toEncrypt.length; i += bs) { + processBlockEncryptHelper(aes, toEncrypt, i, encrypted, 5 + i, bs, cbcHelper); + } + PlatformApplication.setLocalStorage(localStorageKey, encrypted, false); + }else { + toEncrypt[0] = (byte)1; + PlatformApplication.setLocalStorage(localStorageKey, toEncrypt, false); + } + } + } + + private static void processBlockEncryptHelper(AESLightEngine aes, byte[] in, int inOff, byte[] out, int outOff, + int len, byte[] cbcHelper) { + int clampedBlockLength = Math.min(in.length - inOff, len); + if(clampedBlockLength == len) { + for(int i = 0; i < len; ++i) { + in[i + inOff] ^= cbcHelper[i]; + } + aes.processBlock(in, inOff, out, outOff); + System.arraycopy(out, outOff, cbcHelper, 0, len); + }else { + byte[] paddedBlock = new byte[len]; + System.arraycopy(in, inOff, paddedBlock, 0, clampedBlockLength); + byte padValue = (byte)(len - clampedBlockLength); + for(byte i = 0; i < padValue; ++i) { + paddedBlock[clampedBlockLength + i] = padValue; + } + for(int i = 0; i < len; ++i) { + paddedBlock[i] ^= cbcHelper[i]; + } + aes.processBlock(paddedBlock, 0, out, outOff); + } + } + + private static void processBlockDecryptHelper(AESLightEngine aes, byte[] in, int inOff, byte[] out, int outOff, + int paddedLen, int unpaddedLen, byte[] cbcHelper) throws IOException { + aes.processBlock(in, inOff, out, outOff); + for(int i = 0; i < paddedLen; ++i) { + out[i + outOff] ^= cbcHelper[i]; + } + if(unpaddedLen == paddedLen) { + System.arraycopy(in, inOff, cbcHelper, 0, paddedLen); + }else { + byte padValue = (byte)(paddedLen - unpaddedLen); + for(byte i = 0; i < padValue; ++i) { + if(out[outOff + unpaddedLen + i] != padValue) { + throw new IOException("Invalid padding!"); + } + } + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/crypto/AESLightEngine.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/crypto/AESLightEngine.java new file mode 100755 index 0000000..d5f5ee7 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/crypto/AESLightEngine.java @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +package net.lax1dude.eaglercraft.v1_8.crypto; + +/** + * an implementation of the AES (Rijndael), from FIPS-197. + *

+ * For further details see: https://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * https://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first + * + * The slowest version uses no static tables at all and computes the values + * in each round. + *

+ * This file contains the slowest performance version with no static tables + * for round precomputation, but it has the smallest foot print. + * + */ +public class AESLightEngine { + // The S box + private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107, (byte) 111, + (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171, (byte) 118, + (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240, (byte) 173, + (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183, (byte) 253, + (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165, (byte) 229, + (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35, (byte) 195, + (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226, (byte) 235, + (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27, (byte) 110, + (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227, (byte) 47, + (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177, (byte) 91, + (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207, (byte) 208, + (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69, (byte) 249, + (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163, (byte) 64, + (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218, (byte) 33, + (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236, (byte) 95, + (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100, (byte) 93, + (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42, (byte) 144, + (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11, (byte) 219, + (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92, (byte) 194, + (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231, (byte) 200, + (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86, (byte) 244, + (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37, (byte) 46, + (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31, (byte) 75, + (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72, (byte) 3, + (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193, (byte) 29, + (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142, (byte) 148, + (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223, (byte) 140, + (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65, (byte) 153, + (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, }; + + // The inverse S-box + private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165, + (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251, + (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52, + (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123, + (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149, + (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102, + (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109, + (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104, + (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182, + (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218, + (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144, + (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228, + (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30, + (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3, + (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79, + (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180, + (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53, + (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110, + (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111, + (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86, + (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192, + (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51, + (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39, + (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181, + (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156, + (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176, + (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23, + (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105, + (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, + 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + private static int shift(int r, int shift) { + return (r >>> shift) | (r << -shift); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private static final int m1 = 0x80808080; + private static final int m2 = 0x7f7f7f7f; + private static final int m3 = 0x0000001b; + private static final int m4 = 0xC0C0C0C0; + private static final int m5 = 0x3f3f3f3f; + + private static int FFmulX(int x) { + return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3)); + } + + private static int FFmulX2(int x) { + int t0 = (x & m5) << 2; + int t1 = (x & m4); + t1 ^= (t1 >>> 1); + return t0 ^ (t1 >>> 2) ^ (t1 >>> 5); + } + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private static int mcol(int x) { + int t0, t1; + t0 = shift(x, 8); + t1 = x ^ t0; + return shift(t1, 16) ^ t0 ^ FFmulX(t1); + } + + private static int inv_mcol(int x) { + int t0, t1; + t0 = x; + t1 = t0 ^ shift(t0, 8); + t0 ^= FFmulX(t1); + t1 ^= FFmulX2(t0); + t0 ^= t1 ^ shift(t1, 16); + return t0; + } + + private static int subWord(int x) { + return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) + | S[(x >> 24) & 255] << 24); + } + + private static int littleEndianToInt(byte[] bs, int off) { + int n = bs[off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff) << 16; + n |= bs[++off] << 24; + return n; + } + + public static void intToLittleEndian(int n, byte[] bs, int off) { + bs[off] = (byte) (n); + bs[++off] = (byte) (n >>> 8); + bs[++off] = (byte) (n >>> 16); + bs[++off] = (byte) (n >>> 24); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[][] generateWorkingKey(byte[] key, boolean forEncryption) { + int keyLen = key.length; + if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0) { + throw new IllegalArgumentException("Key length not 128/192/256 bits."); + } + + int KC = keyLen >>> 2; + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block + // sizes + int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block + + switch (KC) { + case 4: { + int col0 = littleEndianToInt(key, 0); + W[0][0] = col0; + int col1 = littleEndianToInt(key, 4); + W[0][1] = col1; + int col2 = littleEndianToInt(key, 8); + W[0][2] = col2; + int col3 = littleEndianToInt(key, 12); + W[0][3] = col3; + + for (int i = 1; i <= 10; ++i) { + int colx = subWord(shift(col3, 8)) ^ rcon[i - 1]; + col0 ^= colx; + W[i][0] = col0; + col1 ^= col0; + W[i][1] = col1; + col2 ^= col1; + W[i][2] = col2; + col3 ^= col2; + W[i][3] = col3; + } + + break; + } + case 6: { + int col0 = littleEndianToInt(key, 0); + W[0][0] = col0; + int col1 = littleEndianToInt(key, 4); + W[0][1] = col1; + int col2 = littleEndianToInt(key, 8); + W[0][2] = col2; + int col3 = littleEndianToInt(key, 12); + W[0][3] = col3; + + int col4 = littleEndianToInt(key, 16); + int col5 = littleEndianToInt(key, 20); + + int i = 1, rcon = 1, colx; + for (;;) { + W[i][0] = col4; + W[i][1] = col5; + colx = subWord(shift(col5, 8)) ^ rcon; + rcon <<= 1; + col0 ^= colx; + W[i][2] = col0; + col1 ^= col0; + W[i][3] = col1; + + col2 ^= col1; + W[i + 1][0] = col2; + col3 ^= col2; + W[i + 1][1] = col3; + col4 ^= col3; + W[i + 1][2] = col4; + col5 ^= col4; + W[i + 1][3] = col5; + + colx = subWord(shift(col5, 8)) ^ rcon; + rcon <<= 1; + col0 ^= colx; + W[i + 2][0] = col0; + col1 ^= col0; + W[i + 2][1] = col1; + col2 ^= col1; + W[i + 2][2] = col2; + col3 ^= col2; + W[i + 2][3] = col3; + + if ((i += 3) >= 13) { + break; + } + + col4 ^= col3; + col5 ^= col4; + } + + break; + } + case 8: { + int col0 = littleEndianToInt(key, 0); + W[0][0] = col0; + int col1 = littleEndianToInt(key, 4); + W[0][1] = col1; + int col2 = littleEndianToInt(key, 8); + W[0][2] = col2; + int col3 = littleEndianToInt(key, 12); + W[0][3] = col3; + + int col4 = littleEndianToInt(key, 16); + W[1][0] = col4; + int col5 = littleEndianToInt(key, 20); + W[1][1] = col5; + int col6 = littleEndianToInt(key, 24); + W[1][2] = col6; + int col7 = littleEndianToInt(key, 28); + W[1][3] = col7; + + int i = 2, rcon = 1, colx; + for (;;) { + colx = subWord(shift(col7, 8)) ^ rcon; + rcon <<= 1; + col0 ^= colx; + W[i][0] = col0; + col1 ^= col0; + W[i][1] = col1; + col2 ^= col1; + W[i][2] = col2; + col3 ^= col2; + W[i][3] = col3; + ++i; + + if (i >= 15) { + break; + } + + colx = subWord(col3); + col4 ^= colx; + W[i][0] = col4; + col5 ^= col4; + W[i][1] = col5; + col6 ^= col5; + W[i][2] = col6; + col7 ^= col6; + W[i][3] = col7; + ++i; + } + + break; + } + default: { + throw new IllegalStateException("Should never get here"); + } + } + + if (!forEncryption) { + for (int j = 1; j < ROUNDS; j++) { + for (int i = 0; i < 4; i++) { + W[j][i] = inv_mcol(W[j][i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[][] WorkingKey = null; + private boolean forEncryption; + + private static final int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AESLightEngine() { + + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, byte[] key) { + WorkingKey = generateWorkingKey(key, forEncryption); + this.forEncryption = forEncryption; + return; + } + + public String getAlgorithmName() { + return "AES"; + } + + public int getBlockSize() { + return BLOCK_SIZE; + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) { + if (WorkingKey == null) { + throw new IllegalStateException("AES engine not initialised"); + } + + if (inOff > (in.length - BLOCK_SIZE)) { + throw new IndexOutOfBoundsException("input buffer too short"); + } + + if (outOff > (out.length - BLOCK_SIZE)) { + throw new IndexOutOfBoundsException("output buffer too short"); + } + + if (forEncryption) { + encryptBlock(in, inOff, out, outOff, WorkingKey); + } else { + decryptBlock(in, inOff, out, outOff, WorkingKey); + } + + return BLOCK_SIZE; + } + + public void reset() { + } + + private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW) { + int C0 = littleEndianToInt(in, inOff + 0); + int C1 = littleEndianToInt(in, inOff + 4); + int C2 = littleEndianToInt(in, inOff + 8); + int C3 = littleEndianToInt(in, inOff + 12); + + int t0 = C0 ^ KW[0][0]; + int t1 = C1 ^ KW[0][1]; + int t2 = C2 ^ KW[0][2]; + + int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3]; + while (r < ROUNDS - 1) { + r0 = mcol((S[t0 & 255] & 255) ^ ((S[(t1 >> 8) & 255] & 255) << 8) ^ ((S[(t2 >> 16) & 255] & 255) << 16) + ^ (S[(r3 >> 24) & 255] << 24)) ^ KW[r][0]; + r1 = mcol((S[t1 & 255] & 255) ^ ((S[(t2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) + ^ (S[(t0 >> 24) & 255] << 24)) ^ KW[r][1]; + r2 = mcol((S[t2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(t0 >> 16) & 255] & 255) << 16) + ^ (S[(t1 >> 24) & 255] << 24)) ^ KW[r][2]; + r3 = mcol((S[r3 & 255] & 255) ^ ((S[(t0 >> 8) & 255] & 255) << 8) ^ ((S[(t1 >> 16) & 255] & 255) << 16) + ^ (S[(t2 >> 24) & 255] << 24)) ^ KW[r++][3]; + t0 = mcol((S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16) + ^ (S[(r3 >> 24) & 255] << 24)) ^ KW[r][0]; + t1 = mcol((S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) + ^ (S[(r0 >> 24) & 255] << 24)) ^ KW[r][1]; + t2 = mcol((S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16) + ^ (S[(r1 >> 24) & 255] << 24)) ^ KW[r][2]; + r3 = mcol((S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16) + ^ (S[(r2 >> 24) & 255] << 24)) ^ KW[r++][3]; + } + + r0 = mcol((S[t0 & 255] & 255) ^ ((S[(t1 >> 8) & 255] & 255) << 8) ^ ((S[(t2 >> 16) & 255] & 255) << 16) + ^ (S[(r3 >> 24) & 255] << 24)) ^ KW[r][0]; + r1 = mcol((S[t1 & 255] & 255) ^ ((S[(t2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) + ^ (S[(t0 >> 24) & 255] << 24)) ^ KW[r][1]; + r2 = mcol((S[t2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(t0 >> 16) & 255] & 255) << 16) + ^ (S[(t1 >> 24) & 255] << 24)) ^ KW[r][2]; + r3 = mcol((S[r3 & 255] & 255) ^ ((S[(t0 >> 8) & 255] & 255) << 8) ^ ((S[(t1 >> 16) & 255] & 255) << 16) + ^ (S[(t2 >> 24) & 255] << 24)) ^ KW[r++][3]; + + // the final round is a simple function of S + + C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16) + ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0]; + C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) + ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1]; + C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16) + ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2]; + C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16) + ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3]; + + intToLittleEndian(C0, out, outOff + 0); + intToLittleEndian(C1, out, outOff + 4); + intToLittleEndian(C2, out, outOff + 8); + intToLittleEndian(C3, out, outOff + 12); + } + + private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW) { + int C0 = littleEndianToInt(in, inOff + 0); + int C1 = littleEndianToInt(in, inOff + 4); + int C2 = littleEndianToInt(in, inOff + 8); + int C3 = littleEndianToInt(in, inOff + 12); + + int t0 = C0 ^ KW[ROUNDS][0]; + int t1 = C1 ^ KW[ROUNDS][1]; + int t2 = C2 ^ KW[ROUNDS][2]; + + int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3]; + while (r > 1) { + r0 = inv_mcol((Si[t0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) + ^ ((Si[(t2 >> 16) & 255] & 255) << 16) ^ (Si[(t1 >> 24) & 255] << 24)) ^ KW[r][0]; + r1 = inv_mcol((Si[t1 & 255] & 255) ^ ((Si[(t0 >> 8) & 255] & 255) << 8) + ^ ((Si[(r3 >> 16) & 255] & 255) << 16) ^ (Si[(t2 >> 24) & 255] << 24)) ^ KW[r][1]; + r2 = inv_mcol((Si[t2 & 255] & 255) ^ ((Si[(t1 >> 8) & 255] & 255) << 8) + ^ ((Si[(t0 >> 16) & 255] & 255) << 16) ^ (Si[(r3 >> 24) & 255] << 24)) ^ KW[r][2]; + r3 = inv_mcol((Si[r3 & 255] & 255) ^ ((Si[(t2 >> 8) & 255] & 255) << 8) + ^ ((Si[(t1 >> 16) & 255] & 255) << 16) ^ (Si[(t0 >> 24) & 255] << 24)) ^ KW[r--][3]; + t0 = inv_mcol((Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) + ^ ((Si[(r2 >> 16) & 255] & 255) << 16) ^ (Si[(r1 >> 24) & 255] << 24)) ^ KW[r][0]; + t1 = inv_mcol((Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) + ^ ((Si[(r3 >> 16) & 255] & 255) << 16) ^ (Si[(r2 >> 24) & 255] << 24)) ^ KW[r][1]; + t2 = inv_mcol((Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) + ^ ((Si[(r0 >> 16) & 255] & 255) << 16) ^ (Si[(r3 >> 24) & 255] << 24)) ^ KW[r][2]; + r3 = inv_mcol((Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) + ^ ((Si[(r1 >> 16) & 255] & 255) << 16) ^ (Si[(r0 >> 24) & 255] << 24)) ^ KW[r--][3]; + } + + r0 = inv_mcol((Si[t0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(t2 >> 16) & 255] & 255) << 16) + ^ (Si[(t1 >> 24) & 255] << 24)) ^ KW[r][0]; + r1 = inv_mcol((Si[t1 & 255] & 255) ^ ((Si[(t0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16) + ^ (Si[(t2 >> 24) & 255] << 24)) ^ KW[r][1]; + r2 = inv_mcol((Si[t2 & 255] & 255) ^ ((Si[(t1 >> 8) & 255] & 255) << 8) ^ ((Si[(t0 >> 16) & 255] & 255) << 16) + ^ (Si[(r3 >> 24) & 255] << 24)) ^ KW[r][2]; + r3 = inv_mcol((Si[r3 & 255] & 255) ^ ((Si[(t2 >> 8) & 255] & 255) << 8) ^ ((Si[(t1 >> 16) & 255] & 255) << 16) + ^ (Si[(t0 >> 24) & 255] << 24)) ^ KW[r][3]; + + // the final round's table is a simple function of Si + + C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16) + ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0]; + C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16) + ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1]; + C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16) + ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2]; + C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16) + ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3]; + + intToLittleEndian(C0, out, outOff + 0); + intToLittleEndian(C1, out, outOff + 4); + intToLittleEndian(C2, out, outOff + 8); + intToLittleEndian(C3, out, outOff + 12); + } + + private int bitsOfSecurity() { + if (WorkingKey == null) { + return 256; + } + return (WorkingKey.length - 7) << 5; + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/futures/ListenableFutureTask.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/futures/ListenableFutureTask.java index 52a65b4..0e17ec4 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/futures/ListenableFutureTask.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/futures/ListenableFutureTask.java @@ -29,8 +29,8 @@ import java.util.concurrent.Executor; * */ public class ListenableFutureTask extends FutureTask implements ListenableFuture { - - private final List listeners = new ArrayList(); + + private final List listeners = new ArrayList<>(); public ListenableFutureTask(Callable callable) { super(callable); @@ -62,7 +62,7 @@ public class ListenableFutureTask extends FutureTask implements Listenable } public static ListenableFutureTask create(Callable callableToSchedule) { - return new ListenableFutureTask(callableToSchedule); + return new ListenableFutureTask<>(callableToSchedule); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/AbstractWebSocketClient.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/AbstractWebSocketClient.java new file mode 100755 index 0000000..7508d8c --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/AbstractWebSocketClient.java @@ -0,0 +1,227 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class AbstractWebSocketClient implements IWebSocketClient { + + protected volatile int availableStringFrames = 0; + protected volatile int availableBinaryFrames = 0; + protected final List recievedPacketBuffer = new LinkedList<>(); + protected String currentURI; + + protected AbstractWebSocketClient(String currentURI) { + this.currentURI = currentURI; + } + + protected void addRecievedFrame(IWebSocketFrame frame) { + boolean str = frame.isString(); + synchronized(recievedPacketBuffer) { + recievedPacketBuffer.add(frame); + if(str) { + ++availableStringFrames; + }else { + ++availableBinaryFrames; + } + } + } + + @Override + public int availableFrames() { + synchronized(recievedPacketBuffer) { + return availableStringFrames + availableBinaryFrames; + } + } + + @Override + public IWebSocketFrame getNextFrame() { + synchronized(recievedPacketBuffer) { + if(!recievedPacketBuffer.isEmpty()) { + IWebSocketFrame frame = recievedPacketBuffer.remove(0); + if(frame.isString()) { + --availableStringFrames; + }else { + --availableBinaryFrames; + } + return frame; + }else { + return null; + } + } + } + + @Override + public List getNextFrames() { + synchronized(recievedPacketBuffer) { + if(!recievedPacketBuffer.isEmpty()) { + List ret = new ArrayList<>(recievedPacketBuffer); + recievedPacketBuffer.clear(); + availableStringFrames = 0; + availableBinaryFrames = 0; + return ret; + }else { + return null; + } + } + } + + @Override + public void clearFrames() { + synchronized(recievedPacketBuffer) { + recievedPacketBuffer.clear(); + } + } + + @Override + public int availableStringFrames() { + synchronized(recievedPacketBuffer) { + return availableStringFrames; + } + } + + @Override + public IWebSocketFrame getNextStringFrame() { + synchronized(recievedPacketBuffer) { + if(availableStringFrames > 0) { + Iterator itr = recievedPacketBuffer.iterator(); + while(itr.hasNext()) { + IWebSocketFrame r = itr.next(); + if(r.isString()) { + itr.remove(); + --availableStringFrames; + return r; + } + } + availableStringFrames = 0; + return null; + }else { + return null; + } + } + } + + @Override + public List getNextStringFrames() { + synchronized(recievedPacketBuffer) { + if(availableStringFrames > 0) { + List ret = new ArrayList<>(availableStringFrames); + Iterator itr = recievedPacketBuffer.iterator(); + while(itr.hasNext()) { + IWebSocketFrame r = itr.next(); + if(r.isString()) { + itr.remove(); + ret.add(r); + } + } + availableStringFrames = 0; + return ret; + }else { + return null; + } + } + } + + @Override + public void clearStringFrames() { + synchronized(recievedPacketBuffer) { + if(availableStringFrames > 0) { + Iterator itr = recievedPacketBuffer.iterator(); + while(itr.hasNext()) { + IWebSocketFrame r = itr.next(); + if(r.isString()) { + itr.remove(); + } + } + availableStringFrames = 0; + } + } + } + + @Override + public int availableBinaryFrames() { + synchronized(recievedPacketBuffer) { + return availableBinaryFrames; + } + } + + @Override + public IWebSocketFrame getNextBinaryFrame() { + synchronized(recievedPacketBuffer) { + if(availableBinaryFrames > 0) { + Iterator itr = recievedPacketBuffer.iterator(); + while(itr.hasNext()) { + IWebSocketFrame r = itr.next(); + if(!r.isString()) { + itr.remove(); + --availableBinaryFrames; + return r; + } + } + availableBinaryFrames = 0; + return null; + }else { + return null; + } + } + } + + @Override + public List getNextBinaryFrames() { + synchronized(recievedPacketBuffer) { + if(availableBinaryFrames > 0) { + List ret = new ArrayList<>(availableBinaryFrames); + Iterator itr = recievedPacketBuffer.iterator(); + while(itr.hasNext()) { + IWebSocketFrame r = itr.next(); + if(!r.isString()) { + itr.remove(); + ret.add(r); + } + } + availableBinaryFrames = 0; + return ret; + }else { + return null; + } + } + } + + @Override + public void clearBinaryFrames() { + synchronized(recievedPacketBuffer) { + if(availableBinaryFrames > 0) { + Iterator itr = recievedPacketBuffer.iterator(); + while(itr.hasNext()) { + IWebSocketFrame r = itr.next(); + if(!r.isString()) { + itr.remove(); + } + } + availableBinaryFrames = 0; + } + } + } + + @Override + public String getCurrentURI() { + return currentURI; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EaglerMissingResourceException.java similarity index 63% rename from src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EaglerMissingResourceException.java index c3b6593..a4791d1 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EaglerMissingResourceException.java @@ -1,10 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.internal; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; - /** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * Copyright (c) 2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -18,17 +15,21 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; * POSSIBILITY OF SUCH DAMAGE. * */ -public class PlatformBufferFunctions { - - public static void put(ByteBuffer newBuffer, ByteBuffer flip) { - newBuffer.put(flip); +public class EaglerMissingResourceException extends RuntimeException { + + public EaglerMissingResourceException() { } - public static void put(IntBuffer intBuffer, int index, int[] data) { - int p = intBuffer.position(); - intBuffer.position(index); - intBuffer.put(data); - intBuffer.position(p); + public EaglerMissingResourceException(String message, Throwable cause) { + super(message, cause); } - + + public EaglerMissingResourceException(String message) { + super(message); + } + + public EaglerMissingResourceException(Throwable cause) { + super(cause); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumFireKeyboardEvent.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumFireKeyboardEvent.java new file mode 100755 index 0000000..d583d91 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumFireKeyboardEvent.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumFireKeyboardEvent { + KEY_DOWN, KEY_UP, KEY_REPEAT; +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumFireMouseEvent.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumFireMouseEvent.java new file mode 100755 index 0000000..78ffde7 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumFireMouseEvent.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumFireMouseEvent { + MOUSE_DOWN, MOUSE_UP, MOUSE_MOVE, MOUSE_WHEEL; +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformANGLE.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformANGLE.java index 051b5f5..84e127d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformANGLE.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformANGLE.java @@ -1,74 +1,79 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public enum EnumPlatformANGLE { - - DEFAULT(225281 /* GLFW_ANGLE_PLATFORM_TYPE_NONE */, "default", "Default"), - D3D11(225285 /* GLFW_ANGLE_PLATFORM_TYPE_D3D11 */, "d3d11", "Direct3D11"), - OPENGL(225282 /* GLFW_ANGLE_PLATFORM_TYPE_OPENGL */, "opengl", "OpenGL"), - OPENGLES(225283 /* GLFW_ANGLE_PLATFORM_TYPE_OPENGLES */, "opengles", "OpenGL ES"), - METAL(225288 /* GLFW_ANGLE_PLATFORM_TYPE_METAL */, "metal", "Metal"), - VULKAN(225287 /* GLFW_ANGLE_PLATFORM_TYPE_VULKAN */, "vulkan", "Vulkan"); - - public final int eglEnum; - public final String id; - public final String name; - - private EnumPlatformANGLE(int eglEnum, String id, String name) { - this.eglEnum = eglEnum; - this.id = id; - this.name = name; - } - - public String toString() { - return id; - } - - public static EnumPlatformANGLE fromId(String id) { - if(id.equals("d3d11") || id.equals("d3d") || id.equals("dx11")) { - return D3D11; - }else if(id.equals("opengl")) { - return OPENGL; - }else if(id.equals("opengles")) { - return OPENGLES; - }else if(id.equals("metal")) { - return METAL; - }else if(id.equals("vulkan")) { - return VULKAN; - }else { - return DEFAULT; - } - } - - public static EnumPlatformANGLE fromGLRendererString(String str) { - str = str.toLowerCase(); - if(str.contains("direct3d11") || str.contains("d3d11")) { - return D3D11; - }else if(str.contains("opengl es")) { - return OPENGLES; - }else if(str.contains("opengl")) { - return OPENGL; - }else if(str.contains("metal")) { - return METAL; - }else if(str.contains("vulkan")) { - return VULKAN; - }else { - return DEFAULT; - } - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumPlatformANGLE { + + DEFAULT(225281 /* GLFW_ANGLE_PLATFORM_TYPE_NONE */, "default", "Default"), + D3D9(225284 /* GLFW_ANGLE_PLATFORM_TYPE_D3D9 */, "d3d9", "Direct3D9"), + D3D11(225285 /* GLFW_ANGLE_PLATFORM_TYPE_D3D11 */, "d3d11", "Direct3D11"), + OPENGL(225282 /* GLFW_ANGLE_PLATFORM_TYPE_OPENGL */, "opengl", "OpenGL"), + OPENGLES(225283 /* GLFW_ANGLE_PLATFORM_TYPE_OPENGLES */, "opengles", "OpenGL ES"), + METAL(225288 /* GLFW_ANGLE_PLATFORM_TYPE_METAL */, "metal", "Metal"), + VULKAN(225287 /* GLFW_ANGLE_PLATFORM_TYPE_VULKAN */, "vulkan", "Vulkan"); + + public final int eglEnum; + public final String id; + public final String name; + + private EnumPlatformANGLE(int eglEnum, String id, String name) { + this.eglEnum = eglEnum; + this.id = id; + this.name = name; + } + + public String toString() { + return id; + } + + public static EnumPlatformANGLE fromId(String id) { + if(id.equals("d3d11") || id.equals("d3d") || id.equals("dx11")) { + return D3D11; + }else if(id.equals("d3d9") || id.equals("dx9")) { + return D3D9; + }else if(id.equals("opengl")) { + return OPENGL; + }else if(id.equals("opengles")) { + return OPENGLES; + }else if(id.equals("metal")) { + return METAL; + }else if(id.equals("vulkan")) { + return VULKAN; + }else { + return DEFAULT; + } + } + + public static EnumPlatformANGLE fromGLRendererString(String str) { + str = str.toLowerCase(); + if(str.contains("direct3d11") || str.contains("d3d11")) { + return D3D11; + }else if(str.contains("direct3d9") || str.contains("d3d9")) { + return D3D9; + }else if(str.contains("opengl es")) { + return OPENGLES; + }else if(str.contains("opengl")) { + return OPENGL; + }else if(str.contains("metal")) { + return METAL; + }else if(str.contains("vulkan")) { + return VULKAN; + }else { + return DEFAULT; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformAgent.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformAgent.java index 1ea4f44..dc0171e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformAgent.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformAgent.java @@ -35,12 +35,15 @@ public enum EnumPlatformAgent { } public static EnumPlatformAgent getFromUA(String ua) { + if(ua == null) { + return UNKNOWN; + } ua = " " + ua.toLowerCase(); if(ua.contains(" edg/")) { return EDGE; }else if(ua.contains(" opr/")) { return OPERA; - }else if(ua.contains(" chrome/")) { + }else if(ua.contains(" chrome/") || ua.contains(" chromium/")) { return CHROME; }else if(ua.contains(" firefox/")) { return FIREFOX; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformOS.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformOS.java index b42f732..6818396 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformOS.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumPlatformOS.java @@ -1,74 +1,80 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import net.minecraft.util.Util; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public enum EnumPlatformOS { - WINDOWS("Windows", Util.EnumOS.WINDOWS), MACOS("MacOS", Util.EnumOS.OSX), LINUX("Linux", Util.EnumOS.LINUX), - CHROMEBOOK_LINUX("ChromeOS", Util.EnumOS.LINUX), OTHER("Unknown", Util.EnumOS.UNKNOWN); - - private final String name; - private final Util.EnumOS minecraftEnum; - - private EnumPlatformOS(String name, Util.EnumOS minecraftEnum) { - this.name = name; - this.minecraftEnum = minecraftEnum; - } - - public String getName() { - return name; - } - - public Util.EnumOS getMinecraftEnum() { - return minecraftEnum; - } - - public String toString() { - return name; - } - - public static EnumPlatformOS getFromJVM(String osNameProperty) { - osNameProperty = osNameProperty.toLowerCase(); - if(osNameProperty.contains("chrome")) { - return CHROMEBOOK_LINUX; - }else if(osNameProperty.contains("linux")) { - return LINUX; - }else if(osNameProperty.contains("windows") || osNameProperty.contains("win32")) { - return WINDOWS; - }else if(osNameProperty.contains("macos") || osNameProperty.contains("osx")) { - return MACOS; - }else { - return OTHER; - } - } - - public static EnumPlatformOS getFromUA(String ua) { - ua = " " + ua.toLowerCase(); - if(ua.contains(" cros")) { - return CHROMEBOOK_LINUX; - }else if(ua.contains(" linux")) { - return LINUX; - }else if(ua.contains(" windows") || ua.contains(" win32") || ua.contains(" win64")) { - return WINDOWS; - }else if(ua.contains(" macos") || ua.contains(" osx")) { - return MACOS; - }else { - return OTHER; - } - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.minecraft.util.Util; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumPlatformOS { + WINDOWS("Windows", Util.EnumOS.WINDOWS), MACOS("MacOS", Util.EnumOS.OSX), LINUX("Linux", Util.EnumOS.LINUX), + CHROMEBOOK_LINUX("ChromeOS", Util.EnumOS.LINUX), OTHER("Unknown", Util.EnumOS.UNKNOWN); + + private final String name; + private final Util.EnumOS minecraftEnum; + + private EnumPlatformOS(String name, Util.EnumOS minecraftEnum) { + this.name = name; + this.minecraftEnum = minecraftEnum; + } + + public String getName() { + return name; + } + + public Util.EnumOS getMinecraftEnum() { + return minecraftEnum; + } + + public String toString() { + return name; + } + + public static EnumPlatformOS getFromJVM(String osNameProperty) { + if(osNameProperty == null) { + return OTHER; + } + osNameProperty = osNameProperty.toLowerCase(); + if(osNameProperty.contains("chrome")) { + return CHROMEBOOK_LINUX; + }else if(osNameProperty.contains("linux")) { + return LINUX; + }else if(osNameProperty.contains("windows") || osNameProperty.contains("win32")) { + return WINDOWS; + }else if(osNameProperty.contains("macos") || osNameProperty.contains("osx")) { + return MACOS; + }else { + return OTHER; + } + } + + public static EnumPlatformOS getFromUA(String ua) { + if(ua == null) { + return OTHER; + } + ua = " " + ua.toLowerCase(); + if(ua.contains(" cros")) { + return CHROMEBOOK_LINUX; + }else if(ua.contains(" linux")) { + return LINUX; + }else if(ua.contains(" windows") || ua.contains(" win32") || ua.contains(" win64")) { + return WINDOWS; + }else if(ua.contains(" macos") || ua.contains(" osx")) { + return MACOS; + }else { + return OTHER; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumTouchEvent.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumTouchEvent.java new file mode 100755 index 0000000..0f49fb2 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumTouchEvent.java @@ -0,0 +1,43 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumTouchEvent { + TOUCHSTART(0), TOUCHMOVE(1), TOUCHEND(2); + + public final int id; + + private EnumTouchEvent(int id) { + this.id = id; + } + + public static EnumTouchEvent getById(int id) { + if(id >= 0 && id < lookup.length) { + return lookup[id]; + }else { + return null; + } + } + + private static final EnumTouchEvent[] lookup = new EnumTouchEvent[3]; + + static { + EnumTouchEvent[] v = values(); + for(int i = 0; i < v.length; ++i) { + lookup[v[i].id] = v[i]; + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumWebViewContentMode.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumWebViewContentMode.java new file mode 100755 index 0000000..e6a9030 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/EnumWebViewContentMode.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumWebViewContentMode { + URL_BASED, BLOB_BASED; +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GLObjectMap.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GLObjectMap.java index f47b755..31b2d68 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GLObjectMap.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GLObjectMap.java @@ -1,70 +1,77 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GLObjectMap { - private Object[] values; - private int size; - private int insertIndex; - public int allocatedObjects; - - public GLObjectMap(int initialSize) { - this.values = new Object[initialSize]; - this.size = initialSize; - this.insertIndex = 0; - this.allocatedObjects = 0; - } - - public int register(T obj) { - int start = insertIndex; - do { - ++insertIndex; - if(insertIndex >= size) { - insertIndex = 0; - } - if(insertIndex == start) { - resize(); - return register(obj); - } - }while(values[insertIndex] != null); - values[insertIndex] = obj; - ++allocatedObjects; - return insertIndex + 1; - } - - public T free(int obj) { - --obj; - if(obj >= size || obj < 0) return null; - Object ret = values[obj]; - values[obj] = null; - --allocatedObjects; - return (T) ret; - } - - public T get(int obj) { - --obj; - if(obj >= size || obj < 0) return null; - return (T) values[obj]; - } - - private void resize() { - int oldSize = size; - size += size / 2; - Object[] oldValues = values; - values = new Object[size]; - System.arraycopy(oldValues, 0, values, 0, oldSize); - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GLObjectMap { + private Object[] values; + private int size; + private int insertIndex; + public int allocatedObjects; + + public GLObjectMap(int initialSize) { + this.values = new Object[initialSize]; + this.size = initialSize; + this.insertIndex = 0; + this.allocatedObjects = 0; + } + + public int register(T obj) { + int start = insertIndex; + do { + ++insertIndex; + if(insertIndex >= size) { + insertIndex = 0; + } + if(insertIndex == start) { + resize(); + return register(obj); + } + }while(values[insertIndex] != null); + values[insertIndex] = obj; + ++allocatedObjects; + return insertIndex + 1; + } + + public T free(int obj) { + --obj; + if(obj >= size || obj < 0) return null; + Object ret = values[obj]; + values[obj] = null; + --allocatedObjects; + return (T) ret; + } + + public T get(int obj) { + --obj; + if(obj >= size || obj < 0) return null; + return (T) values[obj]; + } + + private void resize() { + int oldSize = size; + size += size / 2; + Object[] oldValues = values; + values = new Object[size]; + System.arraycopy(oldValues, 0, values, 0, oldSize); + } + + public void clear() { + if(allocatedObjects == 0) return; + values = new Object[size]; + insertIndex = 0; + allocatedObjects = 0; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GamepadConstants.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GamepadConstants.java new file mode 100755 index 0000000..d9a9411 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/GamepadConstants.java @@ -0,0 +1,134 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GamepadConstants { + + private static final String[] buttonNames = new String[24]; + private static final String[] axisNames = new String[4]; + private static final int[] eaglerButtonsToGLFW = new int[24]; + private static final int[] glfwButtonsToEagler = new int[24]; + + public static final int GAMEPAD_NONE = -1; + public static final int GAMEPAD_A = 0; + public static final int GAMEPAD_B = 1; + public static final int GAMEPAD_X = 2; + public static final int GAMEPAD_Y = 3; + public static final int GAMEPAD_LEFT_BUTTON = 4; + public static final int GAMEPAD_RIGHT_BUTTON = 5; + public static final int GAMEPAD_LEFT_TRIGGER = 6; + public static final int GAMEPAD_RIGHT_TRIGGER = 7; + public static final int GAMEPAD_BACK = 8; + public static final int GAMEPAD_START = 9; + public static final int GAMEPAD_LEFT_STICK_BUTTON = 10; + public static final int GAMEPAD_RIGHT_STICK_BUTTON = 11; + public static final int GAMEPAD_DPAD_UP = 12; + public static final int GAMEPAD_DPAD_DOWN = 13; + public static final int GAMEPAD_DPAD_LEFT = 14; + public static final int GAMEPAD_DPAD_RIGHT = 15; + public static final int GAMEPAD_GUIDE = 16; + + public static final int GAMEPAD_AXIS_NONE = -1; + public static final int GAMEPAD_AXIS_LEFT_STICK_X = 0; + public static final int GAMEPAD_AXIS_LEFT_STICK_Y = 1; + public static final int GAMEPAD_AXIS_RIGHT_STICK_X = 2; + public static final int GAMEPAD_AXIS_RIGHT_STICK_Y = 3; + + private static final int GLFW_GAMEPAD_BUTTON_A = 0, GLFW_GAMEPAD_BUTTON_B = 1, GLFW_GAMEPAD_BUTTON_X = 2, + GLFW_GAMEPAD_BUTTON_Y = 3, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER = 4, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER = 5, + GLFW_GAMEPAD_BUTTON_BACK = 6, GLFW_GAMEPAD_BUTTON_START = 7, GLFW_GAMEPAD_BUTTON_GUIDE = 8, + GLFW_GAMEPAD_BUTTON_LEFT_THUMB = 9, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB = 10, GLFW_GAMEPAD_BUTTON_DPAD_UP = 11, + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT = 12, GLFW_GAMEPAD_BUTTON_DPAD_DOWN = 13, GLFW_GAMEPAD_BUTTON_DPAD_LEFT = 14; + + private static void registerBtn(int eaglerBtn, int glfwBtn, String name) { + if(eaglerButtonsToGLFW[eaglerBtn] != 0) throw new IllegalArgumentException("Duplicate eaglerButtonsToGLFW entry: " + eaglerBtn + " -> " + glfwBtn); + if(glfwBtn != -1 && glfwButtonsToEagler[glfwBtn] != 0) throw new IllegalArgumentException("Duplicate glfwButtonsToEAGLER entry: " + glfwBtn + " -> " + eaglerBtn); + eaglerButtonsToGLFW[eaglerBtn] = glfwBtn; + if(glfwBtn != -1) glfwButtonsToEagler[glfwBtn] = eaglerBtn; + if(buttonNames[eaglerBtn] != null) throw new IllegalArgumentException("Duplicate buttonNames entry: " + eaglerBtn); + buttonNames[eaglerBtn] = name; + } + + private static void registerAxis(int eaglerAxis, String name) { + if(axisNames[eaglerAxis] != null) throw new IllegalArgumentException("Duplicate axisNames entry: " + eaglerAxis); + axisNames[eaglerAxis] = name; + } + + static { + registerBtn(GAMEPAD_A, GLFW_GAMEPAD_BUTTON_A, "A"); + registerBtn(GAMEPAD_B, GLFW_GAMEPAD_BUTTON_B, "B"); + registerBtn(GAMEPAD_X, GLFW_GAMEPAD_BUTTON_X, "X"); + registerBtn(GAMEPAD_Y, GLFW_GAMEPAD_BUTTON_Y, "Y"); + registerBtn(GAMEPAD_LEFT_BUTTON, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, "Left Button"); + registerBtn(GAMEPAD_LEFT_TRIGGER, -1, "Left Trigger"); + registerBtn(GAMEPAD_RIGHT_BUTTON, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, "Right Button"); + registerBtn(GAMEPAD_RIGHT_TRIGGER, -1, "Right Trigger"); + registerBtn(GAMEPAD_BACK, GLFW_GAMEPAD_BUTTON_BACK, "Back"); + registerBtn(GAMEPAD_START, GLFW_GAMEPAD_BUTTON_START, "Start"); + registerBtn(GAMEPAD_LEFT_STICK_BUTTON, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, "L. Stick Button"); + registerBtn(GAMEPAD_RIGHT_STICK_BUTTON, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, "R. Stick Button"); + registerBtn(GAMEPAD_DPAD_UP, GLFW_GAMEPAD_BUTTON_DPAD_UP, "D-Pad Up"); + registerBtn(GAMEPAD_DPAD_DOWN, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, "D-Pad Down"); + registerBtn(GAMEPAD_DPAD_LEFT, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, "D-Pad Left"); + registerBtn(GAMEPAD_DPAD_RIGHT, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, "D-Pad Right"); + registerBtn(GAMEPAD_GUIDE, GLFW_GAMEPAD_BUTTON_GUIDE, "Guide"); + registerAxis(GAMEPAD_AXIS_LEFT_STICK_X, "Left Stick X"); + registerAxis(GAMEPAD_AXIS_LEFT_STICK_Y, "Left Stick Y"); + registerAxis(GAMEPAD_AXIS_RIGHT_STICK_X, "Right Stick X"); + registerAxis(GAMEPAD_AXIS_RIGHT_STICK_Y, "Right Stick Y"); + } + + public static int getEaglerButtonFromBrowser(int button) { + return button; + } + + public static int getBrowserButtonFromEagler(int button) { + return button; + } + + public static int getEaglerButtonFromGLFW(int button) { + if(button >= 0 && button < glfwButtonsToEagler.length) { + return glfwButtonsToEagler[button]; + }else { + return -1; + } + } + + public static int getGLFWButtonFromEagler(int button) { + if(button >= 0 && button < eaglerButtonsToGLFW.length) { + return eaglerButtonsToGLFW[button]; + }else { + return -1; + } + } + + public static String getButtonName(int button) { + if(button >= 0 && button < buttonNames.length) { + return buttonNames[button]; + }else { + return "Button " + button; + } + } + + public static String getAxisName(int button) { + if(button >= 0 && button < axisNames.length) { + return axisNames[button]; + }else { + return "Axis " + button; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java index 5320bbf..3086747 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java @@ -1,87 +1,101 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry; -import org.json.JSONObject; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public interface IClientConfigAdapter { - - public static class DefaultServer { - - public final String name; - public final String addr; - - public DefaultServer(String name, String addr) { - this.name = name; - this.addr = addr; - } - - } - - String getDefaultLocale(); - - List getDefaultServerList(); - - String getServerToJoin(); - - String getWorldsDB(); - - String getResourcePacksDB(); - - JSONObject getIntegratedServerOpts(); - - List getRelays(); - - boolean isCheckShaderGLErrors(); - - boolean isDemo(); - - boolean allowUpdateSvc(); - - boolean allowUpdateDL(); - - boolean isEnableDownloadOfflineButton(); - - String getDownloadOfflineButtonLink(); - - boolean useSpecialCursors(); - - boolean isLogInvalidCerts(); - - boolean isCheckRelaysForUpdates(); - - boolean isEnableSignatureBadge(); - - boolean isAllowVoiceClient(); - - boolean isAllowFNAWSkins(); - - String getLocalStorageNamespace(); - - boolean isEnableMinceraft(); - - IClientConfigAdapterHooks getHooks(); -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry; +import org.json.JSONObject; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IClientConfigAdapter { + + public static class DefaultServer { + + public final String name; + public final String addr; + public final boolean hideAddress; + + public DefaultServer(String name, String addr, boolean hideAddress) { + this.name = name; + this.addr = addr; + this.hideAddress = hideAddress; + } + + } + + String getDefaultLocale(); + + List getDefaultServerList(); + + String getServerToJoin(); + + String getWorldsDB(); + + String getResourcePacksDB(); + + JSONObject getIntegratedServerOpts(); + + List getRelays(); + + boolean isCheckShaderGLErrors(); + + boolean isDemo(); + + boolean allowUpdateSvc(); + + boolean allowUpdateDL(); + + boolean isEnableDownloadOfflineButton(); + + String getDownloadOfflineButtonLink(); + + boolean useSpecialCursors(); + + boolean isLogInvalidCerts(); + + boolean isCheckRelaysForUpdates(); + + boolean isEnableSignatureBadge(); + + boolean isAllowVoiceClient(); + + boolean isAllowFNAWSkins(); + + String getLocalStorageNamespace(); + + boolean isEnableMinceraft(); + + boolean isEnableServerCookies(); + + boolean isAllowServerRedirects(); + + boolean isOpenDebugConsoleOnLaunch(); + + boolean isForceWebViewSupport(); + + boolean isEnableWebViewCSP(); + + boolean isAllowBootMenu(); + + boolean isForceProfanityFilter(); + + boolean isEaglerNoDelay(); + + boolean isRamdiskMode(); + + IClientConfigAdapterHooks getHooks(); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java index 8de6e09..888e36f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java @@ -5,21 +5,14 @@ import java.util.function.Consumer; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -32,4 +25,6 @@ public interface IClientConfigAdapterHooks { void callCrashReportHook(String crashReport, Consumer customMessageCB); + void callScreenChangedHook(String screenName, int scaledWidth, int scaledHeight, int realWidth, int realHeight, int scaleFactor); + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IEaglerFilesystem.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IEaglerFilesystem.java new file mode 100755 index 0000000..3c5f11c --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IEaglerFilesystem.java @@ -0,0 +1,46 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerFilesystem { + + String getFilesystemName(); + + String getInternalDBName(); + + boolean isRamdisk(); + + boolean eaglerDelete(String pathName); + + ByteBuffer eaglerRead(String pathName); + + void eaglerWrite(String pathName, ByteBuffer data); + + boolean eaglerExists(String pathName); + + boolean eaglerMove(String pathNameOld, String pathNameNew); + + int eaglerCopy(String pathNameOld, String pathNameNew); + + int eaglerSize(String pathName); + + void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive); + + void closeHandle(); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IServerQuery.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IServerQuery.java index ba97b26..6e9340d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IServerQuery.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IServerQuery.java @@ -1,121 +1,125 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import org.json.JSONObject; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public interface IServerQuery { - - public static final long defaultTimeout = 10000l; - - public static enum QueryReadyState { - CONNECTING(true, false), OPEN(true, false), CLOSED(false, true), FAILED(false, true); - - private final boolean open; - private final boolean closed; - - private QueryReadyState(boolean open, boolean closed) { - this.open = open; - this.closed = closed; - } - - public boolean isOpen() { - return open; - } - - public boolean isClosed() { - return closed; - } - - } - - void send(String str); - - default void send(JSONObject json) { - send(json.toString()); - } - - void send(byte[] bytes); - - int responsesAvailable(); - - QueryResponse getResponse(); - - int binaryResponsesAvailable(); - - byte[] getBinaryResponse(); - - QueryReadyState readyState(); - - default boolean isOpen() { - return readyState().isOpen(); - } - - default boolean isClosed() { - return readyState().isClosed(); - } - - void close(); - - EnumServerRateLimit getRateLimit(); - - default boolean awaitResponseAvailable(long timeout) { - long start = System.currentTimeMillis(); - while(isOpen() && responsesAvailable() <= 0 && (timeout <= 0l || System.currentTimeMillis() - start < timeout)) { - try { - Thread.sleep(0l, 250000); - } catch (InterruptedException e) { - } - } - return responsesAvailable() > 0; - } - - default boolean awaitResponseAvailable() { - return awaitResponseAvailable(defaultTimeout); - } - - default boolean awaitResponseBinaryAvailable(long timeout) { - long start = System.currentTimeMillis(); - while(isOpen() && binaryResponsesAvailable() <= 0 && (timeout <= 0l || System.currentTimeMillis() - start < timeout)) { - try { - Thread.sleep(0l, 250000); - } catch (InterruptedException e) { - } - } - return binaryResponsesAvailable() > 0; - } - - default boolean awaitResponseBinaryAvailable() { - return awaitResponseBinaryAvailable(defaultTimeout); - } - - default QueryResponse awaitResponse(long timeout) { - return awaitResponseAvailable(timeout) ? getResponse() : null; - } - - default QueryResponse awaitResponse() { - return awaitResponseAvailable() ? getResponse() : null; - } - - default byte[] awaitResponseBinary(long timeout) { - return awaitResponseBinaryAvailable(timeout) ? getBinaryResponse() : null; - } - - default byte[] awaitResponseBinary() { - return awaitResponseBinaryAvailable() ? getBinaryResponse() : null; - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IServerQuery { + + public static final long defaultTimeout = 10000l; + + public static enum QueryReadyState { + CONNECTING(true, false), OPEN(true, false), CLOSED(false, true), FAILED(false, true); + + private final boolean open; + private final boolean closed; + + private QueryReadyState(boolean open, boolean closed) { + this.open = open; + this.closed = closed; + } + + public boolean isOpen() { + return open; + } + + public boolean isClosed() { + return closed; + } + + } + + void update(); + + void send(String str); + + default void send(JSONObject json) { + send(json.toString()); + } + + void send(byte[] bytes); + + int responsesAvailable(); + + QueryResponse getResponse(); + + int binaryResponsesAvailable(); + + byte[] getBinaryResponse(); + + QueryReadyState readyState(); + + default boolean isOpen() { + return readyState().isOpen(); + } + + default boolean isClosed() { + return readyState().isClosed(); + } + + void close(); + + EnumServerRateLimit getRateLimit(); + + default boolean awaitResponseAvailable(long timeout) { + long start = EagRuntime.steadyTimeMillis(); + while(isOpen() && responsesAvailable() <= 0 && (timeout <= 0l || EagRuntime.steadyTimeMillis() - start < timeout)) { + try { + Thread.sleep(0l, 250000); + } catch (InterruptedException e) { + } + } + return responsesAvailable() > 0; + } + + default boolean awaitResponseAvailable() { + return awaitResponseAvailable(defaultTimeout); + } + + default boolean awaitResponseBinaryAvailable(long timeout) { + long start = EagRuntime.steadyTimeMillis(); + while(isOpen() && binaryResponsesAvailable() <= 0 && (timeout <= 0l || EagRuntime.steadyTimeMillis() - start < timeout)) { + try { + Thread.sleep(0l, 250000); + } catch (InterruptedException e) { + } + } + return binaryResponsesAvailable() > 0; + } + + default boolean awaitResponseBinaryAvailable() { + return awaitResponseBinaryAvailable(defaultTimeout); + } + + default QueryResponse awaitResponse(long timeout) { + return awaitResponseAvailable(timeout) ? getResponse() : null; + } + + default QueryResponse awaitResponse() { + return awaitResponseAvailable() ? getResponse() : null; + } + + default byte[] awaitResponseBinary(long timeout) { + return awaitResponseBinaryAvailable(timeout) ? getBinaryResponse() : null; + } + + default byte[] awaitResponseBinary() { + return awaitResponseBinaryAvailable() ? getBinaryResponse() : null; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IWebSocketClient.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IWebSocketClient.java new file mode 100755 index 0000000..7646561 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IWebSocketClient.java @@ -0,0 +1,62 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.List; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IWebSocketClient { + + EnumEaglerConnectionState getState(); + + boolean connectBlocking(int timeoutMS); + + boolean isOpen(); + + boolean isClosed(); + + void close(); + + int availableFrames(); + + IWebSocketFrame getNextFrame(); + + List getNextFrames(); + + void clearFrames(); + + int availableStringFrames(); + + IWebSocketFrame getNextStringFrame(); + + List getNextStringFrames(); + + void clearStringFrames(); + + int availableBinaryFrames(); + + IWebSocketFrame getNextBinaryFrame(); + + List getNextBinaryFrames(); + + void clearBinaryFrames(); + + void send(String str); + + void send(byte[] bytes); + + String getCurrentURI(); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IWebSocketFrame.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IWebSocketFrame.java new file mode 100644 index 0000000..7ecec0f --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IWebSocketFrame.java @@ -0,0 +1,34 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.io.InputStream; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IWebSocketFrame { + + boolean isString(); + + String getString(); + + byte[] getByteArray(); + + InputStream getInputStream(); + + int getLength(); + + long getTimestamp(); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/QueryResponse.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/QueryResponse.java index 3f95a0a..f069acb 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/QueryResponse.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/QueryResponse.java @@ -2,6 +2,8 @@ package net.lax1dude.eaglercraft.v1_8.internal; import org.json.JSONObject; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + /** * Copyright (c) 2022 lax1dude. All Rights Reserved. * @@ -37,7 +39,7 @@ public class QueryResponse { this.serverBrand = obj.getString("brand"); this.serverName = obj.getString("name"); this.serverTime = obj.getLong("time"); - this.clientTime = System.currentTimeMillis(); + this.clientTime = EagRuntime.steadyTimeMillis(); this.serverCracked = obj.optBoolean("cracked", false); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/RamdiskFilesystemImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/RamdiskFilesystemImpl.java new file mode 100755 index 0000000..8f7f7e6 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/RamdiskFilesystemImpl.java @@ -0,0 +1,131 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.Map; +import java.util.TreeMap; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RamdiskFilesystemImpl implements IEaglerFilesystem { + + protected final String filesystemName; + protected final Map filesystemMap = new TreeMap<>(); + + public RamdiskFilesystemImpl(String filesystemName) { + this.filesystemName = filesystemName; + } + + @Override + public String getFilesystemName() { + return filesystemName; + } + + @Override + public String getInternalDBName() { + return "ramdisk:" + filesystemName; + } + + @Override + public boolean isRamdisk() { + return true; + } + + @Override + public boolean eaglerDelete(String pathName) { + return filesystemMap.remove(pathName) != null; + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + byte[] data = filesystemMap.get(pathName); + if(data != null) { + ByteBuffer buf = PlatformRuntime.castPrimitiveByteArray(data); + if(buf == null) { + buf = PlatformRuntime.allocateByteBuffer(data.length); + buf.put(data); + buf.flip(); + } + return buf; + }else { + return null; + } + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + byte[] arr = PlatformRuntime.castNativeByteBuffer(data); + if(arr == null) { + arr = new byte[data.remaining()]; + int i = data.position(); + data.get(arr); + data.position(i); + } + filesystemMap.put(pathName, arr); + } + + @Override + public boolean eaglerExists(String pathName) { + return filesystemMap.containsKey(pathName); + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + byte[] dat = filesystemMap.remove(pathNameOld); + if(dat != null) { + filesystemMap.put(pathNameNew, dat); + return true; + } + return false; + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + byte[] dat = filesystemMap.get(pathNameOld); + if(dat != null) { + filesystemMap.put(pathNameNew, dat); + return dat.length; + } + return -1; + } + + @Override + public int eaglerSize(String pathName) { + byte[] dat = filesystemMap.get(pathName); + return dat != null ? dat.length : -1; + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + if(!recursive) { + eaglerIterate(pathName, new VFSFilenameIteratorNonRecursive(itr, + VFSFilenameIteratorNonRecursive.countSlashes(pathName) + 1), true); + }else { + boolean b = pathName.length() == 0; + for(String key : filesystemMap.keySet()) { + if(b || key.startsWith(pathName)) { + itr.next(key); + } + } + } + } + + @Override + public void closeHandle() { + filesystemMap.clear(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/ScreenRecordParameters.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/ScreenRecordParameters.java new file mode 100755 index 0000000..8ff90dc --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/ScreenRecordParameters.java @@ -0,0 +1,37 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ScreenRecordParameters { + + public final EnumScreenRecordingCodec codec; + public final int resolutionDivisior; + public final int videoBitsPerSecond; + public final int audioBitsPerSecond; + public final int captureFrameRate; + + public ScreenRecordParameters(EnumScreenRecordingCodec codec, int resolutionDivisior, int videoBitsPerSecond, + int audioBitsPerSecond, int captureFrameRate) { + this.codec = codec; + this.resolutionDivisior = resolutionDivisior; + this.videoBitsPerSecond = videoBitsPerSecond; + this.audioBitsPerSecond = audioBitsPerSecond; + this.captureFrameRate = captureFrameRate; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/VFSFilenameIteratorNonRecursive.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/VFSFilenameIteratorNonRecursive.java new file mode 100755 index 0000000..612bec8 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/VFSFilenameIteratorNonRecursive.java @@ -0,0 +1,47 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class VFSFilenameIteratorNonRecursive implements VFSFilenameIterator { + + private final VFSFilenameIterator child; + private final int pathCount; + + public VFSFilenameIteratorNonRecursive(VFSFilenameIterator child, int pathCount) { + this.child = child; + this.pathCount = pathCount; + } + + @Override + public void next(String entry) { + int i = countSlashes(entry); + if(i == pathCount) { + child.next(entry); + } + } + + public static int countSlashes(String str) { + if(str.length() == 0) return -1; + int j = 0; + for(int i = 0, l = str.length(); i < l; ++i) { + if(str.charAt(i) == '/') { + ++j; + } + } + return j; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/WebViewOptions.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/WebViewOptions.java new file mode 100755 index 0000000..878c82c --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/WebViewOptions.java @@ -0,0 +1,67 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URI; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class WebViewOptions { + + public EnumWebViewContentMode contentMode = EnumWebViewContentMode.BLOB_BASED; + public String fallbackTitle = "WebView"; + public boolean scriptEnabled = false; + public boolean strictCSPEnable = true; + public boolean serverMessageAPIEnabled = false; + public URI url = null; + public byte[] blob = null; + public EaglercraftUUID permissionsOriginUUID = null; + + public WebViewOptions() { + } + + public WebViewOptions(boolean script, boolean serverMessageAPIEnabled, boolean strictCSPEnable, URI url) { + this.contentMode = EnumWebViewContentMode.URL_BASED; + this.scriptEnabled = script; + this.strictCSPEnable = strictCSPEnable; + this.serverMessageAPIEnabled = serverMessageAPIEnabled; + this.url = url; + this.permissionsOriginUUID = getURLOriginUUID(url); + } + + public WebViewOptions(boolean script, boolean serverMessageAPIEnabled, boolean strictCSPEnable, byte[] data, EaglercraftUUID permissionsOriginUUID) { + this.contentMode = EnumWebViewContentMode.BLOB_BASED; + this.scriptEnabled = script; + this.strictCSPEnable = strictCSPEnable; + this.serverMessageAPIEnabled = serverMessageAPIEnabled; + this.blob = data; + this.permissionsOriginUUID = permissionsOriginUUID; + } + + public static EaglercraftUUID getURLOriginUUID(URI url) { + return EaglercraftUUID.nameUUIDFromBytes(("URLOrigin:" + url.toString()).getBytes(StandardCharsets.UTF_8)); + } + + public static EaglercraftUUID getEmbedOriginUUID(byte[] sha256) { + byte[] vigg = "BlobOrigin:".getBytes(StandardCharsets.UTF_8); + byte[] eagler = new byte[sha256.length + vigg.length]; + System.arraycopy(vigg, 0, eagler, 0, vigg.length); + System.arraycopy(sha256, 0, eagler, vigg.length, sha256.length); + return EaglercraftUUID.nameUUIDFromBytes(eagler); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/Buffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/Buffer.java index 0aff7fc..ee27b23 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/Buffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/Buffer.java @@ -41,14 +41,14 @@ public interface Buffer { boolean hasRemaining(); - boolean isReadOnly(); - boolean hasArray(); Object array(); - int arrayOffset(); - boolean isDirect(); + static IndexOutOfBoundsException makeIOOBE(int idx) { + return new IndexOutOfBoundsException("Index out of range: " + idx); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java index b301853..4ec39b1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java @@ -18,12 +18,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; */ public interface ByteBuffer extends Buffer { - ByteBuffer slice(); - ByteBuffer duplicate(); - ByteBuffer asReadOnlyBuffer(); - byte get(); ByteBuffer put(byte b); @@ -42,10 +38,6 @@ public interface ByteBuffer extends Buffer { ByteBuffer put(byte[] src); - int arrayOffset(); - - ByteBuffer compact(); - char getChar(); ByteBuffer putChar(char value); @@ -54,7 +46,7 @@ public interface ByteBuffer extends Buffer { ByteBuffer putChar(int index, char value); - public abstract short getShort(); + short getShort(); ByteBuffer putShort(short value); @@ -106,4 +98,6 @@ public interface ByteBuffer extends Buffer { ByteBuffer position(int newPosition); + byte[] array(); + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerBufferInputStream.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerBufferInputStream.java index 383f83e..d6fd8ee 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerBufferInputStream.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerBufferInputStream.java @@ -3,7 +3,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; import java.io.IOException; import java.io.InputStream; - /** * Copyright (c) 2022 lax1dude. All Rights Reserved. * diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java index 73656cb..ecaf25f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java @@ -17,12 +17,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; */ public interface FloatBuffer extends Buffer { - FloatBuffer slice(); - FloatBuffer duplicate(); - FloatBuffer asReadOnlyBuffer(); - float get(); FloatBuffer put(float b); @@ -45,10 +41,6 @@ public interface FloatBuffer extends Buffer { FloatBuffer put(float[] src); - int getArrayOffset(); - - FloatBuffer compact(); - boolean isDirect(); FloatBuffer mark(); @@ -64,6 +56,8 @@ public interface FloatBuffer extends Buffer { FloatBuffer limit(int newLimit); FloatBuffer position(int newPosition); - + + float[] array(); + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java index 27b3360..0d96de5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java @@ -17,12 +17,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; */ public interface IntBuffer extends Buffer { - IntBuffer slice(); - IntBuffer duplicate(); - IntBuffer asReadOnlyBuffer(); - int get(); IntBuffer put(int b); @@ -45,10 +41,6 @@ public interface IntBuffer extends Buffer { IntBuffer put(int[] src); - int getArrayOffset(); - - IntBuffer compact(); - boolean isDirect(); IntBuffer mark(); @@ -64,6 +56,8 @@ public interface IntBuffer extends Buffer { IntBuffer limit(int newLimit); IntBuffer position(int newPosition); - + + int[] array(); + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java index b66bad6..56e54a5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java @@ -17,12 +17,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; */ public interface ShortBuffer extends Buffer { - ShortBuffer slice(); - ShortBuffer duplicate(); - ShortBuffer asReadOnlyBuffer(); - short get(); ShortBuffer put(short b); @@ -45,10 +41,6 @@ public interface ShortBuffer extends Buffer { ShortBuffer put(short[] src); - int getArrayOffset(); - - ShortBuffer compact(); - boolean isDirect(); ShortBuffer mark(); @@ -64,5 +56,7 @@ public interface ShortBuffer extends Buffer { ShortBuffer limit(int newLimit); ShortBuffer position(int newPosition); - + + short[] array(); + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSFilenameIteratorImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSFilenameIteratorImpl.java index 0311f15..6bfec45 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSFilenameIteratorImpl.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSFilenameIteratorImpl.java @@ -1,5 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.vfs2; +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; /** @@ -19,15 +20,17 @@ import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; */ class VFSFilenameIteratorImpl implements VFSFilenameIterator { + protected IEaglerFilesystem fs; protected VFSIterator2 itr; - VFSFilenameIteratorImpl(VFSIterator2 itr) { + VFSFilenameIteratorImpl(IEaglerFilesystem fs, VFSIterator2 itr) { + this.fs = fs; this.itr = itr; } @Override public void next(String entry) { - itr.next(new VFile2(entry)); + itr.next(VFile2.create(fs, entry)); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSListFilesIteratorImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSListFilesIteratorImpl.java index 81d4476..0e93774 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSListFilesIteratorImpl.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFSListFilesIteratorImpl.java @@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.vfs2; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; /** @@ -21,15 +22,17 @@ import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; */ class VFSListFilesIteratorImpl implements VFSFilenameIterator { + protected IEaglerFilesystem fs; protected List list; - VFSListFilesIteratorImpl(List list) { + VFSListFilesIteratorImpl(IEaglerFilesystem fs, List list) { + this.fs = fs; this.list = list; } @Override public void next(String entry) { - list.add(new VFile2(entry)); + list.add(VFile2.create(fs, entry)); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java index fbcfa1c..ecf6891 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java @@ -1,271 +1,310 @@ -package net.lax1dude.eaglercraft.v1_8.internal.vfs2; - -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class VFile2 { - - public static final String pathSeperator = "/"; - public static final String[] altPathSeperator = new String[] { "\\" }; - - public static String normalizePath(String p) { - for (int i = 0; i < altPathSeperator.length; ++i) { - p = p.replace(altPathSeperator[i], pathSeperator); - } - if (p.startsWith(pathSeperator)) { - p = p.substring(1); - } - if (p.endsWith(pathSeperator)) { - p = p.substring(0, p.length() - pathSeperator.length()); - } - return p; - } - - public static String[] splitPath(String p) { - String[] pth = normalizePath(p).split(pathSeperator); - for (int i = 0; i < pth.length; ++i) { - pth[i] = pth[i].trim(); - } - return pth; - } - - protected String path; - - public static String createPath(Object... p) { - ArrayList r = new ArrayList(); - for (int i = 0; i < p.length; ++i) { - if (p[i] == null) { - continue; - } - String gg = p[i].toString(); - if (gg == null) { - continue; - } - String[] parts = splitPath(gg); - for (int j = 0; j < parts.length; ++j) { - if (parts[j] == null || parts[j].equals(".")) { - continue; - } else if (parts[j].equals("..") && r.size() > 0) { - int k = r.size() - 1; - if (!r.get(k).equals("..")) { - r.remove(k); - } else { - r.add(".."); - } - } else { - r.add(parts[j]); - } - } - } - if (r.size() > 0) { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < r.size(); ++i) { - if (i > 0) { - s.append(pathSeperator); - } - s.append(r.get(i)); - } - return s.toString(); - } else { - return null; - } - } - - public VFile2(Object... p) { - this.path = createPath(p); - } - - public InputStream getInputStream() { - assertNotRelative(); - return new VFileInputStream(PlatformFilesystem.eaglerRead(path)); - } - - public OutputStream getOutputStream() { - assertNotRelative(); - return new VFileOutputStream(this); - } - - public String toString() { - return path; - } - - public boolean isRelative() { - return path == null || path.contains(".."); - } - - public void assertNotRelative() { - if (isRelative()) - throw new EaglerFileSystemException("Relative paths are not allowed: " + path); - } - - public boolean canRead() { - return !isRelative() && PlatformFilesystem.eaglerExists(path); - } - - public String getPath() { - return path.equals("unnamed") ? null : path; - } - - public String getName() { - int i = path.lastIndexOf(pathSeperator); - return i == -1 ? path : path.substring(i + 1); - } - - public static String getNameFromPath(String path) { - path = normalizePath(path); - int i = path.lastIndexOf(pathSeperator); - return i == -1 ? path : path.substring(i + 1); - } - - public boolean canWrite() { - return !isRelative(); - } - - public String getParent() { - if (path == null) { - return null; - } - int i = path.lastIndexOf(pathSeperator); - return i == -1 ? ".." : path.substring(0, i); - } - - public int hashCode() { - return path == null ? 0 : path.hashCode(); - } - - public boolean equals(Object o) { - return path != null && o != null && (o instanceof VFile2) && path.equals(((VFile2) o).path); - } - - public boolean exists() { - return !isRelative() && PlatformFilesystem.eaglerExists(path); - } - - public boolean delete() { - return !isRelative() && PlatformFilesystem.eaglerDelete(path); - } - - public boolean renameTo(String p) { - if (!isRelative() && PlatformFilesystem.eaglerMove(path, p)) { - return true; - } - return false; - } - - public boolean renameTo(VFile2 p) { - return renameTo(p.path); - } - - public int length() { - return isRelative() ? -1 : PlatformFilesystem.eaglerSize(path); - } - - public byte[] getAllBytes() { - assertNotRelative(); - if (!exists()) { - return null; - } - ByteBuffer readBuffer = PlatformFilesystem.eaglerRead(path); - byte[] copyBuffer = PlatformRuntime.castNativeByteBuffer(readBuffer); - if (copyBuffer != null) { - return copyBuffer; - } - try { - copyBuffer = new byte[readBuffer.remaining()]; - readBuffer.get(copyBuffer); - return copyBuffer; - } finally { - PlatformRuntime.freeByteBuffer(readBuffer); - } - } - - public String getAllChars() { - assertNotRelative(); - if (!exists()) { - return null; - } - return new String(getAllBytes(), StandardCharsets.UTF_8); - } - - public String[] getAllLines() { - assertNotRelative(); - if (!exists()) { - return null; - } - return EagUtils.linesArray(new String(getAllBytes(), StandardCharsets.UTF_8)); - } - - public void setAllChars(String bytes) { - setAllBytes(bytes.getBytes(StandardCharsets.UTF_8)); - } - - public void setAllBytes(byte[] bytes) { - assertNotRelative(); - ByteBuffer copyBuffer = PlatformRuntime.castPrimitiveByteArray(bytes); - if (copyBuffer != null) { - PlatformFilesystem.eaglerWrite(path, copyBuffer); - return; - } - copyBuffer = PlatformRuntime.allocateByteBuffer(bytes.length); - try { - copyBuffer.put(bytes); - copyBuffer.flip(); - PlatformFilesystem.eaglerWrite(path, copyBuffer); - } finally { - PlatformRuntime.freeByteBuffer(copyBuffer); - } - } - - public void iterateFiles(VFSIterator2 itr, boolean recursive) { - assertNotRelative(); - PlatformFilesystem.eaglerIterate(path, new VFSFilenameIteratorImpl(itr), recursive); - } - - public List listFilenames(boolean recursive) { - List ret = new ArrayList(); - PlatformFilesystem.eaglerIterate(path, new VFSListFilenamesIteratorImpl(ret), recursive); - return ret; - } - - public List listFiles(boolean recursive) { - List ret = new ArrayList(); - PlatformFilesystem.eaglerIterate(path, new VFSListFilesIteratorImpl(ret), recursive); - return ret; - } - - public static int copyFile(VFile2 src, VFile2 dst) { - src.assertNotRelative(); - dst.assertNotRelative(); - return PlatformFilesystem.eaglerCopy(src.path, dst.path); - } +package net.lax1dude.eaglercraft.v1_8.internal.vfs2; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class VFile2 { + + public static final String pathSeperator = "/"; + public static final String[] altPathSeperator = new String[] { "\\" }; + + static IEaglerFilesystem primaryFilesystem = null; + + public static void setPrimaryFilesystem(IEaglerFilesystem fs) { + primaryFilesystem = fs; + } + + public static String normalizePath(String p) { + for(int i = 0; i < altPathSeperator.length; ++i) { + p = p.replace(altPathSeperator[i], pathSeperator); + } + if(p.startsWith(pathSeperator)) { + p = p.substring(1); + } + if(p.endsWith(pathSeperator)) { + p = p.substring(0, p.length() - pathSeperator.length()); + } + return p; + } + + public static String[] splitPath(String p) { + String[] pth = normalizePath(p).split(pathSeperator); + for(int i = 0; i < pth.length; ++i) { + pth[i] = pth[i].trim(); + } + return pth; + } + + protected String path; + protected IEaglerFilesystem myFilesystem; + protected Supplier myFilesystemProvider; + + public static String createPath(Object... p) { + ArrayList r = new ArrayList<>(); + for(int i = 0; i < p.length; ++i) { + if(p[i] == null) { + continue; + } + String gg = p[i].toString(); + if(gg == null) { + continue; + } + String[] parts = splitPath(gg); + for(int j = 0; j < parts.length; ++j) { + if(parts[j] == null || parts[j].equals(".")) { + continue; + }else if(parts[j].equals("..") && r.size() > 0) { + int k = r.size() - 1; + if(!r.get(k).equals("..")) { + r.remove(k); + }else { + r.add(".."); + } + }else { + r.add(parts[j]); + } + } + } + if(r.size() > 0) { + StringBuilder s = new StringBuilder(); + for(int i = 0; i < r.size(); ++i) { + if(i > 0) { + s.append(pathSeperator); + } + s.append(r.get(i)); + } + return s.toString(); + }else { + return null; + } + } + + public static VFile2 create(IEaglerFilesystem fs, Object... path) { + return new VFile2(createPath(path), fs); + } + + public static VFile2 create(Supplier fs, Object... path) { + return new VFile2(createPath(path), fs); + } + + public VFile2(Object... path) { + this(createPath(path), primaryFilesystem); + } + + private VFile2(String path, IEaglerFilesystem fs) { + this.path = path; + this.myFilesystem = fs; + } + + private VFile2(String path, Supplier fs) { + this.path = path; + this.myFilesystemProvider = fs; + } + + protected IEaglerFilesystem getFS() { + if(myFilesystem == null) { + if(myFilesystemProvider != null) { + myFilesystem = myFilesystemProvider.get(); + }else { + myFilesystem = primaryFilesystem; + } + if(myFilesystem == null) { + throw new IllegalStateException("The filesystem has not been initialized yet!"); + } + } + return myFilesystem; + } + + public InputStream getInputStream() { + assertNotRelative(); + return new VFileInputStream(getFS().eaglerRead(path)); + } + + public OutputStream getOutputStream() { + assertNotRelative(); + return new VFileOutputStream(this); + } + + public String toString() { + return path; + } + + public boolean isRelative() { + return path == null || path.contains(".."); + } + + public void assertNotRelative() { + if(isRelative()) throw new EaglerFileSystemException("Relative paths are not allowed: " + path); + } + + public boolean canRead() { + return !isRelative() && getFS().eaglerExists(path); + } + + public String getPath() { + return path.equals("unnamed") ? null : path; + } + + public String getName() { + int i = path.lastIndexOf(pathSeperator); + return i == -1 ? path : path.substring(i + 1); + } + + public static String getNameFromPath(String path) { + path = normalizePath(path); + int i = path.lastIndexOf(pathSeperator); + return i == -1 ? path : path.substring(i + 1); + } + + public boolean canWrite() { + return !isRelative(); + } + + public String getParent() { + if(path == null) { + return null; + } + int i = path.lastIndexOf(pathSeperator); + return i == -1 ? ".." : path.substring(0, i); + } + + public int hashCode() { + return path == null ? 0 : path.hashCode(); + } + + public boolean equals(Object o) { + return path != null && o != null && (o instanceof VFile2) && path.equals(((VFile2)o).path); + } + + public boolean exists() { + return !isRelative() && getFS().eaglerExists(path); + } + + public boolean delete() { + return !isRelative() && getFS().eaglerDelete(path); + } + + public boolean renameTo(String p) { + if(!isRelative() && getFS().eaglerMove(path, p)) { + return true; + } + return false; + } + + public boolean renameTo(VFile2 p) { + return renameTo(p.path); + } + + public int length() { + return isRelative() ? -1 : getFS().eaglerSize(path); + } + + public byte[] getAllBytes() { + assertNotRelative(); + if(!exists()) { + return null; + } + ByteBuffer readBuffer = getFS().eaglerRead(path); + byte[] copyBuffer = PlatformRuntime.castNativeByteBuffer(readBuffer); + if(copyBuffer != null) { + return copyBuffer; + } + try { + copyBuffer = new byte[readBuffer.remaining()]; + readBuffer.get(copyBuffer); + return copyBuffer; + }finally { + PlatformRuntime.freeByteBuffer(readBuffer); + } + } + + public String getAllChars() { + assertNotRelative(); + if(!exists()) { + return null; + } + return new String(getAllBytes(), StandardCharsets.UTF_8); + } + + public String[] getAllLines() { + assertNotRelative(); + if(!exists()) { + return null; + } + return EagUtils.linesArray(new String(getAllBytes(), StandardCharsets.UTF_8)); + } + + public void setAllChars(String bytes) { + setAllBytes(bytes.getBytes(StandardCharsets.UTF_8)); + } + + public void setAllBytes(byte[] bytes) { + assertNotRelative(); + ByteBuffer copyBuffer = PlatformRuntime.castPrimitiveByteArray(bytes); + if(copyBuffer != null) { + getFS().eaglerWrite(path, copyBuffer); + return; + } + copyBuffer = PlatformRuntime.allocateByteBuffer(bytes.length); + try { + copyBuffer.put(bytes); + copyBuffer.flip(); + getFS().eaglerWrite(path, copyBuffer); + }finally { + PlatformRuntime.freeByteBuffer(copyBuffer); + } + } + + public void iterateFiles(VFSIterator2 itr, boolean recursive) { + assertNotRelative(); + IEaglerFilesystem fs = getFS(); + fs.eaglerIterate(path, new VFSFilenameIteratorImpl(fs, itr), recursive); + } + + public List listFilenames(boolean recursive) { + List ret = new ArrayList<>(); + getFS().eaglerIterate(path, new VFSListFilenamesIteratorImpl(ret), recursive); + return ret; + } + + public List listFiles(boolean recursive) { + List ret = new ArrayList<>(); + IEaglerFilesystem fs = getFS(); + fs.eaglerIterate(path, new VFSListFilesIteratorImpl(fs, ret), recursive); + return ret; + } + + public static int copyFile(VFile2 src, VFile2 dst) { + src.assertNotRelative(); + dst.assertNotRelative(); + IEaglerFilesystem sfs = src.getFS(); + if(sfs != dst.getFS()) { + throw new UnsupportedOperationException("Cannot copy file between filesystems!"); + } + return sfs.eaglerCopy(src.path, dst.path); + } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFileOutputStream.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFileOutputStream.java index 4333bfd..8be7175 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFileOutputStream.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFileOutputStream.java @@ -3,7 +3,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.vfs2; import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; @@ -41,7 +40,7 @@ class VFileOutputStream extends EaglerOutputStream { copyBuffer.put(buf, 0, count); copyBuffer.flip(); try { - PlatformFilesystem.eaglerWrite(vfsFile.path, copyBuffer); + vfsFile.getFS().eaglerWrite(vfsFile.path, copyBuffer); }catch(Throwable t) { throw new IOException("Could not write stream contents to file!", t); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/json/JSONTypeProvider.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/json/JSONTypeProvider.java index 7a1d10b..3d0dd8b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/json/JSONTypeProvider.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/json/JSONTypeProvider.java @@ -19,7 +19,6 @@ import net.minecraft.client.renderer.block.model.BlockFaceUV; import net.minecraft.client.renderer.block.model.BlockPart; import net.minecraft.client.renderer.block.model.BlockPartFace; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; -import net.minecraft.client.renderer.block.model.ItemOverride; import net.minecraft.client.renderer.block.model.ItemTransformVec3f; import net.minecraft.client.renderer.block.model.ModelBlock; import net.minecraft.client.renderer.block.model.ModelBlockDefinition; @@ -41,45 +40,38 @@ import net.minecraft.world.gen.ChunkProviderSettings; /** * Copyright (c) 2022 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class JSONTypeProvider { - private static final Map, JSONTypeSerializer> serializers = new HashMap(); - private static final Map, JSONTypeDeserializer> deserializers = new HashMap(); - - private static final List parsers = new ArrayList(); + private static final Map,JSONTypeSerializer> serializers = new HashMap<>(); + private static final Map,JSONTypeDeserializer> deserializers = new HashMap<>(); + + private static final List parsers = new ArrayList<>(); public static J serialize(Object object) throws JSONException { - JSONTypeSerializer ser = (JSONTypeSerializer) serializers.get(object.getClass()); - if (ser == null) { - for (Entry, JSONTypeSerializer> etr : serializers.entrySet()) { - if (etr.getKey().isInstance(object)) { - ser = (JSONTypeSerializer) etr.getValue(); + JSONTypeSerializer ser = (JSONTypeSerializer) serializers.get(object.getClass()); + if(ser == null) { + for(Entry,JSONTypeSerializer> etr : serializers.entrySet()) { + if(etr.getKey().isInstance(object)) { + ser = (JSONTypeSerializer)etr.getValue(); break; } } } - if (ser != null) { + if(ser != null) { return ser.serializeToJson(object); - } else { + }else { throw new JSONException("Could not find a serializer for " + object.getClass().getSimpleName()); } } @@ -89,54 +81,53 @@ public class JSONTypeProvider { } public static O deserializeNoCast(Object object, Class clazz) throws JSONException { - JSONTypeDeserializer ser = (JSONTypeDeserializer) deserializers.get(clazz); - if (ser != null) { - return (O) ser.deserializeFromJson(object); - } else { + JSONTypeDeserializer ser = (JSONTypeDeserializer) deserializers.get(clazz); + if(ser != null) { + return (O)ser.deserializeFromJson(object); + }else { throw new JSONException("Could not find a deserializer for " + object.getClass().getSimpleName()); } } - - public static JSONTypeSerializer getSerializer(Class object) { - return (JSONTypeSerializer) serializers.get(object); + + public static JSONTypeSerializer getSerializer(Class object) { + return (JSONTypeSerializer)serializers.get(object); } - - public static JSONTypeDeserializer getDeserializer(Class object) { - return (JSONTypeDeserializer) deserializers.get(object); + + public static JSONTypeDeserializer getDeserializer(Class object) { + return (JSONTypeDeserializer)deserializers.get(object); } - + public static Object parse(Object object) { - for (int i = 0, l = parsers.size(); i < l; ++i) { + for(int i = 0, l = parsers.size(); i < l; ++i) { JSONDataParserImpl parser = parsers.get(i); - if (parser.accepts(object)) { + if(parser.accepts(object)) { return parser.parse(object); } } return object; } - + public static void registerType(Class clazz, Object obj) { boolean valid = false; - if (obj instanceof JSONTypeSerializer) { - serializers.put(clazz, (JSONTypeSerializer) obj); + if(obj instanceof JSONTypeSerializer) { + serializers.put(clazz, (JSONTypeSerializer)obj); valid = true; } - if (obj instanceof JSONTypeDeserializer) { - deserializers.put(clazz, (JSONTypeDeserializer) obj); + if(obj instanceof JSONTypeDeserializer) { + deserializers.put(clazz, (JSONTypeDeserializer)obj); valid = true; } - if (!valid) { - throw new IllegalArgumentException( - "Object " + obj.getClass().getSimpleName() + " is not a JsonSerializer or JsonDeserializer object"); + if(!valid) { + throw new IllegalArgumentException("Object " + obj.getClass().getSimpleName() + " is not a JsonSerializer or JsonDeserializer object"); } } - + public static void registerParser(JSONDataParserImpl obj) { parsers.add(obj); } - + static { - + registerType(IChatComponent.class, new IChatComponent.Serializer()); registerType(ChatStyle.class, new ChatStyle.Serializer()); registerType(ServerStatusResponse.class, new ServerStatusResponse.Serializer()); @@ -152,8 +143,6 @@ public class JSONTypeProvider { registerType(ItemCameraTransforms.class, new ItemCameraTransforms.Deserializer()); registerType(ModelBlockDefinition.class, new ModelBlockDefinition.Deserializer()); registerType(ModelBlockDefinition.Variant.class, new ModelBlockDefinition.Variant.Deserializer()); - registerType(ItemOverride.class, new ItemOverride.Deserializer()); - registerType(SoundList.class, new SoundListSerializer()); registerType(SoundMap.class, new SoundMapDeserializer()); registerType(TextureMetadataSection.class, new TextureMetadataSectionSerializer()); @@ -166,7 +155,7 @@ public class JSONTypeProvider { registerParser(new JSONDataParserString()); registerParser(new JSONDataParserReader()); registerParser(new JSONDataParserStream()); - + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/json/impl/SoundMapDeserializer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/json/impl/SoundMapDeserializer.java index ccbeca4..7e21dec 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/json/impl/SoundMapDeserializer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/json/impl/SoundMapDeserializer.java @@ -30,7 +30,7 @@ public class SoundMapDeserializer implements JSONTypeDeserializer soundsMap = new HashMap(); + Map soundsMap = new HashMap<>(); for(String str : json.keySet()) { soundsMap.put(str, JSONTypeProvider.deserialize(json.getJSONObject(str), SoundList.class)); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/LogManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/LogManager.java index 3cc11e1..2d295d3 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/LogManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/LogManager.java @@ -20,7 +20,7 @@ import java.util.Map; */ public class LogManager { - private static final Map loggerInstances = new HashMap(); + private static final Map loggerInstances = new HashMap<>(); public static final Object logLock = new Object(); public static Level logLevel = Level.DEBUG; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/Logger.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/Logger.java index 137ac39..f734f35 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/Logger.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/log4j/Logger.java @@ -101,7 +101,7 @@ public class Logger { log(Level.FATAL, msg); } - private static final SimpleDateFormat fmt = EagRuntime.fixDateFormat(new SimpleDateFormat("hh:mm:ss+SSS")); + private static final SimpleDateFormat fmt = new SimpleDateFormat("hh:mm:ss+SSS"); private static final Date dateInstance = new Date(); public void log(Level level, String msg) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ChunkUpdateManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ChunkUpdateManager.java index 174c560..579a29d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ChunkUpdateManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ChunkUpdateManager.java @@ -5,6 +5,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.util.LinkedList; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; @@ -34,17 +35,17 @@ public class ChunkUpdateManager { private int chunkUpdatesQueued = 0; private int chunkUpdatesQueuedLast = 0; private long chunkUpdatesTotalLastUpdate = 0l; - - private final List queue = new LinkedList(); + + private final List queue = new LinkedList<>(); public ChunkUpdateManager() { worldVertexUploader = new WorldVertexBufferUploader(); renderCache = new RegionRenderCacheBuilder(); } - + public static class EmptyBlockLayerException extends IllegalStateException { } - + private void runGenerator(ChunkCompileTaskGenerator generator, Entity entity) { generator.setRegionRenderCacheBuilder(renderCache); float f = (float) entity.posX; @@ -59,13 +60,11 @@ public class ChunkUpdateManager { try { r.resortTransparency(f, f1, f2, generator); CompiledChunk ch = generator.getCompiledChunk(); - if (ch.isLayerEmpty(EnumWorldBlockLayer.TRANSLUCENT) - && ch.isLayerEmpty(EnumWorldBlockLayer.REALISTIC_WATER)) { + if(ch.isLayerEmpty(EnumWorldBlockLayer.TRANSLUCENT) && ch.isLayerEmpty(EnumWorldBlockLayer.REALISTIC_WATER)) { throw new EmptyBlockLayerException(); } - } catch (EmptyBlockLayerException ex) { - LOGGER.error("RenderChunk {} tried to update it's TRANSLUCENT layer with no proper initialization", - r.getPosition()); + }catch(EmptyBlockLayerException ex) { + LOGGER.error("RenderChunk {} tried to update it's TRANSLUCENT layer with no proper initialization", r.getPosition()); generator.setStatus(ChunkCompileTaskGenerator.Status.DONE); return; // rip } @@ -87,47 +86,47 @@ public class ChunkUpdateManager { } generator.getRenderChunk().setCompiledChunk(compiledchunk); } else if (chunkcompiletaskgenerator$type == ChunkCompileTaskGenerator.Type.RESORT_TRANSPARENCY) { - if (!compiledchunk.isLayerEmpty(EnumWorldBlockLayer.TRANSLUCENT)) { + if(!compiledchunk.isLayerEmpty(EnumWorldBlockLayer.TRANSLUCENT)) { this.uploadChunk(EnumWorldBlockLayer.TRANSLUCENT, generator.getRegionRenderCacheBuilder() - .getWorldRendererByLayer(EnumWorldBlockLayer.TRANSLUCENT), + .getWorldRendererByLayer(EnumWorldBlockLayer.TRANSLUCENT), generator.getRenderChunk(), compiledchunk); } - if (!compiledchunk.isLayerEmpty(EnumWorldBlockLayer.REALISTIC_WATER)) { + if(!compiledchunk.isLayerEmpty(EnumWorldBlockLayer.REALISTIC_WATER)) { this.uploadChunk(EnumWorldBlockLayer.REALISTIC_WATER, generator.getRegionRenderCacheBuilder() - .getWorldRendererByLayer(EnumWorldBlockLayer.REALISTIC_WATER), + .getWorldRendererByLayer(EnumWorldBlockLayer.REALISTIC_WATER), generator.getRenderChunk(), compiledchunk); } generator.getRenderChunk().setCompiledChunk(compiledchunk); generator.setStatus(ChunkCompileTaskGenerator.Status.DONE); } } - + public boolean updateChunks(long timeout) { Entity entity = Minecraft.getMinecraft().getRenderViewEntity(); if (entity == null) { queue.clear(); chunkUpdatesQueued = 0; return false; - } else { + }else { boolean flag = false; - long millis = System.currentTimeMillis(); - List droppedUpdates = new LinkedList(); - while (!queue.isEmpty()) { + long millis = EagRuntime.steadyTimeMillis(); + List droppedUpdates = new LinkedList<>(); + while(!queue.isEmpty()) { ChunkCompileTaskGenerator generator = queue.remove(0); - - if (!generator.canExecuteYet()) { - if (millis - generator.goddamnFuckingTimeout < 60000l) { + + if(!generator.canExecuteYet()) { + if(millis - generator.goddamnFuckingTimeout < 60000l) { droppedUpdates.add(generator); } continue; } - + runGenerator(generator, entity); flag = true; - + ++chunkUpdatesTotal; - - if (timeout < System.nanoTime()) { + + if(timeout < EagRuntime.nanoTime()) { break; } } @@ -141,11 +140,11 @@ public class ChunkUpdateManager { boolean flag = queue.size() < 100; if (!flag) { chunkcompiletaskgenerator.finish(); - } else { + }else { chunkcompiletaskgenerator.addFinishRunnable(new Runnable() { @Override public void run() { - if (queue.remove(chunkcompiletaskgenerator)) { + if(queue.remove(chunkcompiletaskgenerator)) { ++chunkUpdatesTotal; } } @@ -171,19 +170,19 @@ public class ChunkUpdateManager { } public boolean updateTransparencyLater(RenderChunk chunkRenderer) { - if (isAlreadyQueued(chunkRenderer)) { + if(isAlreadyQueued(chunkRenderer)) { return true; } final ChunkCompileTaskGenerator chunkcompiletaskgenerator = chunkRenderer.makeCompileTaskTransparency(); if (chunkcompiletaskgenerator == null) { return true; } - chunkcompiletaskgenerator.goddamnFuckingTimeout = System.currentTimeMillis(); - if (queue.size() < 100) { + chunkcompiletaskgenerator.goddamnFuckingTimeout = EagRuntime.steadyTimeMillis(); + if(queue.size() < 100) { chunkcompiletaskgenerator.addFinishRunnable(new Runnable() { @Override public void run() { - if (queue.remove(chunkcompiletaskgenerator)) { + if(queue.remove(chunkcompiletaskgenerator)) { ++chunkUpdatesTotal; } } @@ -191,7 +190,7 @@ public class ChunkUpdateManager { queue.add(chunkcompiletaskgenerator); ++chunkUpdatesQueued; return true; - } else { + }else { return false; } } @@ -212,8 +211,8 @@ public class ChunkUpdateManager { } public boolean isAlreadyQueued(RenderChunk update) { - for (int i = 0, l = queue.size(); i < l; ++i) { - if (queue.get(i).getRenderChunk() == update) { + for(int i = 0, l = queue.size(); i < l; ++i) { + if(queue.get(i).getRenderChunk() == update) { return true; } } @@ -221,9 +220,9 @@ public class ChunkUpdateManager { } public String getDebugInfo() { - long millis = System.currentTimeMillis(); - - if (millis - chunkUpdatesTotalLastUpdate > 500l) { + long millis = EagRuntime.steadyTimeMillis(); + + if(millis - chunkUpdatesTotalLastUpdate > 500l) { chunkUpdatesTotalLastUpdate = millis; chunkUpdatesTotalLast = chunkUpdatesTotal; chunkUpdatesTotalImmediateLast = chunkUpdatesTotalImmediate; @@ -231,13 +230,13 @@ public class ChunkUpdateManager { chunkUpdatesTotal = 0; chunkUpdatesQueuedLast = chunkUpdatesQueued; chunkUpdatesQueued -= chunkUpdatesTotalLast; - if (chunkUpdatesQueued < 0) { + if(chunkUpdatesQueued < 0) { chunkUpdatesQueued = 0; } } - + return "Uq: " + (chunkUpdatesTotalLast + chunkUpdatesTotalImmediateLast) + "/" + (chunkUpdatesQueuedLast + chunkUpdatesTotalImmediateLast); } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java index e45d36c..633492d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java @@ -1,355 +1,364 @@ -package net.lax1dude.eaglercraft.v1_8.minecraft; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -import net.lax1dude.eaglercraft.v1_8.ArrayUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.minecraft.client.resources.AbstractResourcePack; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglerFolderResourcePack extends AbstractResourcePack { - - public static final Logger logger = LogManager.getLogger("EaglerFolderResourcePack"); - - public static final String SERVER_RESOURCE_PACKS = "srp"; - public static final String RESOURCE_PACKS = "resourcepacks"; - - private final String prefix; - private final String displayName; - private final Set domains; - private final long timestamp; - - private static boolean isSupported = false; - - public static void setSupported(boolean supported) { - isSupported = supported; - } - - public static boolean isSupported() { - return isSupported; - } - - public EaglerFolderResourcePack(String resourcePackFileIn, String displayName, String prefix, Set domains, long timestamp) { - super(resourcePackFileIn); - this.displayName = displayName; - this.prefix = prefix; - this.domains = domains; - this.timestamp = timestamp; - } - - @Override - public Set getResourceDomains() { - return domains; - } - - @Override - protected InputStream getInputStreamByName(String var1) throws IOException { - return (new VFile2(prefix, this.resourcePackFile, var1)).getInputStream(); - } - - @Override - protected boolean hasResourceName(String var1) { - return (new VFile2(prefix, this.resourcePackFile, var1)).exists(); - } - - public long getTimestamp() { - return timestamp; - } - - public String getDisplayName() { - return displayName; - } - - public static List getFolderResourcePacks(String prefix) { - if(!isSupported) { - return Collections.emptyList(); - } - String str = (new VFile2(prefix, "manifest.json")).getAllChars(); - if(str == null) { - return Collections.emptyList(); - } - try { - JSONArray json = (new JSONObject(str)).getJSONArray("resourcePacks"); - List ret = new ArrayList(json.length()); - for(int i = 0, l = json.length(); i < l; ++i) { - JSONObject jp = json.getJSONObject(i); - String folderName = jp.getString("folder"); - String displayName = jp.optString("name", folderName); - long timestamp = jp.getLong("timestamp"); - Set domains = Sets.newHashSet(); - JSONArray jsonDomains = jp.getJSONArray("domains"); - for(int j = 0, k = jsonDomains.length(); j < k; ++j) { - domains.add(jsonDomains.getString(j)); - } - ret.add(new EaglerFolderResourcePack(folderName, displayName, prefix, domains, timestamp)); - } - return ret; - }catch(JSONException ex) { - logger.error("Failed to load resource pack manifest!"); - logger.error(ex); - return Collections.emptyList(); - } - } - - public static EaglerFolderResourcePack importResourcePack(String name, String prefix, byte[] file) throws IOException { - if(!isSupported) { - return null; - } - logger.info("Importing resource pack: {}", name); - int idx = name.lastIndexOf('.'); - if(idx != -1) { - name = name.substring(0, idx); - } - String folderName = name.replaceAll("[^A-Za-z0-9\\-_ \\(\\)]", "_"); - - final List existingLst = getFolderResourcePacks(RESOURCE_PACKS); - - vigg: for(;;) { - for(int i = 0, l = existingLst.size(); i < l; ++i) { - EaglerFolderResourcePack rp = existingLst.get(i); - if(rp.resourcePackFile.equalsIgnoreCase(folderName)) { - folderName = folderName + "-"; - continue vigg; - } - } - break; - } - - List fileNames = Lists.newArrayList(); - - logger.info("Counting files..."); - ZipInputStream ziss = new ZipInputStream(new EaglerInputStream(file)); - ZipEntry zipEntry; - while ((zipEntry = ziss.getNextEntry()) != null) { - if (!zipEntry.isDirectory()) { - fileNames.add(zipEntry.getName()); - } - } - - int prefixLen = Integer.MAX_VALUE; - for(int i = 0, l = fileNames.size(); i < l; ++i) { - String fn = fileNames.get(i); - if(fn.equals("pack.mcmeta") || fn.endsWith("/pack.mcmeta")) { - int currPrefixLen = fn.length() - 11; - if (prefixLen > currPrefixLen) { - prefixLen = currPrefixLen; - } - } - } - if (prefixLen == Integer.MAX_VALUE) { - prefixLen = 0; - } - - Set domainsList = Sets.newHashSet(); - String fn; - for(int i = 0, l = fileNames.size(); i < l; ++i) { - fn = fileNames.get(i); - if(fn.length() > prefixLen + 7) { - fn = fn.substring(prefixLen + 7); - int j = fn.indexOf('/'); - if(j != -1) { - domainsList.add(fn.substring(0, j)); - } - } - } - - VFile2 dstDir = new VFile2(prefix, folderName); - logger.info("Extracting to: {}", dstDir.getPath()); - - try { - int totalSize = 0; - int totalFiles = 0; - int lastProg = 0; - ziss = new ZipInputStream(new EaglerInputStream(file)); - while ((zipEntry = ziss.getNextEntry()) != null) { - if (!zipEntry.isDirectory()) { - fn = zipEntry.getName(); - if(fn.length() > prefixLen) { - byte[] buffer = new byte[(int)zipEntry.getSize()]; - int i = 0, j; - while(i < buffer.length && (j = ziss.read(buffer, i, buffer.length - i)) != -1) { - i += j; - } - (new VFile2(prefix, folderName, fn.substring(prefixLen))).setAllBytes(buffer); - totalSize += buffer.length; - ++totalFiles; - if(totalSize - lastProg > 25000) { - lastProg = totalSize; - logger.info("Extracted {} files, {} bytes from ZIP file...", totalFiles, totalSize); - } - } - } - } - }catch(IOException ex) { - logger.error("Encountered an error extracting zip file, deleting extracted files..."); - for(int i = 0, l = fileNames.size(); i < l; ++i) { - fn = fileNames.get(i); - if(fn.length() > prefixLen) { - (new VFile2(dstDir, fn.substring(prefixLen))).delete(); - } - } - throw ex; - } - - logger.info("Updating manifest..."); - - VFile2 manifestFile = new VFile2(prefix, "manifest.json"); - String str = manifestFile.getAllChars(); - JSONArray arr = null; - if(str != null) { - try { - arr = (new JSONObject(str)).getJSONArray("resourcePacks"); - }catch(JSONException ex) { - } - } - - if(arr == null) { - arr = new JSONArray(); - } - - JSONObject manifestEntry = new JSONObject(); - manifestEntry.put("folder", folderName); - manifestEntry.put("name", name); - long timestamp = System.currentTimeMillis(); - manifestEntry.put("timestamp", timestamp); - JSONArray domainsListJson = new JSONArray(); - for(String str2 : domainsList) { - domainsListJson.put(str2); - } - manifestEntry.put("domains", domainsListJson); - arr.put(manifestEntry); - - manifestFile.setAllChars((new JSONObject()).put("resourcePacks", arr).toString()); - - logger.info("Done!"); - return new EaglerFolderResourcePack(folderName, name, prefix, domainsList, timestamp); - } - - public static void loadRemoteResourcePack(String url, String hash, Consumer cb, Consumer ast, Runnable loading) { - if (!isSupported || !hash.matches("^[a-f0-9]{40}$")) { - cb.accept(null); - return; - } - final List lst = getFolderResourcePacks(SERVER_RESOURCE_PACKS); - for(int i = 0, l = lst.size(); i < l; ++i) { - EaglerFolderResourcePack rp = lst.get(i); - if(rp.resourcePackFile.equals(hash)) { - cb.accept(rp); - return; - } - } - PlatformRuntime.downloadRemoteURIByteArray(url, arr -> { - ast.accept(() -> { - if (arr == null) { - cb.accept(null); - return; - } - SHA1Digest digest = new SHA1Digest(); - digest.update(arr, 0, arr.length); - byte[] hashOut = new byte[20]; - digest.doFinal(hashOut, 0); - if(!hash.equals(ArrayUtils.hexString(hashOut))) { - logger.error("Downloaded resource pack hash does not equal expected resource pack hash!"); - cb.accept(null); - return; - } - if(lst.size() >= 5) { - lst.sort(Comparator.comparingLong(pack -> pack.timestamp)); - for(int i = 0; i < lst.size() - 5; i++) { - deleteResourcePack(SERVER_RESOURCE_PACKS, lst.get(i).resourcePackFile); - } - } - loading.run(); - try { - cb.accept(importResourcePack(hash, SERVER_RESOURCE_PACKS, arr)); - }catch(IOException ex) { - logger.error("Failed to load resource pack downloaded from server!"); - logger.error(ex); - cb.accept(null); - } - }); - }); - } - - public static void deleteResourcePack(EaglerFolderResourcePack pack) { - deleteResourcePack(pack.prefix, pack.resourcePackFile); - } - - public static void deleteResourcePack(String prefix, String name) { - if (!isSupported) { - return; - } - logger.info("Deleting resource pack: {}/{}", prefix, name); - (new VFile2(prefix, name)).listFiles(true).forEach(VFile2::delete); - VFile2 manifestFile = new VFile2(prefix, "manifest.json"); - String str = manifestFile.getAllChars(); - if(str != null) { - try { - JSONArray json = (new JSONObject(str)).getJSONArray("resourcePacks"); - boolean changed = false; - for(int i = 0, l = json.length(); i < l; ++i) { - if(json.getJSONObject(i).getString("folder").equals(name)) { - json.remove(i); - changed = true; - break; - } - } - if(changed) { - manifestFile.setAllChars((new JSONObject()).put("resourcePacks", json).toString()); - }else { - logger.warn("Failed to remove pack \"{}\" from manifest, it wasn't found in the list for some reason", name); - } - }catch(JSONException ex) { - } - } - } - - public static void deleteOldResourcePacks(String prefix, long maxAge) { - if (!isSupported) { - return; - } - long millis = System.currentTimeMillis(); - List lst = getFolderResourcePacks(prefix); - for(int i = 0, l = lst.size(); i < l; ++i) { - EaglerFolderResourcePack rp = lst.get(i); - if(millis - rp.timestamp > maxAge) { - deleteResourcePack(rp); - } - } - } -} +package net.lax1dude.eaglercraft.v1_8.minecraft; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.client.resources.AbstractResourcePack; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerFolderResourcePack extends AbstractResourcePack { + + public static final Logger logger = LogManager.getLogger("EaglerFolderResourcePack"); + + public static final String SERVER_RESOURCE_PACKS = "srp"; + public static final String RESOURCE_PACKS = "resourcepacks"; + + private final String prefix; + private final String displayName; + private final Set domains; + private final long timestamp; + + private static boolean isSupported = false; + + public static void setSupported(boolean supported) { + isSupported = supported; + } + + public static boolean isSupported() { + return isSupported; + } + + public EaglerFolderResourcePack(String resourcePackFileIn, String displayName, String prefix, Set domains, long timestamp) { + super(resourcePackFileIn); + this.displayName = displayName; + this.prefix = prefix; + this.domains = domains; + this.timestamp = timestamp; + } + + @Override + public Set getResourceDomains() { + return domains; + } + + @Override + protected InputStream getInputStreamByName(String var1) throws IOException { + return (new VFile2(prefix, this.resourcePackFile, var1)).getInputStream(); + } + + @Override + protected boolean hasResourceName(String var1) { + return (new VFile2(prefix, this.resourcePackFile, var1)).exists(); + } + + public long getTimestamp() { + return timestamp; + } + + public String getDisplayName() { + return displayName; + } + + public static List getFolderResourcePacks(String prefix) { + if(!isSupported) { + return Collections.emptyList(); + } + String str = (new VFile2(prefix, "manifest.json")).getAllChars(); + if(str == null) { + return Collections.emptyList(); + } + try { + JSONArray json = (new JSONObject(str)).getJSONArray("resourcePacks"); + List ret = new ArrayList<>(json.length()); + for(int i = 0, l = json.length(); i < l; ++i) { + JSONObject jp = json.getJSONObject(i); + String folderName = jp.getString("folder"); + String displayName = jp.optString("name", folderName); + long timestamp = jp.getLong("timestamp"); + Set domains = Sets.newHashSet(); + JSONArray jsonDomains = jp.getJSONArray("domains"); + for(int j = 0, k = jsonDomains.length(); j < k; ++j) { + domains.add(jsonDomains.getString(j)); + } + ret.add(new EaglerFolderResourcePack(folderName, displayName, prefix, domains, timestamp)); + } + return ret; + }catch(JSONException ex) { + logger.error("Failed to load resource pack manifest!"); + logger.error(ex); + return Collections.emptyList(); + } + } + + public static EaglerFolderResourcePack importResourcePack(String name, String prefix, byte[] file) throws IOException { + if(!isSupported) { + return null; + } + logger.info("Importing resource pack: {}", name); + int idx = name.lastIndexOf('.'); + if(idx != -1) { + name = name.substring(0, idx); + } + String folderName = name.replaceAll("[^A-Za-z0-9\\-_ \\(\\)]", "_"); + + final List existingLst = getFolderResourcePacks(RESOURCE_PACKS); + + vigg: for(;;) { + for(int i = 0, l = existingLst.size(); i < l; ++i) { + EaglerFolderResourcePack rp = existingLst.get(i); + if(rp.resourcePackFile.equalsIgnoreCase(folderName)) { + folderName = folderName + "-"; + continue vigg; + } + } + break; + } + + List fileNames = Lists.newArrayList(); + + logger.info("Counting files..."); + ZipEntry zipEntry; + try(ZipInputStream ziss = new ZipInputStream(new EaglerInputStream(file))) { + while ((zipEntry = ziss.getNextEntry()) != null) { + if (!zipEntry.isDirectory()) { + fileNames.add(zipEntry.getName()); + } + } + } + + int prefixLen = Integer.MAX_VALUE; + for(int i = 0, l = fileNames.size(); i < l; ++i) { + String fn = fileNames.get(i); + if(fn.equals("pack.mcmeta") || fn.endsWith("/pack.mcmeta")) { + int currPrefixLen = fn.length() - 11; + if (prefixLen > currPrefixLen) { + prefixLen = currPrefixLen; + } + } + } + if (prefixLen == Integer.MAX_VALUE) { + prefixLen = 0; + } + + Set domainsList = Sets.newHashSet(); + String fn; + for(int i = 0, l = fileNames.size(); i < l; ++i) { + fn = fileNames.get(i); + if(fn.length() > prefixLen + 7) { + fn = fn.substring(prefixLen + 7); + int j = fn.indexOf('/'); + if(j != -1) { + domainsList.add(fn.substring(0, j)); + } + } + } + + VFile2 dstDir = new VFile2(prefix, folderName); + logger.info("Extracting to: {}", dstDir.getPath()); + + try { + int totalSize = 0; + int totalFiles = 0; + int lastProg = 0; + try(ZipInputStream ziss = new ZipInputStream(new EaglerInputStream(file))) { + int sz; + while ((zipEntry = ziss.getNextEntry()) != null) { + if (!zipEntry.isDirectory()) { + fn = zipEntry.getName(); + if(fn.length() > prefixLen) { + byte[] buffer; + sz = (int)zipEntry.getSize(); + if(sz >= 0) { + buffer = new byte[sz]; + int i = 0, j; + while(i < buffer.length && (j = ziss.read(buffer, i, buffer.length - i)) != -1) { + i += j; + } + }else { + buffer = EaglerInputStream.inputStreamToBytes(ziss); + } + (new VFile2(prefix, folderName, fn.substring(prefixLen))).setAllBytes(buffer); + totalSize += buffer.length; + ++totalFiles; + if(totalSize - lastProg > 25000) { + lastProg = totalSize; + logger.info("Extracted {} files, {} bytes from ZIP file...", totalFiles, totalSize); + } + } + } + } + } + }catch(IOException ex) { + logger.error("Encountered an error extracting zip file, deleting extracted files..."); + for(int i = 0, l = fileNames.size(); i < l; ++i) { + fn = fileNames.get(i); + if(fn.length() > prefixLen) { + (new VFile2(dstDir, fn.substring(prefixLen))).delete(); + } + } + throw ex; + } + + logger.info("Updating manifest..."); + + VFile2 manifestFile = new VFile2(prefix, "manifest.json"); + String str = manifestFile.getAllChars(); + JSONArray arr = null; + if(str != null) { + try { + arr = (new JSONObject(str)).getJSONArray("resourcePacks"); + }catch(JSONException ex) { + } + } + + if(arr == null) { + arr = new JSONArray(); + } + + JSONObject manifestEntry = new JSONObject(); + manifestEntry.put("folder", folderName); + manifestEntry.put("name", name); + long timestamp = System.currentTimeMillis(); + manifestEntry.put("timestamp", timestamp); + JSONArray domainsListJson = new JSONArray(); + for(String str2 : domainsList) { + domainsListJson.put(str2); + } + manifestEntry.put("domains", domainsListJson); + arr.put(manifestEntry); + + manifestFile.setAllChars((new JSONObject()).put("resourcePacks", arr).toString()); + + logger.info("Done!"); + return new EaglerFolderResourcePack(folderName, name, prefix, domainsList, timestamp); + } + + public static void loadRemoteResourcePack(String url, String hash, Consumer cb, Consumer ast, Runnable loading) { + if (!isSupported || !hash.matches("^[a-f0-9]{40}$")) { + cb.accept(null); + return; + } + final List lst = getFolderResourcePacks(SERVER_RESOURCE_PACKS); + for(int i = 0, l = lst.size(); i < l; ++i) { + EaglerFolderResourcePack rp = lst.get(i); + if(rp.resourcePackFile.equals(hash)) { + cb.accept(rp); + return; + } + } + PlatformRuntime.downloadRemoteURIByteArray(url, arr -> { + ast.accept(() -> { + if (arr == null) { + cb.accept(null); + return; + } + SHA1Digest digest = new SHA1Digest(); + digest.update(arr, 0, arr.length); + byte[] hashOut = new byte[20]; + digest.doFinal(hashOut, 0); + if(!hash.equals(ArrayUtils.hexString(hashOut))) { + logger.error("Downloaded resource pack hash does not equal expected resource pack hash!"); + cb.accept(null); + return; + } + if(lst.size() >= 5) { + lst.sort(Comparator.comparingLong(pack -> pack.timestamp)); + for(int i = 0; i < lst.size() - 5; i++) { + deleteResourcePack(SERVER_RESOURCE_PACKS, lst.get(i).resourcePackFile); + } + } + loading.run(); + try { + cb.accept(importResourcePack(hash, SERVER_RESOURCE_PACKS, arr)); + }catch(IOException ex) { + logger.error("Failed to load resource pack downloaded from server!"); + logger.error(ex); + cb.accept(null); + } + }); + }); + } + + public static void deleteResourcePack(EaglerFolderResourcePack pack) { + deleteResourcePack(pack.prefix, pack.resourcePackFile); + } + + public static void deleteResourcePack(String prefix, String name) { + if (!isSupported) { + return; + } + logger.info("Deleting resource pack: {}/{}", prefix, name); + (new VFile2(prefix, name)).listFiles(true).forEach(VFile2::delete); + VFile2 manifestFile = new VFile2(prefix, "manifest.json"); + String str = manifestFile.getAllChars(); + if(str != null) { + try { + JSONArray json = (new JSONObject(str)).getJSONArray("resourcePacks"); + boolean changed = false; + for(int i = 0, l = json.length(); i < l; ++i) { + if(json.getJSONObject(i).getString("folder").equals(name)) { + json.remove(i); + changed = true; + break; + } + } + if(changed) { + manifestFile.setAllChars((new JSONObject()).put("resourcePacks", json).toString()); + }else { + logger.warn("Failed to remove pack \"{}\" from manifest, it wasn't found in the list for some reason", name); + } + }catch(JSONException ex) { + } + } + } + + public static void deleteOldResourcePacks(String prefix, long maxAge) { + if (!isSupported) { + return; + } + long millis = System.currentTimeMillis(); + List lst = getFolderResourcePacks(prefix); + for(int i = 0, l = lst.size(); i < l; ++i) { + EaglerFolderResourcePack rp = lst.get(i); + if(millis - rp.timestamp > maxAge) { + deleteResourcePack(rp); + } + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFontRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFontRenderer.java index 61a5249..a04e7d6 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFontRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFontRenderer.java @@ -1,233 +1,242 @@ -package net.lax1dude.eaglercraft.v1_8.minecraft; - -import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.InstancedFontRenderer; -import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.settings.GameSettings; -import net.minecraft.util.ResourceLocation; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglerFontRenderer extends FontRenderer { - - private final int[] temporaryCodepointArray = new int[6553]; - - public EaglerFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, - boolean unicode) { - super(gameSettingsIn, location, textureManagerIn, unicode); - } - - public int drawString(String text, float x, float y, int color, boolean dropShadow) { - if (text == null || text.length() == 0) { - this.posX = x + (dropShadow ? 1 : 0); - this.posY = y; - } else { - if(this.unicodeFlag || !decodeASCIICodepointsAndValidate(text)) { - return super.drawString(text, x, y, color, dropShadow); - } - this.resetStyles(); - if ((color & 0xFC000000) == 0) { - color |= 0xFF000000; - } - this.red = (float) (color >>> 16 & 255) / 255.0F; - this.blue = (float) (color >>> 8 & 255) / 255.0F; - this.green = (float) (color & 255) / 255.0F; - this.alpha = (float) (color >>> 24 & 255) / 255.0F; - this.posX = x; - this.posY = y; - this.textColor = color; - this.renderStringAtPos0(text, dropShadow); - } - return (int) this.posX; - } - - protected void renderStringAtPos(String parString1, boolean parFlag) { - if(parString1 == null) return; - if(this.unicodeFlag || !decodeASCIICodepointsAndValidate(parString1)) { - super.renderStringAtPos(parString1, parFlag); - }else { - renderStringAtPos0(parString1, false); - } - } - - private void renderStringAtPos0(String parString1, boolean parFlag) { - renderEngine.bindTexture(locationFontTexture); - InstancedFontRenderer.begin(); - - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR); - - boolean hasStrike = false; - - for (int i = 0; i < parString1.length(); ++i) { - char c0 = parString1.charAt(i); - if (c0 == 167 && i + 1 < parString1.length()) { - int i1 = "0123456789abcdefklmnor".indexOf(Character.toLowerCase(parString1.charAt(i + 1))); - if (i1 < 16) { - this.randomStyle = false; - this.boldStyle = false; - this.strikethroughStyle = false; - this.underlineStyle = false; - this.italicStyle = false; - if (i1 < 0 || i1 > 15) { - i1 = 15; - } - int j1 = this.colorCode[i1]; - this.textColor = j1 | (this.textColor & 0xFF000000); - } else if (i1 == 16) { - this.randomStyle = true; - } else if (i1 == 17) { - this.boldStyle = true; - } else if (i1 == 18) { - this.strikethroughStyle = true; - } else if (i1 == 19) { - this.underlineStyle = true; - } else if (i1 == 20) { - this.italicStyle = true; - } else if (i1 == 21) { - this.randomStyle = false; - this.boldStyle = false; - this.strikethroughStyle = false; - this.underlineStyle = false; - this.italicStyle = false; - this.textColor = ((int) (this.alpha * 255.0f) << 24) | ((int) (this.red * 255.0f) << 16) - | ((int) (this.green * 255.0f) << 8) | (int) (this.blue * 255.0f); - } - - ++i; - } else { - int j = temporaryCodepointArray[i]; - - if (this.randomStyle && j != -1) { - int k = this.getCharWidth(c0); - String chars = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000"; - - char c1; - while (true) { - j = this.fontRandom.nextInt(chars.length()); - c1 = chars.charAt(j); - if (k == this.getCharWidth(c1)) { - break; - } - } - - c0 = c1; - } - - float f = this.appendCharToBuffer(j, this.textColor, this.boldStyle, this.italicStyle); - - if (this.strikethroughStyle) { - hasStrike = true; - worldrenderer.pos((double) this.posX, (double) (this.posY + (float) (this.FONT_HEIGHT / 2)), 0.0D) - .endVertex(); - worldrenderer - .pos((double) (this.posX + f), (double) (this.posY + (float) (this.FONT_HEIGHT / 2)), 0.0D) - .endVertex(); - worldrenderer.pos((double) (this.posX + f), - (double) (this.posY + (float) (this.FONT_HEIGHT / 2) - 1.0F), 0.0D).endVertex(); - worldrenderer - .pos((double) this.posX, (double) (this.posY + (float) (this.FONT_HEIGHT / 2) - 1.0F), 0.0D) - .endVertex(); - worldrenderer.putColor4(this.textColor); - } - - if (this.underlineStyle) { - hasStrike = true; - int l = this.underlineStyle ? -1 : 0; - worldrenderer.pos((double) (this.posX + (float) l), - (double) (this.posY + (float) this.FONT_HEIGHT), 0.0D).endVertex(); - worldrenderer.pos((double) (this.posX + f), (double) (this.posY + (float) this.FONT_HEIGHT), 0.0D) - .endVertex(); - worldrenderer - .pos((double) (this.posX + f), (double) (this.posY + (float) this.FONT_HEIGHT - 1.0F), 0.0D) - .endVertex(); - worldrenderer.pos((double) (this.posX + (float) l), - (double) (this.posY + (float) this.FONT_HEIGHT - 1.0F), 0.0D).endVertex(); - worldrenderer.putColor4(this.textColor); - } - - this.posX += (float) ((int) f); - } - } - - float texScale = 0.0625f; - - if(!hasStrike) { - worldrenderer.finishDrawing(); - } - - if(parFlag) { - if(hasStrike) { - GlStateManager.color(0.25f, 0.25f, 0.25f, 1.0f); - GlStateManager.translate(1.0f, 1.0f, 0.0f); - tessellator.draw(); - GlStateManager.translate(-1.0f, -1.0f, 0.0f); - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - InstancedFontRenderer.render(8, 8, texScale, texScale, true); - EaglercraftGPU.renderAgain(); - }else { - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - InstancedFontRenderer.render(8, 8, texScale, texScale, true); - } - }else { - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - if(hasStrike) { - tessellator.draw(); - } - InstancedFontRenderer.render(8, 8, texScale, texScale, false); - } - - if(parFlag) { - this.posX += 1.0f; - } - } - - private float appendCharToBuffer(int parInt1, int color, boolean boldStyle, boolean italicStyle) { - if (parInt1 == 32) { - return 4.0f; - }else { - int i = parInt1 % 16; - int j = parInt1 / 16; - float w = this.charWidth[parInt1]; - if(boldStyle) { - InstancedFontRenderer.appendBoldQuad((int)this.posX, (int)this.posY, i, j, color, italicStyle); - ++w; - }else { - InstancedFontRenderer.appendQuad((int)this.posX, (int)this.posY, i, j, color, italicStyle); - } - return w; - } - } - - private boolean decodeASCIICodepointsAndValidate(String str) { - for(int i = 0, l = str.length(); i < l; ++i) { - int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000\u00a7" - .indexOf(str.charAt(i)); - if(j != -1) { - temporaryCodepointArray[i] = j; - }else { - return false; - } - } - return true; - } -} +package net.lax1dude.eaglercraft.v1_8.minecraft; + +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.InstancedFontRenderer; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerFontRenderer extends FontRenderer { + + private final int[] temporaryCodepointArray = new int[6553]; + + public static FontRenderer createSupportedFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, + TextureManager textureManagerIn, boolean unicode) { + if(EaglercraftGPU.checkInstancingCapable()) { + return new EaglerFontRenderer(gameSettingsIn, location, textureManagerIn, unicode); + }else { + return new FontRenderer(gameSettingsIn, location, textureManagerIn, unicode); + } + } + + public EaglerFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, + boolean unicode) { + super(gameSettingsIn, location, textureManagerIn, unicode); + } + + public int drawString(String text, float x, float y, int color, boolean dropShadow) { + if (text == null || text.length() == 0) { + this.posX = x + (dropShadow ? 1 : 0); + this.posY = y; + } else { + if(this.unicodeFlag || !decodeASCIICodepointsAndValidate(text)) { + return super.drawString(text, x, y, color, dropShadow); + } + this.resetStyles(); + if ((color & 0xFC000000) == 0) { + color |= 0xFF000000; + } + this.red = (float) (color >>> 16 & 255) / 255.0F; + this.blue = (float) (color >>> 8 & 255) / 255.0F; + this.green = (float) (color & 255) / 255.0F; + this.alpha = (float) (color >>> 24 & 255) / 255.0F; + this.posX = x; + this.posY = y; + this.textColor = color; + this.renderStringAtPos0(text, dropShadow); + } + return (int) this.posX; + } + + protected void renderStringAtPos(String parString1, boolean parFlag) { + if(parString1 == null) return; + if(this.unicodeFlag || !decodeASCIICodepointsAndValidate(parString1)) { + super.renderStringAtPos(parString1, parFlag); + }else { + renderStringAtPos0(parString1, false); + } + } + + private void renderStringAtPos0(String parString1, boolean parFlag) { + renderEngine.bindTexture(locationFontTexture); + InstancedFontRenderer.begin(); + + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR); + + boolean hasStrike = false; + + for (int i = 0; i < parString1.length(); ++i) { + char c0 = parString1.charAt(i); + if (c0 == 167 && i + 1 < parString1.length()) { + int i1 = "0123456789abcdefklmnor".indexOf(Character.toLowerCase(parString1.charAt(i + 1))); + if (i1 < 16) { + this.randomStyle = false; + this.boldStyle = false; + this.strikethroughStyle = false; + this.underlineStyle = false; + this.italicStyle = false; + if (i1 < 0 || i1 > 15) { + i1 = 15; + } + int j1 = this.colorCode[i1]; + this.textColor = j1 | (this.textColor & 0xFF000000); + } else if (i1 == 16) { + this.randomStyle = true; + } else if (i1 == 17) { + this.boldStyle = true; + } else if (i1 == 18) { + this.strikethroughStyle = true; + } else if (i1 == 19) { + this.underlineStyle = true; + } else if (i1 == 20) { + this.italicStyle = true; + } else if (i1 == 21) { + this.randomStyle = false; + this.boldStyle = false; + this.strikethroughStyle = false; + this.underlineStyle = false; + this.italicStyle = false; + this.textColor = ((int) (this.alpha * 255.0f) << 24) | ((int) (this.red * 255.0f) << 16) + | ((int) (this.green * 255.0f) << 8) | (int) (this.blue * 255.0f); + } + + ++i; + } else { + int j = temporaryCodepointArray[i]; + if(j > 255) continue; + + if (this.randomStyle && j != -1) { + int k = this.getCharWidth(c0); + char[] chars = FontRenderer.codepointLookup; + + char c1; + while (true) { + j = this.fontRandom.nextInt(chars.length); + c1 = chars[j]; + if (k == this.getCharWidth(c1)) { + break; + } + } + + c0 = c1; + } + + float f = this.appendCharToBuffer(j, this.textColor, this.boldStyle, this.italicStyle); + + if (this.strikethroughStyle) { + hasStrike = true; + worldrenderer.pos((double) this.posX, (double) (this.posY + (float) (this.FONT_HEIGHT / 2)), 0.0D) + .endVertex(); + worldrenderer + .pos((double) (this.posX + f), (double) (this.posY + (float) (this.FONT_HEIGHT / 2)), 0.0D) + .endVertex(); + worldrenderer.pos((double) (this.posX + f), + (double) (this.posY + (float) (this.FONT_HEIGHT / 2) - 1.0F), 0.0D).endVertex(); + worldrenderer + .pos((double) this.posX, (double) (this.posY + (float) (this.FONT_HEIGHT / 2) - 1.0F), 0.0D) + .endVertex(); + worldrenderer.putColor4(this.textColor); + } + + if (this.underlineStyle) { + hasStrike = true; + int l = this.underlineStyle ? -1 : 0; + worldrenderer.pos((double) (this.posX + (float) l), + (double) (this.posY + (float) this.FONT_HEIGHT), 0.0D).endVertex(); + worldrenderer.pos((double) (this.posX + f), (double) (this.posY + (float) this.FONT_HEIGHT), 0.0D) + .endVertex(); + worldrenderer + .pos((double) (this.posX + f), (double) (this.posY + (float) this.FONT_HEIGHT - 1.0F), 0.0D) + .endVertex(); + worldrenderer.pos((double) (this.posX + (float) l), + (double) (this.posY + (float) this.FONT_HEIGHT - 1.0F), 0.0D).endVertex(); + worldrenderer.putColor4(this.textColor); + } + + this.posX += (float) ((int) f); + } + } + + float texScale = 0.0625f; + + if(!hasStrike) { + worldrenderer.finishDrawing(); + } + + if(parFlag) { + if(hasStrike) { + GlStateManager.color(0.25f, 0.25f, 0.25f, 1.0f); + GlStateManager.translate(1.0f, 1.0f, 0.0f); + tessellator.draw(); + GlStateManager.translate(-1.0f, -1.0f, 0.0f); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + InstancedFontRenderer.render(8, 8, texScale, texScale, true); + EaglercraftGPU.renderAgain(); + }else { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + InstancedFontRenderer.render(8, 8, texScale, texScale, true); + } + }else { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + if(hasStrike) { + tessellator.draw(); + } + InstancedFontRenderer.render(8, 8, texScale, texScale, false); + } + + if(parFlag) { + this.posX += 1.0f; + } + } + + private float appendCharToBuffer(int parInt1, int color, boolean boldStyle, boolean italicStyle) { + if (parInt1 == 32) { + return 4.0f; + }else { + int i = parInt1 % 16; + int j = parInt1 / 16; + float w = this.charWidth[parInt1]; + if(boldStyle) { + InstancedFontRenderer.appendBoldQuad((int)this.posX, (int)this.posY, i, j, color, italicStyle); + ++w; + }else { + InstancedFontRenderer.appendQuad((int)this.posX, (int)this.posY, i, j, color, italicStyle); + } + return w; + } + } + + private boolean decodeASCIICodepointsAndValidate(String str) { + for(int i = 0, l = str.length(); i < l; ++i) { + int j = FontMappingHelper.lookupChar(str.charAt(i), true); + if(j != -1) { + temporaryCodepointArray[i] = j; + }else { + return false; + } + } + return true; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java index e6d2bf5..5c6685a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java @@ -1,7 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.minecraft; import java.io.IOException; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; @@ -239,10 +238,10 @@ public class EaglerTextureAtlasSprite { int l = i; this.height = this.width; if (meta.getFrameCount() > 0) { - Iterator iterator = meta.getFrameIndexSet().iterator(); + Iterator iterator = meta.getFrameIndexSet().iterator(); while (iterator.hasNext()) { - int i1 = ((Integer) iterator.next()).intValue(); + int i1 = iterator.next().intValue(); if (i1 >= j1) { throw new RuntimeException("invalid frameindex " + i1); } @@ -253,7 +252,7 @@ public class EaglerTextureAtlasSprite { this.animationMetadata = meta; } else { - ArrayList arraylist = Lists.newArrayList(); + List arraylist = Lists.newArrayList(); for (int l1 = 0; l1 < j1; ++l1) { this.framesTextureData.add(getFrameTextureData(aint, k1, l, l1)); @@ -268,10 +267,10 @@ public class EaglerTextureAtlasSprite { } public void generateMipmaps(int level) { - ArrayList arraylist = Lists.newArrayList(); + List arraylist = Lists.newArrayList(); for (int i = 0; i < this.framesTextureData.size(); ++i) { - final int[][] aint = (int[][]) this.framesTextureData.get(i); + final int[][] aint = this.framesTextureData.get(i); if (aint != null) { try { arraylist.add(TextureUtil.generateMipmapData(level, this.width, aint)); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EnumInputEvent.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EnumInputEvent.java new file mode 100755 index 0000000..03bbbf4 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EnumInputEvent.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.minecraft; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumInputEvent { + CLIPBOARD_COPY, CLIPBOARD_PASTE; +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/FontMappingHelper.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/FontMappingHelper.java new file mode 100755 index 0000000..cfe2dfa --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/FontMappingHelper.java @@ -0,0 +1,525 @@ +package net.lax1dude.eaglercraft.v1_8.minecraft; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class FontMappingHelper { + + public static int lookupChar(char c, boolean incSel) { + switch(c) { + case 167: + return incSel ? 256 : -1; + case 192: + return 0; + case 193: + return 1; + case 194: + return 2; + case 200: + return 3; + case 202: + return 4; + case 203: + return 5; + case 205: + return 6; + case 211: + return 7; + case 212: + return 8; + case 213: + return 9; + case 218: + return 10; + case 223: + return 11; + case 227: + return 12; + case 245: + return 13; + case 287: + return 14; + case 304: + return 15; + case 305: + return 16; + case 338: + return 17; + case 339: + return 18; + case 350: + return 19; + case 351: + return 20; + case 372: + return 21; + case 373: + return 22; + case 382: + return 23; + case 519: + return 24; + case 0: + return 25; + case 32: + return 32; + case 33: + return 33; + case 34: + return 34; + case 35: + return 35; + case 36: + return 36; + case 37: + return 37; + case 38: + return 38; + case 39: + return 39; + case 40: + return 40; + case 41: + return 41; + case 42: + return 42; + case 43: + return 43; + case 44: + return 44; + case 45: + return 45; + case 46: + return 46; + case 47: + return 47; + case 48: + return 48; + case 49: + return 49; + case 50: + return 50; + case 51: + return 51; + case 52: + return 52; + case 53: + return 53; + case 54: + return 54; + case 55: + return 55; + case 56: + return 56; + case 57: + return 57; + case 58: + return 58; + case 59: + return 59; + case 60: + return 60; + case 61: + return 61; + case 62: + return 62; + case 63: + return 63; + case 64: + return 64; + case 65: + return 65; + case 66: + return 66; + case 67: + return 67; + case 68: + return 68; + case 69: + return 69; + case 70: + return 70; + case 71: + return 71; + case 72: + return 72; + case 73: + return 73; + case 74: + return 74; + case 75: + return 75; + case 76: + return 76; + case 77: + return 77; + case 78: + return 78; + case 79: + return 79; + case 80: + return 80; + case 81: + return 81; + case 82: + return 82; + case 83: + return 83; + case 84: + return 84; + case 85: + return 85; + case 86: + return 86; + case 87: + return 87; + case 88: + return 88; + case 89: + return 89; + case 90: + return 90; + case 91: + return 91; + case 92: + return 92; + case 93: + return 93; + case 94: + return 94; + case 95: + return 95; + case 96: + return 96; + case 97: + return 97; + case 98: + return 98; + case 99: + return 99; + case 100: + return 100; + case 101: + return 101; + case 102: + return 102; + case 103: + return 103; + case 104: + return 104; + case 105: + return 105; + case 106: + return 106; + case 107: + return 107; + case 108: + return 108; + case 109: + return 109; + case 110: + return 110; + case 111: + return 111; + case 112: + return 112; + case 113: + return 113; + case 114: + return 114; + case 115: + return 115; + case 116: + return 116; + case 117: + return 117; + case 118: + return 118; + case 119: + return 119; + case 120: + return 120; + case 121: + return 121; + case 122: + return 122; + case 123: + return 123; + case 124: + return 124; + case 125: + return 125; + case 126: + return 126; + case 199: + return 128; + case 252: + return 129; + case 233: + return 130; + case 226: + return 131; + case 228: + return 132; + case 224: + return 133; + case 229: + return 134; + case 231: + return 135; + case 234: + return 136; + case 235: + return 137; + case 232: + return 138; + case 239: + return 139; + case 238: + return 140; + case 236: + return 141; + case 196: + return 142; + case 197: + return 143; + case 201: + return 144; + case 230: + return 145; + case 198: + return 146; + case 244: + return 147; + case 246: + return 148; + case 242: + return 149; + case 251: + return 150; + case 249: + return 151; + case 255: + return 152; + case 214: + return 153; + case 220: + return 154; + case 248: + return 155; + case 163: + return 156; + case 216: + return 157; + case 215: + return 158; + case 402: + return 159; + case 225: + return 160; + case 237: + return 161; + case 243: + return 162; + case 250: + return 163; + case 241: + return 164; + case 209: + return 165; + case 170: + return 166; + case 186: + return 167; + case 191: + return 168; + case 174: + return 169; + case 172: + return 170; + case 189: + return 171; + case 188: + return 172; + case 161: + return 173; + case 171: + return 174; + case 187: + return 175; + case 9617: + return 176; + case 9618: + return 177; + case 9619: + return 178; + case 9474: + return 179; + case 9508: + return 180; + case 9569: + return 181; + case 9570: + return 182; + case 9558: + return 183; + case 9557: + return 184; + case 9571: + return 185; + case 9553: + return 186; + case 9559: + return 187; + case 9565: + return 188; + case 9564: + return 189; + case 9563: + return 190; + case 9488: + return 191; + case 9492: + return 192; + case 9524: + return 193; + case 9516: + return 194; + case 9500: + return 195; + case 9472: + return 196; + case 9532: + return 197; + case 9566: + return 198; + case 9567: + return 199; + case 9562: + return 200; + case 9556: + return 201; + case 9577: + return 202; + case 9574: + return 203; + case 9568: + return 204; + case 9552: + return 205; + case 9580: + return 206; + case 9575: + return 207; + case 9576: + return 208; + case 9572: + return 209; + case 9573: + return 210; + case 9561: + return 211; + case 9560: + return 212; + case 9554: + return 213; + case 9555: + return 214; + case 9579: + return 215; + case 9578: + return 216; + case 9496: + return 217; + case 9484: + return 218; + case 9608: + return 219; + case 9604: + return 220; + case 9612: + return 221; + case 9616: + return 222; + case 9600: + return 223; + case 945: + return 224; + case 946: + return 225; + case 915: + return 226; + case 960: + return 227; + case 931: + return 228; + case 963: + return 229; + case 956: + return 230; + case 964: + return 231; + case 934: + return 232; + case 920: + return 233; + case 937: + return 234; + case 948: + return 235; + case 8734: + return 236; + case 8709: + return 237; + case 8712: + return 238; + case 8745: + return 239; + case 8801: + return 240; + case 177: + return 241; + case 8805: + return 242; + case 8804: + return 243; + case 8992: + return 244; + case 8993: + return 245; + case 247: + return 246; + case 8776: + return 247; + case 176: + return 248; + case 8729: + return 249; + case 183: + return 250; + case 8730: + return 251; + case 8319: + return 252; + case 178: + return 253; + case 9632: + return 254; + default: + return -1; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiButtonWithStupidIcons.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiButtonWithStupidIcons.java new file mode 100755 index 0000000..8135c66 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiButtonWithStupidIcons.java @@ -0,0 +1,132 @@ +package net.lax1dude.eaglercraft.v1_8.minecraft; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiButtonWithStupidIcons extends GuiButton { + + protected ResourceLocation leftIcon; + protected float leftIconAspect; + protected ResourceLocation rightIcon; + protected float rightIconAspect; + + public GuiButtonWithStupidIcons(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText) { + super(buttonId, x, y, widthIn, heightIn, buttonText); + } + + public GuiButtonWithStupidIcons(int buttonId, int x, int y, String buttonText) { + super(buttonId, x, y, buttonText); + } + + public GuiButtonWithStupidIcons(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText, + ResourceLocation leftIcon, float leftIconAspect, ResourceLocation rightIcon, float rightIconAspect) { + super(buttonId, x, y, widthIn, heightIn, buttonText); + this.leftIcon = leftIcon; + this.leftIconAspect = leftIconAspect; + this.rightIcon = rightIcon; + this.rightIconAspect = rightIconAspect; + } + + public GuiButtonWithStupidIcons(int buttonId, int x, int y, String buttonText, ResourceLocation leftIcon, + float leftIconAspect, ResourceLocation rightIcon, float rightIconAspect) { + super(buttonId, x, y, buttonText); + this.leftIcon = leftIcon; + this.leftIconAspect = leftIconAspect; + this.rightIcon = rightIcon; + this.rightIconAspect = rightIconAspect; + } + + public ResourceLocation getLeftIcon() { + return leftIcon; + } + + public ResourceLocation getRightIcon() { + return rightIcon; + } + + public void setLeftIcon(ResourceLocation leftIcon, float aspectRatio) { + this.leftIcon = leftIcon; + this.leftIconAspect = aspectRatio; + } + + public void setRightIcon(ResourceLocation rightIcon, float aspectRatio) { + this.rightIcon = rightIcon; + this.rightIconAspect = aspectRatio; + } + + public void drawButton(Minecraft mc, int mouseX, int mouseY) { + if (this.visible) { + FontRenderer fontrenderer = mc.fontRendererObj; + mc.getTextureManager().bindTexture(buttonTextures); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width + && mouseY < this.yPosition + this.height; + if (this.enabled && this.hovered) { + Mouse.showCursor(EnumCursorType.HAND); + } + int i = this.getHoverState(this.hovered); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, 46 + i * 20, this.width / 2, this.height); + this.drawTexturedModalRect(this.xPosition + this.width / 2, this.yPosition, 200 - this.width / 2, + 46 + i * 20, this.width / 2, this.height); + this.mouseDragged(mc, mouseX, mouseY); + int j = 14737632; + if (!this.enabled) { + j = 10526880; + } else if (this.hovered) { + j = 16777120; + } + + int strWidth = fontrenderer.getStringWidth(displayString); + int strWidthAdj = strWidth - (leftIcon != null ? (int) (16 * leftIconAspect) : 0) + + (rightIcon != null ? (int) (16 * rightIconAspect) : 0); + this.drawString(fontrenderer, this.displayString, this.xPosition + (this.width - strWidthAdj) / 2, + this.yPosition + (this.height - 8) / 2, j); + if(leftIcon != null) { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + mc.getTextureManager().bindTexture(leftIcon); + GlStateManager.pushMatrix(); + GlStateManager.translate(this.xPosition + (this.width - strWidthAdj) / 2 - 3 - 16 * leftIconAspect, this.yPosition + 2, 0.0f); + float f = 16.0f / 256.0f; + GlStateManager.scale(f * leftIconAspect, f, f); + this.drawTexturedModalRect(0, 0, 0, 0, 256, 256); + GlStateManager.popMatrix(); + } + if(rightIcon != null) { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + mc.getTextureManager().bindTexture(rightIcon); + GlStateManager.pushMatrix(); + GlStateManager.translate(this.xPosition + (this.width - strWidthAdj) / 2 + strWidth + 3, this.yPosition + 2, 0.0f); + float f = 16.0f / 256.0f; + GlStateManager.scale(f * rightIconAspect, f, f); + this.drawTexturedModalRect(0, 0, 0, 0, 256, 256); + GlStateManager.popMatrix(); + } + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenGenericErrorMessage.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenGenericErrorMessage.java index f6fb104..6e8fa02 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenGenericErrorMessage.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenGenericErrorMessage.java @@ -1,5 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.minecraft; +import org.apache.commons.lang3.StringUtils; + import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.resources.I18n; @@ -26,8 +28,8 @@ public class GuiScreenGenericErrorMessage extends GuiScreen { private GuiScreen cont; public GuiScreenGenericErrorMessage(String str1, String str2, GuiScreen cont) { - this.str1 = I18n.format(str1); - this.str2 = I18n.format(str2); + this.str1 = StringUtils.isAllEmpty(str1) ? "" : I18n.format(str1); + this.str2 = StringUtils.isAllEmpty(str2) ? "" : I18n.format(str2); this.cont = cont; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenVisualViewport.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenVisualViewport.java new file mode 100755 index 0000000..f24625c --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/GuiScreenVisualViewport.java @@ -0,0 +1,144 @@ +package net.lax1dude.eaglercraft.v1_8.minecraft; + +import net.lax1dude.eaglercraft.v1_8.Display; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenVisualViewport extends GuiScreen { + + protected int offsetX; + protected int offsetY; + + @Override + public final void setWorldAndResolution(Minecraft mc, int width, int height) { + Display.wasVisualViewportResized(); // clear state + offsetX = Display.getVisualViewportX() * width / mc.displayWidth; + offsetY = Display.getVisualViewportY() * height / mc.displayHeight; + setWorldAndResolution0(mc, Display.getVisualViewportW() * width / mc.displayWidth, + Display.getVisualViewportH() * height / mc.displayHeight); + } + + protected void setWorldAndResolution0(Minecraft mc, int width, int height) { + super.setWorldAndResolution(mc, width, height); + } + + @Override + public final void updateScreen() { + if(Display.wasVisualViewportResized()) { + setWorldAndResolution(mc, mc.scaledResolution.getScaledWidth(), mc.scaledResolution.getScaledHeight()); + } + updateScreen0(); + } + + protected void updateScreen0() { + super.updateScreen(); + } + + @Override + public final void drawScreen(int i, int j, float var3) { + i -= offsetX; + j -= offsetY; + GlStateManager.pushMatrix(); + GlStateManager.translate(offsetX, offsetY, 0.0f); + drawScreen0(i, j, var3); + GlStateManager.popMatrix(); + } + + protected void drawScreen0(int i, int j, float var3) { + super.drawScreen(i, j, var3); + } + + @Override + protected final void mouseClicked(int parInt1, int parInt2, int parInt3) { + parInt1 -= offsetX; + parInt2 -= offsetY; + mouseClicked0(parInt1, parInt2, parInt3); + } + + protected void mouseClicked0(int parInt1, int parInt2, int parInt3) { + super.mouseClicked(parInt1, parInt2, parInt3); + } + + @Override + protected final void mouseReleased(int i, int j, int k) { + i -= offsetX; + j -= offsetY; + mouseReleased0(i, j, k); + } + + protected void mouseReleased0(int i, int j, int k) { + super.mouseReleased(i, j, k); + } + + @Override + protected final void mouseClickMove(int var1, int var2, int var3, long var4) { + var1 -= offsetX; + var2 -= offsetY; + mouseClickMove0(var1, var2, var3, var4); + } + + protected void mouseClickMove0(int var1, int var2, int var3, long var4) { + super.mouseClickMove(var1, var2, var3, var4); + } + + @Override + protected final void touchEndMove(int parInt1, int parInt2, int parInt3) { + parInt1 -= offsetX; + parInt2 -= offsetY; + touchEndMove0(parInt1, parInt2, parInt3); + } + + protected void touchEndMove0(int parInt1, int parInt2, int parInt3) { + super.touchEndMove(parInt1, parInt2, parInt3); + } + + @Override + protected final void touchMoved(int parInt1, int parInt2, int parInt3) { + parInt1 -= offsetX; + parInt2 -= offsetY; + touchMoved0(parInt1, parInt2, parInt3); + } + + protected void touchMoved0(int parInt1, int parInt2, int parInt3) { + super.touchMoved(parInt1, parInt2, parInt3); + } + + @Override + protected final void touchStarted(int parInt1, int parInt2, int parInt3) { + parInt1 -= offsetX; + parInt2 -= offsetY; + touchStarted0(parInt1, parInt2, parInt3); + } + + protected void touchStarted0(int parInt1, int parInt2, int parInt3) { + super.touchStarted(parInt1, parInt2, parInt3); + } + + @Override + protected void touchTapped(int parInt1, int parInt2, int parInt3) { + parInt1 -= offsetX; + parInt2 -= offsetY; + touchTapped0(parInt1, parInt2, parInt3); + } + + protected void touchTapped0(int parInt1, int parInt2, int parInt3) { + super.touchTapped(parInt1, parInt2, parInt3); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java index fbf84e6..b20d3fc 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java @@ -54,8 +54,8 @@ public class TextureAnimationCache { for(int i = 0; i < cacheTextures.length; ++i) { cacheTextures[i] = GlStateManager.generateTexture(); GlStateManager.bindTexture(cacheTextures[i]); - EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } @@ -123,6 +123,8 @@ public class TextureAnimationCache { if(cacheTextures == null) { throw new IllegalStateException("Cannot copy from uninitialized TextureAnimationCache"); } + GlStateManager.disableBlend(); + GlStateManager.disableAlpha(); GlStateManager.bindTexture(cacheTextures[level]); TextureCopyUtil.srcSize(width >> level, (height >> level) * frameCount); TextureCopyUtil.blitTextureUsingViewports(0, h * animationFrame, dx, dy, w, h); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/CachedNotifBadgeTexture.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/CachedNotifBadgeTexture.java new file mode 100755 index 0000000..06e107e --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/CachedNotifBadgeTexture.java @@ -0,0 +1,46 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import java.util.List; + +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CachedNotifBadgeTexture { + + public final int glTexture; + public final int scaleFactor; + public final int width; + public final int height; + public final List cursorEvents; + public final IChatComponent rootClickEvent; + public final boolean hasClickEvents; + public final boolean hasHoverEvents; + + protected CachedNotifBadgeTexture(int glTexture, int scaleFactor, int width, int height, + List cursorEvents, IChatComponent rootClickEvent, boolean hasClickEvents, + boolean hasHoverEvents) { + this.glTexture = glTexture; + this.scaleFactor = scaleFactor; + this.width = width; + this.height = height; + this.cursorEvents = cursorEvents; + this.rootClickEvent = rootClickEvent; + this.hasClickEvents = hasClickEvents; + this.hasHoverEvents = hasHoverEvents; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ClickEventZone.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ClickEventZone.java new file mode 100755 index 0000000..e7f7552 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ClickEventZone.java @@ -0,0 +1,41 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClickEventZone { + + public final int posX; + public final int posY; + public final int width; + public final int height; + public final IChatComponent chatComponent; + public final boolean hasHoverEvent; + public final boolean hasClickEvent; + + public ClickEventZone(int posX, int posY, int width, int height, IChatComponent chatComponent, + boolean hasHoverEvent, boolean hasClickEvent) { + this.posX = posX; + this.posY = posY; + this.width = width; + this.height = height; + this.chatComponent = chatComponent; + this.hasHoverEvent = hasHoverEvent; + this.hasClickEvent = hasClickEvent; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiButtonNotifBell.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiButtonNotifBell.java new file mode 100755 index 0000000..db1acf5 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiButtonNotifBell.java @@ -0,0 +1,69 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiButtonNotifBell extends GuiButton { + + private static final ResourceLocation eaglerTextures = new ResourceLocation("eagler:gui/eagler_gui.png"); + + private int unread = 0; + + public GuiButtonNotifBell(int buttonID, int xPos, int yPos) { + super(buttonID, xPos, yPos, 20, 20, ""); + } + + public void setUnread(int num) { + unread = num; + } + + public void drawButton(Minecraft minecraft, int i, int j) { + if (this.visible) { + minecraft.getTextureManager().bindTexture(eaglerTextures); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + boolean flag = i >= this.xPosition && j >= this.yPosition && i < this.xPosition + this.width + && j < this.yPosition + this.height; + int k = 0; + int c = 14737632; + if (flag) { + k += this.height; + c = 16777120; + Mouse.showCursor(EnumCursorType.HAND); + } + + drawTexturedModalRect(xPosition, yPosition, unread > 0 ? 116 : 136, k, width, height); + + if(unread > 0) { + GlStateManager.pushMatrix(); + GlStateManager.translate(xPosition + 15.5f, yPosition + 11.0f, 0.0f); + if(unread >= 10) { + GlStateManager.translate(0.0f, 1.0f, 0.0f); + GlStateManager.scale(0.5f, 0.5f, 0.5f); + }else { + GlStateManager.scale(0.75f, 0.75f, 0.75f); + } + drawCenteredString(minecraft.fontRendererObj, Integer.toString(unread), 0, 0, c); + GlStateManager.popMatrix(); + } + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiScreenNotifications.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiScreenNotifications.java new file mode 100755 index 0000000..ac2a6b0 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiScreenNotifications.java @@ -0,0 +1,172 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import java.io.IOException; +import java.util.List; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenNotifications extends GuiScreen { + + private static final String[] priorityLangKeys = new String[] { + "notifications.priority.low", + "notifications.priority.normal", + "notifications.priority.higher", + "notifications.priority.highest" + }; + + private static final int[] priorityOrder = new int[] { + 0, 3, 2, 1 + }; + + GuiScreen parent; + int selected; + GuiSlotNotifications slots; + GuiButton clearAllButton; + GuiButton priorityButton; + int showPriority = 0; + EnumBadgePriority selectedMaxPriority = EnumBadgePriority.LOW; + int lastUpdate = -1; + + public GuiScreenNotifications(GuiScreen parent) { + this.parent = parent; + } + + public void initGui() { + selected = -1; + buttonList.clear(); + buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 32, 100, 20, I18n.format("gui.done"))); + buttonList.add(clearAllButton = new GuiButton(1, this.width / 2 - 154, this.height - 32, 100, 20, + I18n.format("notifications.clearAll"))); + int i = priorityOrder[showPriority]; + buttonList.add(priorityButton = new GuiButton(2, this.width / 2 - 50, this.height - 32, 100, 20, + I18n.format("notifications.priority", I18n.format(priorityLangKeys[i])))); + selectedMaxPriority = EnumBadgePriority.getByID(i); + slots = new GuiSlotNotifications(this); + lastUpdate = -69420; + updateList(); + updateButtons(); + } + + void updateButtons() { + clearAllButton.enabled = !slots.currentDisplayNotifs.isEmpty(); + } + + void updateList() { + if(mc.thePlayer == null) return; + ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager(); + int verHash = showPriority | (mgr.getNotifListUpdateCount() << 2); + if(verHash != lastUpdate) { + lastUpdate = verHash; + EaglercraftUUID selectedUUID = null; + List lst = slots.currentDisplayNotifs; + int oldSelectedId = selected; + if(oldSelectedId >= 0 && oldSelectedId < lst.size()) { + selectedUUID = lst.get(oldSelectedId).badge.badgeUUID; + } + lst.clear(); + lst.addAll(Collections2.transform(Collections2.filter(mgr.getNotifLongHistory(), new Predicate() { + @Override + public boolean apply(NotificationBadge input) { + return input.priority.priority >= priorityOrder[showPriority]; + } + }), GuiSlotNotifications.NotifBadgeSlot::new)); + selected = -1; + if(selectedUUID != null) { + for(int i = 0, l = lst.size(); i < l; ++i) { + if(selectedUUID.equals(lst.get(i).badge.badgeUUID)) { + selected = i; + break; + } + } + } + if(selected != -1) { + if(oldSelectedId != selected) { + slots.scrollBy((selected - oldSelectedId) * slots.getSlotHeight()); + } + } + updateButtons(); + } + } + + public void updateScreen() { + if(mc.thePlayer == null) { + mc.displayGuiScreen(parent); + return; + } + updateList(); + } + + static Minecraft getMinecraft(GuiScreenNotifications screen) { + return screen.mc; + } + + public void actionPerformed(GuiButton btn) { + switch(btn.id) { + case 0: + mc.displayGuiScreen(parent); + break; + case 1: + if(mc.thePlayer != null) { + ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager(); + mgr.removeAllNotifFromActiveList(mgr.getNotifLongHistory()); + clearAllButton.enabled = false; + } + break; + case 2: + showPriority = (showPriority + 1) & 3; + int i = priorityOrder[showPriority]; + priorityButton.displayString = I18n.format("notifications.priority", I18n.format(priorityLangKeys[i])); + selectedMaxPriority = EnumBadgePriority.getByID(i); + updateList(); + break; + default: + break; + } + } + + public void drawScreen(int par1, int par2, float par3) { + if(mc.thePlayer == null) return; + slots.drawScreen(par1, par2, par3); + this.drawCenteredString(fontRendererObj, I18n.format("notifications.title"), this.width / 2, 16, 16777215); + super.drawScreen(par1, par2, par3); + } + + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + slots.handleMouseInput(); + } + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + slots.handleTouchInput(); + } + + public void onGuiClosed() { + if(mc.thePlayer != null) { + mc.thePlayer.sendQueue.getNotifManager().commitUnreadFlag(); + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiSlotNotifications.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiSlotNotifications.java new file mode 100755 index 0000000..b05fcef --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/GuiSlotNotifications.java @@ -0,0 +1,338 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiSlot; +import net.minecraft.client.gui.GuiUtilRenderComponents; +import net.minecraft.event.ClickEvent; +import net.minecraft.event.HoverEvent; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiSlotNotifications extends GuiSlot { + + private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png"); + private static final ResourceLocation largeNotifBk = new ResourceLocation("eagler:gui/notif_bk_large.png"); + + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm a"); + + final GuiScreenNotifications parent; + final List currentDisplayNotifs; + + int mouseX; + int mouseY; + + protected static class NotifBadgeSlot { + + protected final NotificationBadge badge; + protected final List cursorEvents = new ArrayList<>(); + protected int currentScreenX = -69420; + protected int currentScreenY = -69420; + + protected NotifBadgeSlot(NotificationBadge badge) { + this.badge = badge; + } + + } + + public GuiSlotNotifications(GuiScreenNotifications parent) { + super(GuiScreenNotifications.getMinecraft(parent), parent.width, parent.height, 32, parent.height - 44, 68); + this.parent = parent; + this.currentDisplayNotifs = new ArrayList<>(); + } + + @Override + protected int getSize() { + return currentDisplayNotifs.size(); + } + + @Override + protected void elementClicked(int id, boolean doubleClk, int xx, int yy) { + if(selectedElement != id) return; //workaround for vanilla bs + if(id < currentDisplayNotifs.size()) { + NotifBadgeSlot slot = currentDisplayNotifs.get(id); + if(slot.currentScreenY != -69420) { + int w = getListWidth(); + int localX = xx - slot.currentScreenX; + int localY = yy - slot.currentScreenY; + if(localX >= w - 22 && localX < w - 5 && localY >= 5 && localY < 21) { + slot.badge.removeNotif(); + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + IChatComponent cmp = slot.badge.bodyComponent; + if(cmp != null) { + if(doubleClk) { + if (cmp.getChatStyle().getChatClickEvent() != null + && cmp.getChatStyle().getChatClickEvent().getAction().shouldAllowInChat()) { + if(parent.handleComponentClick(cmp)) { + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + } + }else { + if(parent.selected != id) { + parent.selected = id; + }else { + List cursorEvents = slot.cursorEvents; + if(cursorEvents != null && !cursorEvents.isEmpty()) { + for(int j = 0, m = cursorEvents.size(); j < m; ++j) { + ClickEventZone evt = cursorEvents.get(j); + if(evt.hasClickEvent) { + int offsetPosX = slot.currentScreenX + evt.posX; + int offsetPosY = slot.currentScreenY + evt.posY; + if(xx >= offsetPosX && yy >= offsetPosY && xx < offsetPosX + evt.width && yy < offsetPosY + evt.height) { + if(parent.handleComponentClick(evt.chatComponent)) { + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + } + } + } + } + } + } + } + } + } + } + + @Override + protected boolean isSelected(int var1) { + return var1 == parent.selected; + } + + @Override + protected void drawBackground() { + parent.drawBackground(0); + } + + @Override + protected void drawSlot(int id, int xx, int yy, int width, int height, int ii) { + if(id < currentDisplayNotifs.size()) { + NotifBadgeSlot slot = currentDisplayNotifs.get(id); + slot.currentScreenX = xx; + slot.currentScreenY = yy; + NotificationBadge bd = slot.badge; + if(yy + 32 > this.top && yy + 32 < this.bottom) { + bd.markRead(); + } + GlStateManager.pushMatrix(); + GlStateManager.translate(xx, yy, 0.0f); + mc.getTextureManager().bindTexture(largeNotifBk); + int badgeWidth = getListWidth() - 4; + int badgeHeight = getSlotHeight() - 4; + float r = ((bd.backgroundColor >> 16) & 0xFF) * 0.00392156f; + float g = ((bd.backgroundColor >> 8) & 0xFF) * 0.00392156f; + float b = (bd.backgroundColor & 0xFF) * 0.00392156f; + if(parent.selected != id) { + r *= 0.85f; + g *= 0.85f; + b *= 0.85f; + } + GlStateManager.color(r, g, b, 1.0f); + parent.drawTexturedModalRect(0, 0, 0, bd.unreadFlagRender ? 64 : 0, badgeWidth - 32, 64); + parent.drawTexturedModalRect(badgeWidth - 32, 0, 224, bd.unreadFlagRender ? 64 : 0, 32, 64); + mc.getTextureManager().bindTexture(eaglerGui); + if(bd.priority == EnumBadgePriority.LOW) { + parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 192, 176, 16, 16); + } + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + + switch(bd.priority) { + default: + break; + case NORMAL: + parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 208, 176, 16, 16); + break; + case HIGHER: + parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 224, 176, 16, 16); + break; + case HIGHEST: + parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 240, 176, 16, 16); + break; + } + + int bodyYOffset = 16; + + int leftPadding = 6; + int rightPadding = 26; + + int mainIconSW = 32; + boolean mainIconEn = bd.mainIcon != null && bd.mainIcon.isValid(); + if(mainIconEn) { + int iw = bd.mainIcon.texture.getWidth(); + int ih = bd.mainIcon.texture.getHeight(); + float iaspect = (float)iw / (float)ih; + mainIconSW = (int)(32 * iaspect); + leftPadding += Math.min(mainIconSW, 64) + 3; + } + + int textZoneWidth = badgeWidth - leftPadding - rightPadding; + + if(mainIconEn) { + mc.getTextureManager().bindTexture(bd.mainIcon.resource); + ServerNotificationRenderer.drawTexturedRect(6, bodyYOffset, mainIconSW, 32); + } + + boolean titleIconEn = bd.titleIcon != null && bd.titleIcon.isValid(); + if(titleIconEn) { + mc.getTextureManager().bindTexture(bd.titleIcon.resource); + ServerNotificationRenderer.drawTexturedRect(6, 5, 8, 8); + } + + String titleText = ""; + IChatComponent titleComponent = bd.getTitleProfanityFilter(); + if(titleComponent != null) { + titleText = titleComponent.getFormattedText(); + } + + titleText += EnumChatFormatting.GRAY + (titleText.length() > 0 ? " @ " : "@ ") + + (bd.unreadFlagRender ? EnumChatFormatting.YELLOW : EnumChatFormatting.GRAY) + + formatAge(bd.serverTimestamp); + + GlStateManager.pushMatrix(); + GlStateManager.translate(6 + (titleIconEn ? 10 : 0), 6, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + mc.fontRendererObj.drawStringWithShadow(titleText, 0, 0, bd.titleTxtColor); + GlStateManager.popMatrix(); + + String sourceText = null; + IChatComponent sourceComponent = bd.getSourceProfanityFilter(); + if(sourceComponent != null) { + sourceText = sourceComponent.getFormattedText(); + if(sourceText.length() == 0) { + sourceText = null; + } + } + + List bodyLines = null; + float bodyFontSize = (sourceText != null || titleIconEn) ? 0.75f : 1.0f; + IChatComponent bodyComponent = bd.getBodyProfanityFilter(); + if(bodyComponent != null) { + bodyLines = GuiUtilRenderComponents.func_178908_a(bodyComponent, (int) (textZoneWidth / bodyFontSize), + mc.fontRendererObj, true, true); + + int maxHeight = badgeHeight - (sourceText != null ? 32 : 22); + int maxLines = MathHelper.floor_float(maxHeight / (9 * bodyFontSize)); + if(bodyLines.size() > maxLines) { + bodyLines = bodyLines.subList(0, maxLines); + IChatComponent cmp = bodyLines.get(maxLines - 1); + List siblings = cmp.getSiblings(); + IChatComponent dots = new ChatComponentText("..."); + if(siblings != null && siblings.size() > 0) { + dots.setChatStyle(siblings.get(siblings.size() - 1).getChatStyle()); + } + cmp.appendSibling(dots); + } + } + + slot.cursorEvents.clear(); + if(bodyLines != null && !bodyLines.isEmpty()) { + GlStateManager.pushMatrix(); + GlStateManager.translate(leftPadding, bodyYOffset, 0.0f); + int l = bodyLines.size(); + GlStateManager.scale(bodyFontSize, bodyFontSize, bodyFontSize); + IChatComponent toolTip = null; + for(int i = 0; i < l; ++i) { + int startXLocal = 0; + int startXReal = leftPadding; + for(IChatComponent comp : bodyLines.get(i)) { + int w = mc.fontRendererObj.drawStringWithShadow( + comp.getChatStyle().getFormattingCode() + comp.getUnformattedTextForChat(), startXLocal, + i * 9, bd.bodyTxtColor) - startXLocal; + ClickEvent clickEvent = comp.getChatStyle().getChatClickEvent(); + HoverEvent hoverEvent = toolTip == null ? comp.getChatStyle().getChatHoverEvent() : null; + if(clickEvent != null && !clickEvent.getAction().shouldAllowInChat()) { + clickEvent = null; + } + if(hoverEvent != null && !hoverEvent.getAction().shouldAllowInChat()) { + hoverEvent = null; + } + if(clickEvent != null) { + slot.cursorEvents.add(new ClickEventZone(startXReal + (int) (startXLocal * bodyFontSize), + bodyYOffset + (int) (i * 9 * bodyFontSize), (int) (w * bodyFontSize), + (int) (9 * bodyFontSize), comp, clickEvent != null, hoverEvent != null)); + } + if(hoverEvent != null) { + int px = xx + startXReal + (int) (startXLocal * bodyFontSize); + int py = yy + bodyYOffset + (int) (i * 9 * bodyFontSize); + if (mouseX >= px && mouseX < px + (int) (w * bodyFontSize) && mouseY >= py + && mouseY < py + (int) (9 * bodyFontSize)) { + toolTip = comp; + } + } + startXLocal += w; + } + } + GlStateManager.popMatrix(); + if(toolTip != null) { + parent.handleComponentHover(toolTip, mouseX - xx, mouseY - yy); + } + } + + if(sourceText != null) { + GlStateManager.pushMatrix(); + GlStateManager.translate(badgeWidth - 21, badgeHeight - 5, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + mc.fontRendererObj.drawStringWithShadow(sourceText, -mc.fontRendererObj.getStringWidth(sourceText) - 4, -10, bd.sourceTxtColor); + GlStateManager.popMatrix(); + } + + GlStateManager.popMatrix(); + } + } + + private String formatAge(long serverTimestamp) { + long cur = System.currentTimeMillis(); + long daysAgo = Math.round((cur - serverTimestamp) / 86400000.0); + String ret = dateFormat.format(new Date(serverTimestamp)); + if(daysAgo > 0l) { + ret += " (" + daysAgo + (daysAgo == 1l ? " day" : " days") + " ago)"; + }else if(daysAgo < 0l) { + ret += " (in " + -daysAgo + (daysAgo == -1l ? " day" : " days") + ")"; + } + return ret; + } + + @Override + public int getListWidth() { + return 224; + } + + @Override + public void drawScreen(int mouseXIn, int mouseYIn, float parFloat1) { + mouseX = mouseXIn; + mouseY = mouseYIn; + for(int i = 0, l = currentDisplayNotifs.size(); i < l; ++i) { + NotifBadgeSlot slot = currentDisplayNotifs.get(i); + slot.currentScreenX = -69420; + slot.currentScreenY = -69420; + } + super.drawScreen(mouseXIn, mouseYIn, parFloat1); + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/NotificationBadge.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/NotificationBadge.java new file mode 100755 index 0000000..02598a8 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/NotificationBadge.java @@ -0,0 +1,171 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority; +import net.minecraft.client.Minecraft; +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class NotificationBadge { + + public final ServerNotificationManager mgr; + public final EaglercraftUUID badgeUUID; + public final IChatComponent bodyComponent; + protected IChatComponent bodyComponentProfanityFilter; + public final IChatComponent titleComponent; + protected IChatComponent titleComponentProfanityFilter; + public final IChatComponent sourceComponent; + protected IChatComponent sourceComponentProfanityFilter; + public final long clientTimestamp; + public final long serverTimestamp; + public final boolean silent; + public final EnumBadgePriority priority; + public final NotificationIcon mainIcon; + public final NotificationIcon titleIcon; + public final int hideAfterSec; + public final int expireAfterSec; + public final int backgroundColor; + public final int bodyTxtColor; + public final int titleTxtColor; + public final int sourceTxtColor; + + protected CachedNotifBadgeTexture currentCacheGLTexture = null; + protected int currentCacheScaleFac = -1; + protected boolean currentCacheXButton = false; + protected boolean currentCacheProfanityFilter = false; + protected long hideAtMillis = -1l; + protected boolean unreadFlag = true; + protected boolean unreadFlagRender = true; + + protected NotificationBadge(ServerNotificationManager mgr, EaglercraftUUID badgeUUID, IChatComponent bodyComponent, + IChatComponent titleComponent, IChatComponent sourceComponent, long clientTimestamp, long serverTimestamp, + boolean silent, EnumBadgePriority priority, NotificationIcon mainIcon, NotificationIcon titleIcon, + int hideAfterSec, int expireAfterSec, int backgroundColor, int bodyTxtColor, int titleTxtColor, + int sourceTxtColor) { + this.mgr = mgr; + this.badgeUUID = badgeUUID; + this.bodyComponent = bodyComponent; + this.titleComponent = titleComponent; + this.sourceComponent = sourceComponent; + this.clientTimestamp = clientTimestamp; + this.serverTimestamp = serverTimestamp; + this.silent = silent; + this.priority = priority; + this.mainIcon = mainIcon; + this.titleIcon = titleIcon; + this.hideAfterSec = hideAfterSec; + this.expireAfterSec = expireAfterSec; + this.backgroundColor = backgroundColor; + this.bodyTxtColor = bodyTxtColor; + this.titleTxtColor = titleTxtColor; + this.sourceTxtColor = sourceTxtColor; + } + + protected void incrIconRefcounts() { + if(mainIcon != null) { + mainIcon.retain(); + } + if(titleIcon != null) { + titleIcon.retain(); + } + } + + protected void decrIconRefcounts() { + deleteGLTexture(); + if(mainIcon != null) { + mainIcon.release(); + } + if(titleIcon != null) { + titleIcon.release(); + } + } + + protected CachedNotifBadgeTexture getGLTexture(ServerNotificationRenderer renderer, int scaleFactor, boolean showXButton) { + boolean profanityFilter = Minecraft.getMinecraft().isEnableProfanityFilter(); + if(currentCacheGLTexture == null || currentCacheScaleFac != scaleFactor || currentCacheXButton != showXButton || currentCacheProfanityFilter != profanityFilter) { + deleteGLTexture(); + currentCacheGLTexture = renderer.renderBadge(this, scaleFactor, showXButton); + currentCacheScaleFac = scaleFactor; + currentCacheXButton = showXButton; + currentCacheProfanityFilter = profanityFilter; + } + return currentCacheGLTexture; + } + + protected void deleteGLTexture() { + if(currentCacheGLTexture != null) { + GlStateManager.deleteTexture(currentCacheGLTexture.glTexture); + currentCacheGLTexture = null; + } + } + + public void hideNotif() { + if(hideAtMillis == -1l) { + markRead(); + unreadFlagRender = false; + hideAtMillis = EagRuntime.steadyTimeMillis(); + } + } + + public void removeNotif() { + mgr.removeNotifFromActiveList(badgeUUID); + } + + public void markRead() { + if(unreadFlag) { + unreadFlag = false; + --mgr.unreadCounter; + } + } + + public IChatComponent getBodyProfanityFilter() { + if(Minecraft.getMinecraft().isEnableProfanityFilter()) { + if(bodyComponentProfanityFilter == null && bodyComponent != null) { + bodyComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(bodyComponent); + } + return bodyComponentProfanityFilter; + }else { + return bodyComponent; + } + } + + public IChatComponent getTitleProfanityFilter() { + if(Minecraft.getMinecraft().isEnableProfanityFilter()) { + if(titleComponentProfanityFilter == null && titleComponent != null) { + titleComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(titleComponent); + } + return titleComponentProfanityFilter; + }else { + return titleComponent; + } + } + + public IChatComponent getSourceProfanityFilter() { + if(Minecraft.getMinecraft().isEnableProfanityFilter()) { + if(sourceComponentProfanityFilter == null && sourceComponent != null) { + sourceComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(sourceComponent); + } + return sourceComponentProfanityFilter; + }else { + return sourceComponent; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/NotificationIcon.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/NotificationIcon.java new file mode 100755 index 0000000..772e186 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/NotificationIcon.java @@ -0,0 +1,51 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class NotificationIcon { + + private static int notifIconTmpId = 0; + + protected int refCount = 0; + protected boolean serverRegistered = true; + + public final EaglercraftUUID iconUUID; + public final EaglerSkinTexture texture; + public final ResourceLocation resource; + + protected NotificationIcon(EaglercraftUUID iconUUID, EaglerSkinTexture texture) { + this.iconUUID = iconUUID; + this.texture = texture; + this.resource = new ResourceLocation("eagler:gui/server/notifs/tex_" + notifIconTmpId++); + } + + public void retain() { + ++refCount; + } + + public void release() { + --refCount; + } + + public boolean isValid() { + return serverRegistered || refCount > 0; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java new file mode 100755 index 0000000..eb7adc4 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java @@ -0,0 +1,277 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeHideV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsRegisterV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsReleaseV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerNotificationManager { + + private static final Logger logger = LogManager.getLogger("ServerNotificationManager"); + + private final Map activeIcons = new HashMap<>(); + private final Map activeNotifications = new HashMap<>(); + private List sortedNotifList = new ArrayList<>(0); + private List sortedDisplayNotifList = new ArrayList<>(0); + private int updateCounter = 0; + private long lastCleanup = EagRuntime.steadyTimeMillis(); + private final TextureManager textureMgr; + protected int unreadCounter = 0; + + public ServerNotificationManager() { + this.textureMgr = Minecraft.getMinecraft().getTextureManager(); + } + + public void processPacketAddIcons(SPacketNotifIconsRegisterV4EAG packet) { + for(SPacketNotifIconsRegisterV4EAG.CreateIcon icn : packet.iconsToCreate) { + if(icn.uuidMost == 0 && icn.uuidLeast == 0) { + logger.error("Skipping notification icon with UUID 0!"); + continue; + } + EaglercraftUUID uuid = new EaglercraftUUID(icn.uuidMost, icn.uuidLeast); + PacketImageData imageData = icn.imageData; + NotificationIcon existing = activeIcons.get(uuid); + if(existing != null) { + if (existing.texture.getWidth() != imageData.width + || existing.texture.getHeight() != imageData.height) { + logger.error("Error: server tried to change the dimensions of icon {}!", uuid); + }else if(!Arrays.equals(existing.texture.getData(), imageData.rgba)) { + existing.texture.copyPixelsIn(ImageData.swapRB(imageData.rgba)); + } + existing.serverRegistered = true; + continue; + } + NotificationIcon newIcon = new NotificationIcon(uuid, + new EaglerSkinTexture(ImageData.swapRB(imageData.rgba), imageData.width, imageData.height)); + textureMgr.loadTexture(newIcon.resource, newIcon.texture); + activeIcons.put(uuid, newIcon); + } + } + + public void processPacketRemIcons(SPacketNotifIconsReleaseV4EAG packet) { + for(SPacketNotifIconsReleaseV4EAG.DestroyIcon icn : packet.iconsToDestroy) { + NotificationIcon existing = activeIcons.get(new EaglercraftUUID(icn.uuidMost, icn.uuidLeast)); + if(existing != null) { + existing.serverRegistered = false; + } + } + } + + public void processPacketShowBadge(SPacketNotifBadgeShowV4EAG packet) { + EaglercraftUUID newUuid = new EaglercraftUUID(packet.badgeUUIDMost, packet.badgeUUIDLeast); + NotificationBadge existing = activeNotifications.get(newUuid); + if(existing != null) { + logger.error("Duplicate notification UUID {}, all notifications should have unique UUIDs!", newUuid); + return; + } + NotificationBadge newBadge = new NotificationBadge(this, newUuid, + !StringUtils.isAllBlank(packet.bodyComponent) ? IChatComponent.Serializer.jsonToComponent(packet.bodyComponent) : null, + !StringUtils.isAllBlank(packet.titleComponent) ? IChatComponent.Serializer.jsonToComponent(packet.titleComponent) : null, + !StringUtils.isAllBlank(packet.sourceComponent) ? IChatComponent.Serializer.jsonToComponent(packet.sourceComponent) : null, + EagRuntime.steadyTimeMillis(), packet.originalTimestampSec * 1000l, packet.silent, packet.priority, + getIcon(packet.mainIconUUIDMost, packet.mainIconUUIDLeast), + getIcon(packet.titleIconUUIDMost, packet.titleIconUUIDLeast), packet.hideAfterSec, packet.expireAfterSec, + packet.backgroundColor, packet.bodyTxtColor, packet.titleTxtColor, packet.sourceTxtColor); + ++unreadCounter; + addNotifToActiveList(newBadge); + } + + private NotificationIcon getIcon(long uuidMost, long uuidLeast) { + if(uuidMost == 0l && uuidLeast == 0l) { + return null; + } + return activeIcons.get(new EaglercraftUUID(uuidMost, uuidLeast)); + } + + public void processPacketHideBadge(SPacketNotifBadgeHideV4EAG packet) { + removeNotifFromActiveList(new EaglercraftUUID(packet.badgeUUIDLeast, packet.badgeUUIDMost)); + } + + public int getNotifListUpdateCount() { + return updateCounter; + } + + public List getNotifBadgesToDisplay() { + return sortedDisplayNotifList; + } + + public List getNotifLongHistory() { + return sortedNotifList; + } + + protected void addNotifToActiveList(NotificationBadge badge) { + NotificationBadge exists = activeNotifications.put(badge.badgeUUID, badge); + if(exists != null) { + exists.decrIconRefcounts(); + } + badge.incrIconRefcounts(); + resortLists(); + } + + protected void removeNotifFromActiveList(EaglercraftUUID badge) { + NotificationBadge exists = activeNotifications.remove(badge); + if(exists != null) { + exists.decrIconRefcounts(); + resortLists(); + } + } + + protected void removeAllNotifFromActiveList(Collection badges) { + boolean resort = false; + for(NotificationBadge badge : badges) { + NotificationBadge exists = activeNotifications.remove(badge.badgeUUID); + if(exists != null) { + exists.decrIconRefcounts(); + resort = true; + } + } + if(resort) { + resortLists(); + } + } + + protected static final Comparator clientAgeComparator = (a, b) -> { + return (int)(b.clientTimestamp - a.clientTimestamp); + }; + + private void resortLists() { + updateCounter++; + int ll = activeNotifications.size(); + if(!sortedNotifList.isEmpty()) sortedNotifList = new ArrayList<>(ll); + if(!sortedDisplayNotifList.isEmpty()) sortedDisplayNotifList = new ArrayList<>(Math.min(ll, 4)); + if(ll > 0) { + sortedNotifList.addAll(activeNotifications.values()); + Collections.sort(sortedNotifList, clientAgeComparator); + long millis = EagRuntime.steadyTimeMillis(); + for(int i = 0, l = sortedNotifList.size(); i < l; ++i) { + NotificationBadge bd = sortedNotifList.get(i); + if(millis - bd.clientTimestamp < (long)(bd.hideAfterSec * 1000)) { + sortedDisplayNotifList.add(bd); + }else { + bd.deleteGLTexture(); + } + } + } + } + + public void runTick() { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastCleanup > 2500l) { + lastCleanup = millis; + int len = sortedNotifList.size(); + if(len > 128) { + removeAllNotifFromActiveList(new ArrayList(sortedNotifList.subList(128, len))); + } + Iterator itr = activeIcons.values().iterator(); + while(itr.hasNext()) { + NotificationIcon icn = itr.next(); + if(!icn.isValid()) { + itr.remove(); + textureMgr.deleteTexture(icn.resource); + } + } + if(!sortedDisplayNotifList.isEmpty()) { + Iterator itr2 = sortedDisplayNotifList.iterator(); + while(itr2.hasNext()) { + NotificationBadge bd = itr2.next(); + if(bd.hideAtMillis != -1l) { + if(millis - bd.hideAtMillis > 500l) { + bd.deleteGLTexture(); + itr2.remove(); + } + }else { + long age = millis - bd.clientTimestamp; + if(age > (long)(bd.hideAfterSec * 1000) || age > (long)(bd.expireAfterSec * 1000)) { + bd.deleteGLTexture(); + itr2.remove(); + } + } + } + } + if(!activeNotifications.isEmpty()) { + Iterator itr3 = activeNotifications.values().iterator(); + List toDelete = null; + while(itr3.hasNext()) { + NotificationBadge bd = itr3.next(); + long age = millis - bd.clientTimestamp; + if(age > (long)(bd.expireAfterSec * 1000)) { + if(toDelete == null) { + toDelete = new ArrayList<>(); + } + toDelete.add(bd); + } + } + if(toDelete != null) { + removeAllNotifFromActiveList(toDelete); + } + } + } + } + + public int getUnread() { + if(unreadCounter < 0) unreadCounter = 0; + return unreadCounter; + } + + public void commitUnreadFlag() { + for(NotificationBadge badge : activeNotifications.values()) { + badge.unreadFlagRender = badge.unreadFlag; + } + } + + public void markRead() { + for(NotificationBadge badge : activeNotifications.values()) { + badge.unreadFlag = false; + badge.unreadFlagRender = false; + } + unreadCounter = 0; + } + + public void destroy() { + for(NotificationIcon icn : activeIcons.values()) { + textureMgr.deleteTexture(icn.resource); + } + activeIcons.clear(); + activeNotifications.clear(); + sortedNotifList = null; + sortedDisplayNotifList = null; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationRenderer.java new file mode 100755 index 0000000..cdf13cd --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationRenderer.java @@ -0,0 +1,539 @@ +package net.lax1dude.eaglercraft.v1_8.notifications; + +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.VertexFormat; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiUtilRenderComponents; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.event.ClickEvent; +import net.minecraft.event.HoverEvent; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ExtGLEnums.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerNotificationRenderer { + + protected static final Logger logger = LogManager.getLogger("ServerNotificationRenderer"); + + protected Minecraft mc; + protected int width; + protected int height; + protected int scaleFactor; + + protected IFramebufferGL rendererFramebuffer; + + protected static final int BADGE_WIDTH = 160; + protected static final int BADGE_HEIGHT = 64; + + private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png"); + + public ServerNotificationRenderer() { + + } + + public void init() { + destroy(); + rendererFramebuffer = _wglCreateFramebuffer(); + } + + public void setResolution(Minecraft mc, int w, int h, int scaleFactor) { + this.mc = mc; + this.width = w; + this.height = h; + this.scaleFactor = scaleFactor; + } + + public boolean handleClicked(GuiScreen currentScreen, int posX, int posY) { + if(mc.thePlayer == null) return false; + ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager(); + List lst = mgr.getNotifBadgesToDisplay(); + if(!lst.isEmpty()) { + int baseOffset = mc.guiAchievement.getHeight(); + boolean showX = (currentScreen instanceof GuiChat); + if(showX) { + baseOffset += 25; // exit button in chat screen; + } + long millis = EagRuntime.steadyTimeMillis(); + for(int i = 0, l = lst.size(); i < l; ++i) { + NotificationBadge badge = lst.get(i); + CachedNotifBadgeTexture tex = badge.currentCacheGLTexture; + if(tex != null) { + int baseX = width - tex.width; + float texHeight = tex.height; + float timeRemainingSec; + long age = millis - badge.clientTimestamp; + if(badge.hideAtMillis != -1l) { + timeRemainingSec = (float)((double)(500l - (millis - badge.hideAtMillis)) * 0.001); + }else { + timeRemainingSec = (float)((double)((long)badge.hideAfterSec * 1000l - age) * 0.001); + } + timeRemainingSec = Math.min((float)(age * 0.001) + 0.001f, timeRemainingSec); + float f = MathHelper.clamp_float(timeRemainingSec * 3.0F, 0.0F, 1.0F); + f *= f; + texHeight *= f; + if(badge.hideAtMillis == -1l) { + if(posX >= baseX && posX < width && posY >= baseOffset && posY < baseOffset + texHeight) { + if(showX) { + int xposX = baseX + tex.width - 21; + int xposY = baseOffset + 5; + if(posX >= xposX && posY >= xposY && posX < xposX + 16 && posY < xposY + 16) { + badge.hideNotif(); + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return true; + } + } + if(tex.rootClickEvent != null) { + if(currentScreen.handleComponentClick(tex.rootClickEvent)) { + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return true; + } + } + List cursorEvents = tex.cursorEvents; + if(tex.hasClickEvents && cursorEvents != null) { + for(int j = 0, m = cursorEvents.size(); j < m; ++j) { + ClickEventZone evt = cursorEvents.get(j); + if(evt.hasClickEvent) { + int offsetPosX = baseX + evt.posX; + int offsetPosY = baseOffset + evt.posY; + if(posX >= offsetPosX && posY >= offsetPosY && posX < offsetPosX + evt.width && posY < offsetPosY + evt.height) { + if(currentScreen.handleComponentClick(evt.chatComponent)) { + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return true; + } + } + } + } + } + } + } + baseOffset += texHeight; + } + } + } + return false; + } + + public void renderOverlay(int mouseX, int mouseY) { + if(mc.thePlayer == null) return; + ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager(); + List lst = mgr.getNotifBadgesToDisplay(); + if(!lst.isEmpty()) { + GlStateManager.clear(GL_DEPTH_BUFFER_BIT); + boolean showXButtons = false; + int baseOffset = mc.guiAchievement.getHeight(); + if(mc.currentScreen != null) { + if(mc.currentScreen instanceof GuiChat) { + baseOffset += 25; // exit button in chat screen; + showXButtons = true; + }else if(mc.currentScreen instanceof GuiScreenNotifications) { + return; + } + } + long millis = EagRuntime.steadyTimeMillis(); + boolean isBlend = false; + for(int i = 0, l = lst.size(); i < l; ++i) { + NotificationBadge badge = lst.get(i); + boolean isHiding = false; + if(badge.hideAtMillis != -1l) { + isHiding = true; + if(millis - badge.hideAtMillis > 500l) { + continue; + } + } + CachedNotifBadgeTexture tex = badge.getGLTexture(this, scaleFactor, showXButtons); + if(tex != null) { + GlStateManager.bindTexture(tex.glTexture); + float alphaTop = 1.0f; + float alphaBottom = 1.0f; + float timeRemainingSec; + long age = millis - badge.clientTimestamp; + if(isHiding) { + timeRemainingSec = (float)((double)(500l - (millis - badge.hideAtMillis)) * 0.001); + }else { + timeRemainingSec = (float)((double)((long)badge.hideAfterSec * 1000l - age) * 0.001); + } + timeRemainingSec = Math.min((float)(age * 0.001) + 0.001f, timeRemainingSec); + alphaTop *= MathHelper.clamp_float(timeRemainingSec * 3.0F, 0.0F, 1.0F); + alphaTop *= alphaTop; + alphaBottom *= MathHelper.clamp_float(timeRemainingSec * 2.0F, 0.0F, 1.0F); + alphaBottom *= alphaBottom; + if(alphaTop == 0.0F && alphaBottom == 0.0F) { + continue; + } + boolean blend = alphaTop < 1.0f || alphaBottom < 1.0f; + if(blend != isBlend) { + if(blend) { + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); + }else { + GlStateManager.disableBlend(); + } + isBlend = blend; + } + int px = width - tex.width; + drawTexturedGradientFBRect(px, baseOffset, tex.width, tex.height, + ((int) (alphaTop * 255.0f) << 24) | 0xFFFFFF, + ((int) (alphaBottom * 255.0f) << 24) | 0xFFFFFF, 200.0f); + if(showXButtons && tex.hasHoverEvents) { + if(mouseX >= px && mouseY >= baseOffset && mouseX < px + tex.width && mouseY < baseOffset + tex.height) { + List cursorEvents = tex.cursorEvents; + if(cursorEvents != null) { + for(int j = 0, m = cursorEvents.size(); j < m; ++j) { + ClickEventZone evt = cursorEvents.get(j); + if(evt.hasHoverEvent) { + int offsetPosX = px + evt.posX; + int offsetPosY = baseOffset + evt.posY; + if(mouseX >= offsetPosX && mouseY >= offsetPosY && mouseX < offsetPosX + evt.width + && mouseY < offsetPosY + evt.height) { + if(isBlend) { + GlStateManager.disableBlend(); + isBlend = false; + } + mc.currentScreen.handleComponentHover(evt.chatComponent, mouseX, mouseY); + } + } + } + } + } + } + baseOffset += tex.height * alphaTop; + } + } + if(isBlend) { + GlStateManager.disableBlend(); + } + } + } + + protected CachedNotifBadgeTexture renderBadge(NotificationBadge badge, int scaleFactor, boolean showXButton) { + int badgeWidth = BADGE_WIDTH; + int badgeHeight = 10; + + int leftPadding = 6; + int rightPadding = 26; + + int mainIconSW = 32; + if(badge.mainIcon != null) { + int iw = badge.mainIcon.texture.getWidth(); + int ih = badge.mainIcon.texture.getHeight(); + float iaspect = (float)iw / (float)ih; + mainIconSW = (int)(32 * iaspect); + leftPadding += Math.min(mainIconSW, 64) + 3; + } + + int textZoneWidth = badgeWidth - leftPadding - rightPadding; + int bodyYOffset = 5; + + String titleText = null; + IChatComponent titleComponent = badge.getTitleProfanityFilter(); + if(titleComponent != null) { + titleText = titleComponent.getFormattedText(); + if(titleText.length() > 0) { + badgeHeight += 12; + bodyYOffset += 12; + }else { + titleText = null; + } + } + + if(badge.titleIcon != null && titleText == null) { + badgeHeight += 12; + bodyYOffset += 12; + } + + float bodyFontSize = 0.75f; + List bodyLines = null; + List clickEvents = null; + IChatComponent rootClickEvt = null; + boolean hasClickEvents = false; + boolean hasHoverEvents = false; + + int bodyHeight = 0; + + IChatComponent bodyComponent = badge.getBodyProfanityFilter(); + if(bodyComponent != null) { + if (bodyComponent.getChatStyle().getChatClickEvent() != null + && bodyComponent.getChatStyle().getChatClickEvent().getAction().shouldAllowInChat()) { + rootClickEvt = bodyComponent; + } + bodyLines = GuiUtilRenderComponents.func_178908_a(bodyComponent, (int) (textZoneWidth / bodyFontSize), + mc.fontRendererObj, true, true); + + int maxHeight = BADGE_HEIGHT - 32; + int maxLines = MathHelper.floor_float(maxHeight / (9 * bodyFontSize)); + if(bodyLines.size() > maxLines) { + bodyLines = bodyLines.subList(0, maxLines); + bodyComponent = bodyLines.get(maxLines - 1); + List siblings = bodyComponent.getSiblings(); + IChatComponent dots = new ChatComponentText("..."); + if(siblings != null && siblings.size() > 0) { + dots.setChatStyle(siblings.get(siblings.size() - 1).getChatStyle()); + } + bodyComponent.appendSibling(dots); + } + bodyHeight = MathHelper.floor_float(bodyLines.size() * (9 * bodyFontSize)); + } + + String sourceText = null; + IChatComponent sourceComponent = badge.getSourceProfanityFilter(); + if(sourceComponent != null) { + sourceText = sourceComponent.getFormattedText(); + if(sourceText.length() == 0) { + sourceText = null; + } + } + + if(badge.mainIcon != null) { + bodyHeight = Math.max(sourceText != null ? 30 : 32, bodyHeight); + } + + if(sourceText != null) { + badgeHeight += 6; + } + + badgeHeight += bodyHeight; + + badgeHeight = Math.max(badgeHeight, showXButton ? 42 : 26); + + if(badgeHeight > BADGE_HEIGHT) { + logger.info("Warning: Badge {} was {} pixels too high!", badge.badgeUUID, BADGE_HEIGHT - badgeHeight); + badgeHeight = BADGE_HEIGHT; + } + + int glTex = GlStateManager.generateTexture(); + GlStateManager.bindTexture(glTex); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, badgeWidth * scaleFactor, badgeHeight * scaleFactor, 0, GL_RGBA, + GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglBindFramebuffer(_GL_FRAMEBUFFER, rendererFramebuffer); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(glTex), 0); + _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); + + int[] oldViewport = new int[4]; + EaglercraftGPU.glGetInteger(GL_VIEWPORT, oldViewport); + + GlStateManager.viewport(0, 0, badgeWidth * scaleFactor, badgeHeight * scaleFactor); + + GlStateManager.disableDepth(); + GlStateManager.depthMask(false); + GlStateManager.enableTexture2D(); + GlStateManager.disableLighting(); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + + GlStateManager.matrixMode(GL_PROJECTION); + GlStateManager.pushMatrix(); + GlStateManager.loadIdentity(); + GlStateManager.ortho(0.0D, badgeWidth, badgeHeight, 0.0D, 1000.0D, 3000.0D); + GlStateManager.matrixMode(GL_MODELVIEW); + GlStateManager.pushMatrix(); + GlStateManager.loadIdentity(); + GlStateManager.translate(0.0F, 0.0F, -2000.0F); + + Tessellator tess = Tessellator.getInstance(); + WorldRenderer worldRenderer = tess.getWorldRenderer(); + + worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX_COLOR); + + mc.getTextureManager().bindTexture(eaglerGui); + + drawTexturedColoredRect(worldRenderer, 0, 0, 96, 192, 160, 8, (badge.backgroundColor >>> 16) & 0xFF, + (badge.backgroundColor >>> 8) & 0xFF, badge.backgroundColor & 0xFF, 0xFF); + + drawTexturedColoredRect(worldRenderer, 0, 8, 96, 192 + (BADGE_HEIGHT - badgeHeight + 8), 160, (badgeHeight - 8), + (badge.backgroundColor >>> 16) & 0xFF, (badge.backgroundColor >>> 8) & 0xFF, + badge.backgroundColor & 0xFF, 0xFF); + + switch(badge.priority) { + case LOW: + default: + drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 192, 176, 16, 16, (badge.backgroundColor >>> 16) & 0xFF, + (badge.backgroundColor >>> 8) & 0xFF, badge.backgroundColor & 0xFF, 0xFF); + break; + case NORMAL: + drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 208, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF); + break; + case HIGHER: + drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 224, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF); + break; + case HIGHEST: + drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 240, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF); + break; + } + + if(showXButton) { + drawTexturedColoredRect(worldRenderer, badgeWidth - 21, 5, 80, 208, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF); + } + + tess.draw(); + + if(badge.mainIcon != null) { + mc.getTextureManager().bindTexture(badge.mainIcon.resource); + drawTexturedRect(6, bodyYOffset, mainIconSW, 32); + } + + if(badge.titleIcon != null) { + mc.getTextureManager().bindTexture(badge.titleIcon.resource); + drawTexturedRect(6, 5, 8, 8); + } + + if(titleText != null) { + GlStateManager.pushMatrix(); + GlStateManager.translate(6 + (badge.titleIcon != null ? 10 : 0), 6, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + mc.fontRendererObj.drawStringWithShadow(titleText, 0, 0, badge.titleTxtColor); + GlStateManager.popMatrix(); + } + + if(bodyLines != null && !bodyLines.isEmpty()) { + GlStateManager.pushMatrix(); + if(!showXButton && badge.mainIcon == null && titleText != null) { + bodyYOffset -= 2; + } + GlStateManager.translate(leftPadding, bodyYOffset, 0.0f); + int l = bodyLines.size(); + GlStateManager.scale(bodyFontSize, bodyFontSize, bodyFontSize); + for(int i = 0; i < l; ++i) { + int startXLocal = 0; + int startXReal = leftPadding; + for(IChatComponent comp : bodyLines.get(i)) { + int w = mc.fontRendererObj.drawStringWithShadow( + comp.getChatStyle().getFormattingCode() + comp.getUnformattedTextForChat(), startXLocal, + i * 9, badge.bodyTxtColor) - startXLocal; + ClickEvent clickEvent = comp.getChatStyle().getChatClickEvent(); + HoverEvent hoverEvent = comp.getChatStyle().getChatHoverEvent(); + if(clickEvent != null && !clickEvent.getAction().shouldAllowInChat()) { + clickEvent = null; + } + if(hoverEvent != null && !hoverEvent.getAction().shouldAllowInChat()) { + hoverEvent = null; + } + if(clickEvent != null || hoverEvent != null) { + hasClickEvents |= clickEvent != null; + hasHoverEvents |= hoverEvent != null; + if(clickEvents == null) { + clickEvents = new ArrayList<>(); + } + clickEvents.add(new ClickEventZone(startXReal + (int) (startXLocal * bodyFontSize), + bodyYOffset + (int) (i * 9 * bodyFontSize), (int) (w * bodyFontSize), + (int) (9 * bodyFontSize), comp, clickEvent != null, hoverEvent != null)); + } + startXLocal += w; + } + } + GlStateManager.popMatrix(); + } + + if(sourceText != null) { + GlStateManager.pushMatrix(); + GlStateManager.translate(badgeWidth - 21, badgeHeight - 5, 0.0f); + GlStateManager.scale(0.5f, 0.5f, 0.5f); + mc.fontRendererObj.drawStringWithShadow(sourceText, -mc.fontRendererObj.getStringWidth(sourceText) - 4, -10, badge.sourceTxtColor); + GlStateManager.popMatrix(); + } + + GlStateManager.matrixMode(GL_PROJECTION); + GlStateManager.popMatrix(); + GlStateManager.matrixMode(GL_MODELVIEW); + GlStateManager.popMatrix(); + + GlStateManager.depthMask(true); + GlStateManager.enableDepth(); + + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + GlStateManager.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); + + return new CachedNotifBadgeTexture(glTex, scaleFactor, badgeWidth, badgeHeight, clickEvents, rootClickEvt, hasClickEvents, hasHoverEvents); + } + + static void drawTexturedColoredRect(WorldRenderer worldRenderer, float xCoord, float yCoord, int minU, + int minV, int width, int height, int r, int g, int b, int a) { + float f = 0.00390625F; + float f1 = 0.00390625F; + worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), 0.0).color(r, g, b, a) + .tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + height) * f1)).endVertex(); + worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), 0.0).color(r, g, b, a) + .tex((double) ((float) (minU + width) * f), (double) ((float) (minV + height) * f1)).endVertex(); + worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), 0.0).color(r, g, b, a) + .tex((double) ((float) (minU + width) * f), (double) ((float) (minV + 0) * f1)).endVertex(); + worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0).color(r, g, b, a) + .tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + 0) * f1)).endVertex(); + } + + static void drawTexturedGradientFBRect(float xCoord, float yCoord, int width, int height, int rgbaTop, int rgbaBottom, float zIndex) { + int topR = (rgbaTop >>> 16) & 0xFF; + int topG = (rgbaTop >>> 8) & 0xFF; + int topB = rgbaTop & 0xFF; + int topA = (rgbaTop >>> 24) & 0xFF; + int bottomR = (rgbaBottom >>> 16) & 0xFF; + int bottomG = (rgbaBottom >>> 8) & 0xFF; + int bottomB = rgbaBottom & 0xFF; + int bottomA = (rgbaBottom >>> 24) & 0xFF; + Tessellator tess = Tessellator.getInstance(); + WorldRenderer worldRenderer = tess.getWorldRenderer(); + worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX_COLOR); + worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), zIndex) + .color(bottomR, bottomG, bottomB, bottomA).tex(0.0, 0.0).endVertex(); + worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), zIndex) + .color(bottomR, bottomG, bottomB, bottomA).tex(1.0, 0.0).endVertex(); + worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), zIndex) + .color(topR, topG, topB, topA).tex(1.0, 1.0).endVertex(); + worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), zIndex).color(topR, topG, topB, topA) + .tex(0.0, 1.0).endVertex(); + tess.draw(); + } + + static void drawTexturedRect(float xCoord, float yCoord, int width, int height) { + Tessellator tess = Tessellator.getInstance(); + WorldRenderer worldRenderer = tess.getWorldRenderer(); + worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX); + worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), 0.0).tex(0.0, 1.0).endVertex(); + worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), 0.0).tex(1.0, 1.0).endVertex(); + worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), 0.0).tex(1.0, 0.0).endVertex(); + worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0).tex(0.0, 0.0).endVertex(); + tess.draw(); + } + + public void destroy() { + if(rendererFramebuffer != null) { + _wglDeleteFramebuffer(rendererFramebuffer); + rendererFramebuffer = null; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java index a3adcca..5fcd73f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java @@ -1,101 +1,123 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class DrawUtils { - - public static final String vertexShaderPath = "/assets/eagler/glsl/local.vsh"; - - public static IBufferArrayGL standardQuad2DVAO = null; - public static IBufferArrayGL standardQuad3DVAO = null; - public static IBufferGL standardQuadVBO = null; - - public static IShaderGL vshLocal = null; - - static void init() { - if(standardQuad2DVAO == null) { - standardQuad2DVAO = _wglGenVertexArrays(); - standardQuad3DVAO = _wglGenVertexArrays(); - standardQuadVBO = _wglGenBuffers(); - - FloatBuffer verts = EagRuntime.allocateFloatBuffer(18); - verts.put(new float[] { - -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f - }); - verts.flip(); - - EaglercraftGPU.bindGLArrayBuffer(standardQuadVBO); - _wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW); - EagRuntime.freeFloatBuffer(verts); - - EaglercraftGPU.bindGLBufferArray(standardQuad2DVAO); - - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 12, 0); - - EaglercraftGPU.bindGLBufferArray(standardQuad3DVAO); - - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0); - } - - if(vshLocal == null) { - String vertexSource = EagRuntime.getResourceString(vertexShaderPath); - if(vertexSource == null) { - throw new RuntimeException("vertex shader \"" + vertexShaderPath + "\" is missing!"); - } - - vshLocal = _wglCreateShader(GL_VERTEX_SHADER); - - _wglShaderSource(vshLocal, FixedFunctionConstants.VERSION + "\n" + vertexSource); - _wglCompileShader(vshLocal); - - if(_wglGetShaderi(vshLocal, GL_COMPILE_STATUS) != GL_TRUE) { - EaglercraftGPU.logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\"!"); - String log = _wglGetShaderInfoLog(vshLocal); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - EaglercraftGPU.logger.error("[VERT] {}", lines[i]); - } - } - throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); - } - } - } - - public static void drawStandardQuad2D() { - EaglercraftGPU.bindGLBufferArray(standardQuad2DVAO); - _wglDrawArrays(GL_TRIANGLES, 0, 6); - } - - public static void drawStandardQuad3D() { - EaglercraftGPU.bindGLBufferArray(standardQuad3DVAO); - _wglDrawArrays(GL_TRIANGLES, 0, 6); - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class DrawUtils { + + public static final String vertexShaderPath = "/assets/eagler/glsl/local.vsh"; + public static final String vertexShaderPrecision = "precision highp float;\n"; + + public static IBufferArrayGL standardQuad2DVAO = null; + public static IBufferArrayGL standardQuad3DVAO = null; + public static IBufferGL standardQuadVBO = null; + + public static IShaderGL vshLocal = null; + public static List vshLocalLayout = null; + + static void init() { + if(standardQuad2DVAO == null) { + standardQuad2DVAO = EaglercraftGPU.createGLBufferArray(); + standardQuad3DVAO = EaglercraftGPU.createGLBufferArray(); + standardQuadVBO = _wglGenBuffers(); + + FloatBuffer verts = EagRuntime.allocateFloatBuffer(18); + verts.put(new float[] { + -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f + }); + verts.flip(); + + EaglercraftGPU.bindVAOGLArrayBufferNow(standardQuadVBO); + _wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW); + EagRuntime.freeFloatBuffer(verts); + + EaglercraftGPU.bindGLBufferArray(standardQuad2DVAO); + + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 12, 0); + + EaglercraftGPU.bindGLBufferArray(standardQuad3DVAO); + + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0); + } + + if(vshLocal == null) { + String vertexSource = EagRuntime.getRequiredResourceString(vertexShaderPath); + + vshLocalLayout = VSHInputLayoutParser.getShaderInputs(vertexSource); + + vshLocal = _wglCreateShader(GL_VERTEX_SHADER); + + _wglShaderSource(vshLocal, GLSLHeader.getVertexHeaderCompat(vertexSource, vertexShaderPrecision)); + _wglCompileShader(vshLocal); + + if(_wglGetShaderi(vshLocal, GL_COMPILE_STATUS) != GL_TRUE) { + EaglercraftGPU.logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\"!"); + String log = _wglGetShaderInfoLog(vshLocal); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + EaglercraftGPU.logger.error("[VERT] {}", lines[i]); + } + } + throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); + } + } + } + + public static void drawStandardQuad2D() { + EaglercraftGPU.bindGLBufferArray(standardQuad2DVAO); + EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6); + } + + public static void drawStandardQuad3D() { + EaglercraftGPU.bindGLBufferArray(standardQuad3DVAO); + EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6); + } + + public static void destroy() { + if(standardQuad2DVAO != null) { + EaglercraftGPU.destroyGLBufferArray(standardQuad2DVAO); + standardQuad2DVAO = null; + } + if(standardQuad3DVAO != null) { + EaglercraftGPU.destroyGLBufferArray(standardQuad3DVAO); + standardQuad3DVAO = null; + } + if(standardQuadVBO != null) { + _wglDeleteBuffers(standardQuadVBO); + standardQuadVBO = null; + } + if(vshLocal != null) { + vshLocal.free(); + vshLocal = null; + vshLocalLayout = null; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java index 506fbb7..cb1c7b6 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java @@ -39,7 +39,7 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener { private static final Logger logger = LogManager.getLogger("EaglerMeshLoader"); - private static final Map meshCache = new HashMap(); + private static final Map meshCache = new HashMap<>(); public static HighPolyMesh getEaglerMesh(ResourceLocation meshLoc) { if(meshLoc.cachedPointerType == ResourceLocation.CACHED_POINTER_EAGLER_MESH) { @@ -104,7 +104,7 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener { } if(meshStruct.vertexArray == null) { - meshStruct.vertexArray = _wglGenVertexArrays(); + meshStruct.vertexArray = EaglercraftGPU.createGLBufferArray(); } if(meshStruct.vertexBuffer == null) { meshStruct.vertexBuffer = _wglGenBuffers(); @@ -115,29 +115,29 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener { up1.position(0).limit(intsOfVertex); - EaglercraftGPU.bindGLArrayBuffer(meshStruct.vertexBuffer); + EaglercraftGPU.bindVAOGLArrayBufferNow(meshStruct.vertexBuffer); _wglBufferData(GL_ARRAY_BUFFER, up1, GL_STATIC_DRAW); EaglercraftGPU.bindGLBufferArray(meshStruct.vertexArray); up1.position(intsOfVertex).limit(intsTotal); - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshStruct.indexBuffer); + EaglercraftGPU.bindVAOGLElementArrayBufferNow(meshStruct.indexBuffer); _wglBufferData(GL_ELEMENT_ARRAY_BUFFER, up1, GL_STATIC_DRAW); - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0); + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0); if(meshStruct.hasTexture) { - _wglEnableVertexAttribArray(1); - _wglVertexAttribPointer(1, 2, GL_FLOAT, false, stride, 16); + EaglercraftGPU.enableVertexAttribArray(1); + EaglercraftGPU.vertexAttribPointer(1, 2, GL_FLOAT, false, stride, 16); } - _wglEnableVertexAttribArray(meshStruct.hasTexture ? 2 : 1); - _wglVertexAttribPointer(meshStruct.hasTexture ? 2 : 1, 4, GL_BYTE, true, stride, 12); + EaglercraftGPU.enableVertexAttribArray(meshStruct.hasTexture ? 2 : 1); + EaglercraftGPU.vertexAttribPointer(meshStruct.hasTexture ? 2 : 1, 4, GL_BYTE, true, stride, 12); }catch(Throwable ex) { if(meshStruct.vertexArray != null) { - _wglDeleteVertexArrays(meshStruct.vertexArray); + EaglercraftGPU.destroyGLBufferArray(meshStruct.vertexArray); meshStruct.vertexArray = null; } if(meshStruct.vertexBuffer != null) { @@ -169,4 +169,4 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener { } } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java index c32b656..6b5dd78 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java @@ -1,671 +1,1019 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -import java.util.HashMap; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.GLObjectMap; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IQueryGL; -import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformBufferFunctions; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglercraftGPU { - - static final GLObjectMap mapTexturesGL = new GLObjectMap(32767); - static final GLObjectMap mapQueriesGL = new GLObjectMap(32767); - static final GLObjectMap mapDisplayListsGL = new GLObjectMap(32767); - - static final Logger logger = LogManager.getLogger("EaglercraftGPU"); - - public static final String gluErrorString(int i) { - switch (i) { - case GL_INVALID_ENUM: - return "GL_INVALID_ENUM"; - case GL_INVALID_VALUE: - return "GL_INVALID_VALUE"; - case 1286: - return "GL_INVALID_FRAMEBUFFER_OPERATION"; - case GL_INVALID_OPERATION: - return "GL_INVALID_OPERATION"; - case GL_OUT_OF_MEMORY: - return "GL_OUT_OF_MEMORY"; - case GL_CONTEXT_LOST_WEBGL: - return "CONTEXT_LOST_WEBGL"; - default: - return "Unknown Error"; - } - } - - public static final void glTexParameteri(int target, int param, int value) { - _wglTexParameteri(target, param, value); - } - - public static final void glTexParameterf(int target, int param, float value) { - _wglTexParameterf(target, param, value); - } - - public static final void glCopyTexSubImage2D(int target, int level, int sx, int sy, int dx, int dy, int w, int h) { - _wglCopyTexSubImage2D(target, level, sx, sy, dx, dy, w, h); - } - - private static DisplayList currentList = null; - private static ByteBuffer displayListBuffer = EagRuntime.allocateByteBuffer(0x100000); - - public static final void glNewList(int target, int op) { - if (currentList != null) { - throw new IllegalStateException("A display list is already being compiled you eagler!"); - } - if (op != GL_COMPILE) { - throw new UnsupportedOperationException("Only GL_COMPILE is supported by glNewList"); - } - DisplayList dp = currentList = mapDisplayListsGL.get(target); - if (dp == null) { - throw new IllegalArgumentException("Unknown display list: " + target); - } - if (dp.vertexArray != null && dp.attribs > 0) { - EaglercraftGPU.bindGLBufferArray(dp.vertexArray); - int c = 0; - if ((dp.attribs & ATTRIB_TEXTURE) == ATTRIB_TEXTURE) { - _wglDisableVertexAttribArray(++c); - } - if ((dp.attribs & ATTRIB_COLOR) == ATTRIB_COLOR) { - _wglDisableVertexAttribArray(++c); - } - if ((dp.attribs & ATTRIB_NORMAL) == ATTRIB_NORMAL) { - _wglDisableVertexAttribArray(++c); - } - if ((dp.attribs & ATTRIB_LIGHTMAP) == ATTRIB_LIGHTMAP) { - _wglDisableVertexAttribArray(++c); - } - } - dp.attribs = -1; - dp.mode = -1; - dp.count = 0; - } - - private static final void growDisplayListBuffer(int len) { - int wantSize = displayListBuffer.position() + len; - if (displayListBuffer.capacity() < wantSize) { - int newSize = (wantSize & 0xFFFE0000) + 0x40000; - ByteBuffer newBuffer = EagRuntime.allocateByteBuffer(newSize); - PlatformBufferFunctions.put(newBuffer, (ByteBuffer) displayListBuffer.flip()); - EagRuntime.freeByteBuffer(displayListBuffer); - displayListBuffer = newBuffer; - } - } - - public static final void glEndList() { - DisplayList dp = currentList; - if (dp == null) { - throw new IllegalStateException("No list is currently being compiled!"); - } - - if (dp.attribs == -1) { - if (dp.vertexArray != null) { - _wglDeleteVertexArrays(dp.vertexArray); - dp.vertexArray = null; - } - if (dp.vertexBuffer != null) { - _wglDeleteBuffers(dp.vertexBuffer); - dp.vertexBuffer = null; - } - currentList = null; - return; - } - - if (dp.vertexArray == null) { - dp.vertexArray = _wglGenVertexArrays(); - dp.bindQuad16 = false; - dp.bindQuad32 = false; - } - if (dp.vertexBuffer == null) { - dp.vertexBuffer = _wglGenBuffers(); - } - - bindGLArrayBuffer(dp.vertexBuffer); - displayListBuffer.flip(); - _wglBufferData(GL_ARRAY_BUFFER, displayListBuffer, GL_STATIC_DRAW); - displayListBuffer.clear(); - - FixedFunctionPipeline.setupDisplayList(dp); - currentList = null; - } - - public static final void glCallList(int displayList) { - DisplayList dp = mapDisplayListsGL.get(displayList); - if (dp == null) { - throw new NullPointerException("Tried to call a display list that does not exist: " + displayList); - } - if (dp.attribs != -1) { - FixedFunctionPipeline p = FixedFunctionPipeline.setupRenderDisplayList(dp.attribs).update(); - bindGLBufferArray(dp.vertexArray); - if (dp.mode == GL_QUADS) { - int cnt = dp.count; - if (cnt > 0xFFFF) { - if (!dp.bindQuad32) { - dp.bindQuad16 = false; - dp.bindQuad32 = true; - attachQuad32EmulationBuffer(cnt, true); - } else { - attachQuad32EmulationBuffer(cnt, false); - } - p.drawElements(GL_TRIANGLES, cnt + (cnt >> 1), GL_UNSIGNED_INT, 0); - } else { - if (!dp.bindQuad16) { - dp.bindQuad16 = true; - dp.bindQuad32 = false; - attachQuad16EmulationBuffer(cnt, true); - } else { - attachQuad16EmulationBuffer(cnt, false); - } - p.drawElements(GL_TRIANGLES, cnt + (cnt >> 1), GL_UNSIGNED_SHORT, 0); - } - } else { - p.drawArrays(dp.mode, 0, dp.count); - } - } - } - - public static final void flushDisplayList(int displayList) { - DisplayList dp = mapDisplayListsGL.get(displayList); - if (dp == null) { - throw new NullPointerException("Tried to flush a display list that does not exist: " + displayList); - } - dp.attribs = -1; - if (dp.vertexArray != null) { - _wglDeleteVertexArrays(dp.vertexArray); - dp.vertexArray = null; - } - if (dp.vertexBuffer != null) { - _wglDeleteBuffers(dp.vertexBuffer); - dp.vertexBuffer = null; - } - } - - public static final void glNormal3f(float x, float y, float z) { - GlStateManager.stateNormalX = x; - GlStateManager.stateNormalY = y; - GlStateManager.stateNormalZ = z; - ++GlStateManager.stateNormalSerial; - } - - private static final Map stringCache = new HashMap(); - - public static final String glGetString(int param) { - String str = stringCache.get(param); - if (str == null) { - str = _wglGetString(param); - stringCache.put(param, str); - } - return str; - } - - public static final void glGetInteger(int param, int[] values) { - switch (param) { - case GL_VIEWPORT: - values[0] = GlStateManager.viewportX; - values[1] = GlStateManager.viewportY; - values[2] = GlStateManager.viewportW; - values[3] = GlStateManager.viewportH; - break; - default: - throw new UnsupportedOperationException("glGetInteger only accepts GL_VIEWPORT as a parameter"); - } - } - - public static final int glGetInteger(int param) { - return _wglGetInteger(param); - } - - public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused, - int format, int type, IntBuffer pixels) { - _wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels); - } - - public static final void glTexSubImage2D(int target, int level, int x, int y, int w, int h, int format, - int type, IntBuffer pixels) { - _wglTexSubImage2D(target, level, x, y, w, h, format, type, pixels); - } - - public static final void glTexStorage2D(int target, int levels, int internalFormat, int w, int h) { - _wglTexStorage2D(target, levels, internalFormat, w, h); - } - - public static final void glLineWidth(float f) { - _wglLineWidth(f); - } - - public static final void glFog(int param, FloatBuffer valueBuffer) { - int pos = valueBuffer.position(); - switch (param) { - case GL_FOG_COLOR: - GlStateManager.stateFogColorR = valueBuffer.get(); - GlStateManager.stateFogColorG = valueBuffer.get(); - GlStateManager.stateFogColorB = valueBuffer.get(); - GlStateManager.stateFogColorA = valueBuffer.get(); - ++GlStateManager.stateFogSerial; - break; - default: - throw new UnsupportedOperationException("Only GL_FOG_COLOR is configurable!"); - } - valueBuffer.position(pos); - } - - public static final void glFogi(int param, int value) { - // I'm not sure what this is for currently - } - - public static final int glGenLists() { - return mapDisplayListsGL.register(new DisplayList()); - } - - public static final void glDeleteLists(int id) { - DisplayList d = mapDisplayListsGL.free(id); - if (d != null) { - if (d.vertexArray != null) { - _wglDeleteVertexArrays(d.vertexArray); - } - if (d.vertexBuffer != null) { - _wglDeleteBuffers(d.vertexBuffer); - } - } - } - - public static final int glGetError() { - return _wglGetError(); - } - - public static final void glBlendEquation(int equation) { - if (equation != GlStateManager.stateBlendEquation) { - _wglBlendEquation(equation); - GlStateManager.stateBlendEquation = equation; - } - } - - private static IBufferArrayGL currentBufferArray = null; - - public static final void bindGLBufferArray(IBufferArrayGL buffer) { - if (currentBufferArray != buffer) { - _wglBindVertexArray(buffer); - currentBufferArray = buffer; - } - } - - private static IBufferGL currentArrayBuffer = null; - - public static final void bindGLArrayBuffer(IBufferGL buffer) { - if (currentArrayBuffer != buffer) { - _wglBindBuffer(GL_ARRAY_BUFFER, buffer); - currentArrayBuffer = buffer; - } - } - - private static IBufferGL currentUniformBuffer = null; - - public static final void bindGLUniformBuffer(IBufferGL buffer) { - if (currentUniformBuffer != buffer) { - _wglBindBuffer(0x8A11, buffer); - currentUniformBuffer = buffer; - } - } - - private static IProgramGL currentShaderProgram = null; - - public static final void bindGLShaderProgram(IProgramGL prog) { - if (currentShaderProgram != prog) { - _wglUseProgram(prog); - currentShaderProgram = prog; - } - } - - private static final IBufferGL[] currentUniformBlockBindings = new IBufferGL[16]; - private static final int[] currentUniformBlockBindingOffset = new int[16]; - private static final int[] currentUniformBlockBindingSize = new int[16]; - - public static final void bindUniformBufferRange(int index, IBufferGL buffer, int offset, int size) { - if (currentUniformBlockBindings[index] != buffer || currentUniformBlockBindingOffset[index] != offset - || currentUniformBlockBindingSize[index] != size) { - _wglBindBufferRange(0x8A11, index, buffer, offset, size); - currentUniformBlockBindings[index] = buffer; - currentUniformBlockBindingOffset[index] = offset; - currentUniformBlockBindingSize[index] = size; - } - } - - public static final int ATTRIB_TEXTURE = 1; - public static final int ATTRIB_COLOR = 2; - public static final int ATTRIB_NORMAL = 4; - public static final int ATTRIB_LIGHTMAP = 8; - - public static final void renderBuffer(ByteBuffer buffer, int attrib, int mode, int count) { - if (currentList != null) { - if (currentList.attribs == -1) { - currentList.attribs = attrib; - } else if (currentList.attribs != attrib) { - throw new UnsupportedOperationException( - "Inconsistent vertex format in display list (only one is allowed)"); - } - if (currentList.mode == -1) { - currentList.mode = mode; - } else if (currentList.mode != mode) { - throw new UnsupportedOperationException("Inconsistent draw mode in display list (only one is allowed)"); - } - currentList.count += count; - if (buffer.remaining() > displayListBuffer.remaining()) { - growDisplayListBuffer(buffer.remaining()); - } - displayListBuffer.put(buffer); - lastRender = null; - } else { - lastRender = FixedFunctionPipeline.setupDirect(buffer, attrib).update(); - lastRender.drawDirectArrays(mode, 0, count); - lastMode = mode; - lastCount = count; - } - } - - public static final void optimize() { - FixedFunctionPipeline.optimize(); - } - - private static FixedFunctionPipeline lastRender = null; - private static int lastMode = 0; - private static int lastCount = 0; - - public static final void renderAgain() { - if (lastRender == null) { - throw new UnsupportedOperationException( - "Cannot render the same verticies twice while generating display list"); - } - EaglercraftGPU.bindGLBufferArray(lastRender.getDirectModeBufferArray()); - lastRender.update().drawDirectArrays(lastMode, 0, lastCount); - } - - private static IBufferGL quad16EmulationBuffer = null; - private static int quad16EmulationBufferSize = 0; - - private static IBufferGL quad32EmulationBuffer = null; - private static int quad32EmulationBufferSize = 0; - - public static final void attachQuad16EmulationBuffer(int vertexCount, boolean bind) { - IBufferGL buf = quad16EmulationBuffer; - if (buf == null) { - quad16EmulationBuffer = buf = _wglGenBuffers(); - int newSize = quad16EmulationBufferSize = (vertexCount & 0xFFFFF000) + 0x2000; - if (newSize > 0xFFFF) { - newSize = 0xFFFF; - } - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); - resizeQuad16EmulationBuffer(newSize >> 2); - } else { - int cnt = quad16EmulationBufferSize; - if (cnt < vertexCount) { - int newSize = quad16EmulationBufferSize = (vertexCount & 0xFFFFF000) + 0x2000; - if (newSize > 0xFFFF) { - newSize = 0xFFFF; - } - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); - resizeQuad16EmulationBuffer(newSize >> 2); - } else if (bind) { - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); - } - } - } - - public static final void attachQuad32EmulationBuffer(int vertexCount, boolean bind) { - IBufferGL buf = quad32EmulationBuffer; - if (buf == null) { - quad32EmulationBuffer = buf = _wglGenBuffers(); - int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000; - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); - resizeQuad32EmulationBuffer(newSize >> 2); - } else { - int cnt = quad32EmulationBufferSize; - if (cnt < vertexCount) { - int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000; - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); - resizeQuad32EmulationBuffer(newSize >> 2); - } else if (bind) { - _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); - } - } - } - - private static final void resizeQuad16EmulationBuffer(int quadCount) { - IntBuffer buf = EagRuntime.allocateIntBuffer(quadCount * 3); - int v1, v2, v3, v4; - for (int i = 0; i < quadCount; ++i) { - v1 = i << 2; - v2 = v1 + 1; - v3 = v2 + 1; - v4 = v3 + 1; - buf.put(v1 | (v2 << 16)); - buf.put(v4 | (v2 << 16)); - buf.put(v3 | (v4 << 16)); - } - buf.flip(); - _wglBufferData(GL_ELEMENT_ARRAY_BUFFER, buf, GL_STATIC_DRAW); - EagRuntime.freeIntBuffer(buf); - } - - private static final void resizeQuad32EmulationBuffer(int quadCount) { - IntBuffer buf = EagRuntime.allocateIntBuffer(quadCount * 6); - int v1, v2, v3, v4; - for (int i = 0; i < quadCount; ++i) { - v1 = i << 2; - v2 = v1 + 1; - v3 = v2 + 1; - v4 = v3 + 1; - buf.put(v1); - buf.put(v2); - buf.put(v4); - buf.put(v2); - buf.put(v3); - buf.put(v4); - } - buf.flip(); - _wglBufferData(GL_ELEMENT_ARRAY_BUFFER, buf, GL_STATIC_DRAW); - EagRuntime.freeIntBuffer(buf); - } - - public static final ITextureGL getNativeTexture(int tex) { - return mapTexturesGL.get(tex); - } - - public static final void drawHighPoly(HighPolyMesh mesh) { - if (mesh.vertexCount == 0 || mesh.indexCount == 0 || mesh.vertexArray == null) { - return; - } - FixedFunctionPipeline p = FixedFunctionPipeline.setupRenderDisplayList(mesh.getAttribBits()).update(); - EaglercraftGPU.bindGLBufferArray(mesh.vertexArray); - p.drawElements(GL_TRIANGLES, mesh.indexCount, GL_UNSIGNED_SHORT, 0); - } - - static boolean hasFramebufferHDR16FSupport = false; - static boolean hasFramebufferHDR32FSupport = false; - static boolean hasLinearHDR32FSupport = false; - - public static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, - boolean allow32bitFallback) { - createFramebufferHDR16FTexture(target, level, w, h, format, allow32bitFallback, null); - } - - public static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, - ByteBuffer pixelData) { - createFramebufferHDR16FTexture(target, level, w, h, format, false, pixelData); - } - - private static void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, - boolean allow32bitFallback, ByteBuffer pixelData) { - if (hasFramebufferHDR16FSupport) { - int internalFormat; - switch (format) { - case GL_RED: - internalFormat = 0x822D; // GL_R16F - break; - case 0x8227: // GL_RG - internalFormat = 0x822F; // GL_RG16F - case GL_RGB: - throw new UnsupportedOperationException( - "GL_RGB16F isn't supported specifically in WebGL 2.0 for some goddamn reason"); - case GL_RGBA: - internalFormat = 0x881A; // GL_RGBA16F - break; - default: - throw new UnsupportedOperationException("Unknown format: " + format); - } - _wglTexImage2Du16(target, level, internalFormat, w, h, 0, format, 0x140B, pixelData); - } else { - if (allow32bitFallback) { - if (hasFramebufferHDR32FSupport) { - createFramebufferHDR32FTexture(target, level, w, h, format, false, null); - } else { - throw new UnsupportedOperationException( - "No fallback 32-bit HDR (floating point) texture support is available on this device"); - } - } else { - throw new UnsupportedOperationException( - "16-bit HDR (floating point) textures are not supported on this device"); - } - } - } - - public static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, - boolean allow16bitFallback) { - createFramebufferHDR32FTexture(target, level, w, h, format, allow16bitFallback, null); - } - - public static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, - ByteBuffer pixelData) { - createFramebufferHDR32FTexture(target, level, w, h, format, false, pixelData); - } - - private static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, - boolean allow16bitFallback, ByteBuffer pixelData) { - if (hasFramebufferHDR32FSupport) { - int internalFormat; - switch (format) { - case GL_RED: - internalFormat = 0x822E; // GL_R32F - break; - case 0x8227: // GL_RG - internalFormat = 0x822F; // GL_RG32F - case GL_RGB: - throw new UnsupportedOperationException( - "GL_RGB32F isn't supported specifically in WebGL 2.0 for some goddamn reason"); - case GL_RGBA: - internalFormat = 0x8814; // GL_RGBA32F - break; - default: - throw new UnsupportedOperationException("Unknown format: " + format); - } - _wglTexImage2Df32(target, level, internalFormat, w, h, 0, format, GL_FLOAT, pixelData); - } else { - if (allow16bitFallback) { - if (hasFramebufferHDR16FSupport) { - createFramebufferHDR16FTexture(target, level, w, h, format, false); - } else { - throw new UnsupportedOperationException( - "No fallback 16-bit HDR (floating point) texture support is available on this device"); - } - } else { - throw new UnsupportedOperationException( - "32-bit HDR (floating point) textures are not supported on this device"); - } - } - } - - public static final void warmUpCache() { - EaglercraftGPU.glGetString(7936); - EaglercraftGPU.glGetString(7937); - EaglercraftGPU.glGetString(7938); - hasFramebufferHDR16FSupport = PlatformOpenGL.checkHDRFramebufferSupport(16); - if (hasFramebufferHDR16FSupport) { - logger.info("16-bit HDR render target support: true"); - } else { - logger.error("16-bit HDR render target support: false"); - } - hasFramebufferHDR32FSupport = PlatformOpenGL.checkHDRFramebufferSupport(32); - if (hasFramebufferHDR32FSupport) { - logger.info("32-bit HDR render target support: true"); - } else { - logger.error("32-bit HDR render target support: false"); - } - hasLinearHDR32FSupport = PlatformOpenGL.checkLinearHDR32FSupport(); - if (hasLinearHDR32FSupport) { - logger.info("32-bit HDR linear filter support: true"); - } else { - logger.error("32-bit HDR linear filter support: false"); - } - if (!checkHasHDRFramebufferSupportWithFilter()) { - logger.error("No HDR render target support was detected! Shaders will be disabled."); - } - DrawUtils.init(); - SpriteLevelMixer.initialize(); - InstancedFontRenderer.initialize(); - InstancedParticleRenderer.initialize(); - EffectPipelineFXAA.initialize(); - TextureCopyUtil.initialize(); - DrawUtils.vshLocal.free(); - DrawUtils.vshLocal = null; - } - - public static final boolean checkHDRFramebufferSupport(int bits) { - switch (bits) { - case 16: - return hasFramebufferHDR16FSupport; - case 32: - return hasFramebufferHDR32FSupport; - default: - return false; - } - } - - public static final boolean checkHasHDRFramebufferSupport() { - return hasFramebufferHDR16FSupport || hasFramebufferHDR32FSupport; - } - - public static final boolean checkHasHDRFramebufferSupportWithFilter() { - return hasFramebufferHDR16FSupport || (hasFramebufferHDR32FSupport && hasLinearHDR32FSupport); - } - - public static final boolean checkLinearHDR32FSupport() { - return hasLinearHDR32FSupport; - } -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.util.MathHelper; + +import java.util.HashMap; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.GLObjectMap; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IQueryGL; +import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglercraftGPU { + + static final GLObjectMap mapTexturesGL = new GLObjectMap<>(8192); + static final GLObjectMap mapQueriesGL = new GLObjectMap<>(8192); + static final GLObjectMap mapDisplayListsGL = new GLObjectMap<>(8192); + + static final Logger logger = LogManager.getLogger("EaglercraftGPU"); + + static boolean emulatedVAOs = false; + static SoftGLBufferState emulatedVAOState = new SoftGLBufferState(); + + public static final String gluErrorString(int i) { + switch(i) { + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case 1286: return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + case GL_CONTEXT_LOST_WEBGL: return "CONTEXT_LOST_WEBGL"; + default: return "Unknown Error"; + } + } + + public static final void glTexParameteri(int target, int param, int value) { + _wglTexParameteri(target, param, value); + } + + public static final void glTexParameterf(int target, int param, float value) { + _wglTexParameterf(target, param, value); + } + + public static final void glCopyTexSubImage2D(int target, int level, int sx, int sy, int dx, int dy, int w, int h) { + _wglCopyTexSubImage2D(target, level, sx, sy, dx, dy, w, h); + } + + private static DisplayList currentList = null; + private static ByteBuffer displayListBuffer = EagRuntime.allocateByteBuffer(0x100000); + + public static final void glNewList(int target, int op) { + if(currentList != null) { + throw new IllegalStateException("A display list is already being compiled you eagler!"); + } + if(op != GL_COMPILE) { + throw new UnsupportedOperationException("Only GL_COMPILE is supported by glNewList"); + } + DisplayList dp = currentList = mapDisplayListsGL.get(target); + if(dp == null) { + throw new IllegalArgumentException("Unknown display list: " + target); + } + if(dp.vertexArray != null && dp.attribs > 0) { + EaglercraftGPU.bindGLBufferArray(dp.vertexArray); + int c = 0; + if((dp.attribs & ATTRIB_TEXTURE) == ATTRIB_TEXTURE) { + EaglercraftGPU.disableVertexAttribArray(++c); + } + if((dp.attribs & ATTRIB_COLOR) == ATTRIB_COLOR) { + EaglercraftGPU.disableVertexAttribArray(++c); + } + if((dp.attribs & ATTRIB_NORMAL) == ATTRIB_NORMAL) { + EaglercraftGPU.disableVertexAttribArray(++c); + } + if((dp.attribs & ATTRIB_LIGHTMAP) == ATTRIB_LIGHTMAP) { + EaglercraftGPU.disableVertexAttribArray(++c); + } + } + dp.attribs = -1; + dp.mode = -1; + dp.count = 0; + } + + private static final void growDisplayListBuffer(int len) { + int wantSize = displayListBuffer.position() + len; + if(displayListBuffer.capacity() < wantSize) { + int newSize = (wantSize & 0xFFFE0000) + 0x40000; + ByteBuffer newBuffer = EagRuntime.allocateByteBuffer(newSize); + newBuffer.put((ByteBuffer)displayListBuffer.flip()); + EagRuntime.freeByteBuffer(displayListBuffer); + displayListBuffer = newBuffer; + } + } + + public static final void glEndList() { + DisplayList dp = currentList; + if(dp == null) { + throw new IllegalStateException("No list is currently being compiled!"); + } + + if(dp.attribs == -1) { + if(dp.vertexArray != null) { + EaglercraftGPU.destroyGLBufferArray(dp.vertexArray); + dp.vertexArray = null; + } + if(dp.vertexBuffer != null) { + _wglDeleteBuffers(dp.vertexBuffer); + dp.vertexBuffer = null; + } + currentList = null; + return; + } + + if(dp.vertexArray == null) { + dp.vertexArray = createGLBufferArray(); + dp.bindQuad16 = false; + dp.bindQuad32 = false; + } + if(dp.vertexBuffer == null) { + dp.vertexBuffer = _wglGenBuffers(); + } + + bindVAOGLArrayBufferNow(dp.vertexBuffer); + displayListBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, displayListBuffer, GL_STATIC_DRAW); + displayListBuffer.clear(); + + FixedFunctionPipeline.setupDisplayList(dp); + currentList = null; + } + + public static final void glCallList(int displayList) { + DisplayList dp = mapDisplayListsGL.get(displayList); + if(dp == null) { + throw new NullPointerException("Tried to call a display list that does not exist: " + displayList); + } + if(dp.attribs != -1) { + FixedFunctionPipeline p = FixedFunctionPipeline.setupRenderDisplayList(dp.attribs).update(); + bindGLBufferArray(dp.vertexArray); + if(dp.mode == GL_QUADS) { + int cnt = dp.count; + if(cnt > 0xFFFF) { + if(!dp.bindQuad32) { + dp.bindQuad16 = false; + dp.bindQuad32 = true; + attachQuad32EmulationBuffer(cnt, true); + }else { + attachQuad32EmulationBuffer(cnt, false); + } + p.drawElements(GL_TRIANGLES, cnt + (cnt >> 1), GL_UNSIGNED_INT, 0); + }else { + if(!dp.bindQuad16) { + dp.bindQuad16 = true; + dp.bindQuad32 = false; + attachQuad16EmulationBuffer(cnt, true); + }else { + attachQuad16EmulationBuffer(cnt, false); + } + p.drawElements(GL_TRIANGLES, cnt + (cnt >> 1), GL_UNSIGNED_SHORT, 0); + } + }else { + p.drawArrays(dp.mode, 0, dp.count); + } + } + } + + public static final void flushDisplayList(int displayList) { + DisplayList dp = mapDisplayListsGL.get(displayList); + if(dp == null) { + throw new NullPointerException("Tried to flush a display list that does not exist: " + displayList); + } + dp.attribs = -1; + if(dp.vertexArray != null) { + EaglercraftGPU.destroyGLBufferArray(dp.vertexArray); + dp.vertexArray = null; + } + if(dp.vertexBuffer != null) { + _wglDeleteBuffers(dp.vertexBuffer); + dp.vertexBuffer = null; + } + } + + public static final void glNormal3f(float x, float y, float z) { + GlStateManager.stateNormalX = x; + GlStateManager.stateNormalY = y; + GlStateManager.stateNormalZ = z; + ++GlStateManager.stateNormalSerial; + } + + private static final Map stringCache = new HashMap<>(); + + public static final String glGetString(int param) { + String str = stringCache.get(param); + if(str == null) { + str = _wglGetString(param); + stringCache.put(param, str); + } + return str; + } + + public static final void glGetInteger(int param, int[] values) { + switch(param) { + case GL_VIEWPORT: + values[0] = GlStateManager.viewportX; + values[1] = GlStateManager.viewportY; + values[2] = GlStateManager.viewportW; + values[3] = GlStateManager.viewportH; + break; + default: + throw new UnsupportedOperationException("glGetInteger only accepts GL_VIEWPORT as a parameter"); + } + } + + public static final int glGetInteger(int param) { + return _wglGetInteger(param); + } + + public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused, + int format, int type, ByteBuffer pixels) { + if(glesVers >= 300) { + _wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels); + }else { + int tv = TextureFormatHelper.trivializeInternalFormatToGLES20(internalFormat); + _wglTexImage2D(target, level, tv, w, h, unused, tv, type, pixels); + } + } + + public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused, + int format, int type, IntBuffer pixels) { + if(glesVers >= 300) { + _wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels); + }else { + int tv = TextureFormatHelper.trivializeInternalFormatToGLES20(internalFormat); + _wglTexImage2D(target, level, tv, w, h, unused, tv, type, pixels); + } + } + + public static final void glTexSubImage2D(int target, int level, int x, int y, int w, int h, int format, + int type, IntBuffer pixels) { + _wglTexSubImage2D(target, level, x, y, w, h, format, type, pixels); + } + + public static final void glTexStorage2D(int target, int levels, int internalFormat, int w, int h) { + if(texStorageCapable && (glesVers >= 300 || levels == 1 || (MathHelper.calculateLogBaseTwo(Math.max(w, h)) + 1) == levels)) { + _wglTexStorage2D(target, levels, internalFormat, w, h); + }else { + int tv = TextureFormatHelper.trivializeInternalFormatToGLES20(internalFormat); + int type = TextureFormatHelper.getTypeFromInternal(internalFormat); + for(int i = 0; i < levels; ++i) { + _wglTexImage2D(target, i, tv, Math.max(w >> i, 1), Math.max(h >> i, 1), 0, tv, type, (ByteBuffer)null); + } + } + } + + public static final void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer buffer) { + switch(type) { + case GL_FLOAT: + _wglReadPixels(x, y, width, height, format, GL_FLOAT, buffer.asFloatBuffer()); + break; + case 0x140B: // GL_HALF_FLOAT + _wglReadPixels_u16(x, y, width, height, format, glesVers == 200 ? 0x8D61 : 0x140B, buffer); + break; + case GL_UNSIGNED_BYTE: + default: + _wglReadPixels(x, y, width, height, format, type, buffer); + break; + } + } + + public static final void glLineWidth(float f) { + _wglLineWidth(f); + } + + public static final void glFog(int param, FloatBuffer valueBuffer) { + int pos = valueBuffer.position(); + switch(param) { + case GL_FOG_COLOR: + GlStateManager.stateFogColorR = valueBuffer.get(); + GlStateManager.stateFogColorG = valueBuffer.get(); + GlStateManager.stateFogColorB = valueBuffer.get(); + GlStateManager.stateFogColorA = valueBuffer.get(); + ++GlStateManager.stateFogSerial; + break; + default: + throw new UnsupportedOperationException("Only GL_FOG_COLOR is configurable!"); + } + valueBuffer.position(pos); + } + + public static final void glFogi(int param, int value) { + // I'm not sure what this is for currently + } + + public static final int glGenLists() { + return mapDisplayListsGL.register(new DisplayList()); + } + + public static final void glDeleteLists(int id) { + DisplayList d = mapDisplayListsGL.free(id); + if(d != null) { + if(d.vertexArray != null) { + EaglercraftGPU.destroyGLBufferArray(d.vertexArray); + } + if(d.vertexBuffer != null) { + _wglDeleteBuffers(d.vertexBuffer); + } + } + } + + public static final int glGetError() { + return _wglGetError(); + } + + public static final void glBlendEquation(int equation) { + if(equation != GlStateManager.stateBlendEquation) { + _wglBlendEquation(equation); + GlStateManager.stateBlendEquation = equation; + } + } + + public static final boolean areVAOsEmulated() { + return emulatedVAOs; + } + + public static final IBufferArrayGL createGLBufferArray() { + if(emulatedVAOs) { + return new SoftGLBufferArray(); + }else { + return _wglGenVertexArrays(); + } + } + + public static final void destroyGLBufferArray(IBufferArrayGL buffer) { + if(!emulatedVAOs) { + _wglDeleteVertexArrays(buffer); + } + } + + public static final void enableVertexAttribArray(int index) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping enable attrib with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).enableAttrib(index, true); + }else { + _wglEnableVertexAttribArray(index); + } + } + + public static final void disableVertexAttribArray(int index) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping disable attrib with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).enableAttrib(index, false); + }else { + _wglDisableVertexAttribArray(index); + } + } + + public static final void vertexAttribPointer(int index, int size, int format, boolean normalized, int stride, int offset) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping vertexAttribPointer with emulated VAO because no known VAO is bound!"); + return; + } + if(currentVAOArrayBuffer == null) { + logger.warn("Skipping vertexAttribPointer with emulated VAO because no VAO array buffer is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).setAttrib(currentVAOArrayBuffer, index, size, format, normalized, stride, offset); + }else { + _wglVertexAttribPointer(index, size, format, normalized, stride, offset); + } + } + + public static final void vertexAttribDivisor(int index, int divisor) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping vertexAttribPointer with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).setAttribDivisor(index, divisor); + }else { + _wglVertexAttribDivisor(index, divisor); + } + } + + public static final void doDrawArrays(int mode, int first, int count) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping draw call with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, false); + } + _wglDrawArrays(mode, first, count); + } + + public static final void doDrawElements(int mode, int count, int type, int offset) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping draw call with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, true); + } + _wglDrawElements(mode, count, type, offset); + } + + public static final void doDrawArraysInstanced(int mode, int first, int count, int instances) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping instanced draw call with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, false); + } + _wglDrawArraysInstanced(mode, first, count, instances); + } + + public static final void doDrawElementsInstanced(int mode, int count, int type, int offset, int instances) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping instanced draw call with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, true); + } + _wglDrawElementsInstanced(mode, count, type, offset, instances); + } + + static IBufferArrayGL currentBufferArray = null; + + public static final void bindGLBufferArray(IBufferArrayGL buffer) { + if(emulatedVAOs) { + currentBufferArray = buffer; + }else { + if(currentBufferArray != buffer) { + _wglBindVertexArray(buffer); + currentBufferArray = buffer; + } + } + } + + static IBufferGL currentArrayBuffer = null; + + // only used when VAOs are emulated + static IBufferGL currentVAOArrayBuffer = null; + + public static final void bindVAOGLArrayBuffer(IBufferGL buffer) { + if(emulatedVAOs) { + currentVAOArrayBuffer = buffer; + }else { + if(currentArrayBuffer != buffer) { + _wglBindBuffer(GL_ARRAY_BUFFER, buffer); + currentArrayBuffer = buffer; + } + } + } + + public static final void bindVAOGLArrayBufferNow(IBufferGL buffer) { + if(emulatedVAOs) { + currentVAOArrayBuffer = buffer; + } + if(currentArrayBuffer != buffer) { + _wglBindBuffer(GL_ARRAY_BUFFER, buffer); + currentArrayBuffer = buffer; + } + } + + public static final void bindVAOGLElementArrayBuffer(IBufferGL buffer) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping set element array buffer with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).setIndexBuffer(buffer); + }else { + _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + } + } + + static final void bindVAOGLElementArrayBufferNow(IBufferGL buffer) { + if(emulatedVAOs) { + if(currentBufferArray == null) { + logger.warn("Skipping set element array buffer with emulated VAO because no known VAO is bound!"); + return; + } + ((SoftGLBufferArray)currentBufferArray).setIndexBuffer(buffer); + if(currentEmulatedVAOIndexBuffer != buffer) { + _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + currentEmulatedVAOIndexBuffer = buffer; + } + }else { + _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + } + } + + static IBufferGL currentEmulatedVAOIndexBuffer = null; + + static final void bindEmulatedVAOIndexBuffer(IBufferGL buffer) { + if(currentEmulatedVAOIndexBuffer != buffer) { + _wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + currentEmulatedVAOIndexBuffer = buffer; + } + } + + public static final void bindGLArrayBuffer(IBufferGL buffer) { + if(currentArrayBuffer != buffer) { + _wglBindBuffer(GL_ARRAY_BUFFER, buffer); + currentArrayBuffer = buffer; + } + } + + static IBufferGL currentUniformBuffer = null; + + public static final void bindGLUniformBuffer(IBufferGL buffer) { + if(currentUniformBuffer != buffer) { + _wglBindBuffer(0x8A11, buffer); + currentUniformBuffer = buffer; + } + } + + static IProgramGL currentShaderProgram = null; + + public static final void bindGLShaderProgram(IProgramGL prog) { + if(currentShaderProgram != prog) { + _wglUseProgram(prog); + currentShaderProgram = prog; + } + } + + private static final IBufferGL[] currentUniformBlockBindings = new IBufferGL[16]; + private static final int[] currentUniformBlockBindingOffset = new int[16]; + private static final int[] currentUniformBlockBindingSize = new int[16]; + + public static final void bindUniformBufferRange(int index, IBufferGL buffer, int offset, int size) { + if(currentUniformBlockBindings[index] != buffer || currentUniformBlockBindingOffset[index] != offset + || currentUniformBlockBindingSize[index] != size) { + _wglBindBufferRange(0x8A11, index, buffer, offset, size); + currentUniformBlockBindings[index] = buffer; + currentUniformBlockBindingOffset[index] = offset; + currentUniformBlockBindingSize[index] = size; + } + } + + public static final int CLEAR_BINDING_TEXTURE = 1; + public static final int CLEAR_BINDING_TEXTURE0 = 2; + public static final int CLEAR_BINDING_ACTIVE_TEXTURE = 4; + public static final int CLEAR_BINDING_BUFFER_ARRAY = 8; + public static final int CLEAR_BINDING_ARRAY_BUFFER = 16; + public static final int CLEAR_BINDING_SHADER_PROGRAM = 32; + + public static final void clearCurrentBinding(int mask) { + if((mask & CLEAR_BINDING_TEXTURE) != 0) { + int[] i = GlStateManager.boundTexture; + for(int j = 0; j < i.length; ++j) { + i[j] = -1; + } + } + if((mask & CLEAR_BINDING_TEXTURE0) != 0) { + GlStateManager.boundTexture[0] = -1; + } + if((mask & CLEAR_BINDING_ACTIVE_TEXTURE) != 0) { + GlStateManager.activeTexture = 0; + _wglActiveTexture(GL_TEXTURE0); + } + if((mask & CLEAR_BINDING_BUFFER_ARRAY) != 0) { + currentBufferArray = null; + } + if((mask & CLEAR_BINDING_ARRAY_BUFFER) != 0) { + currentArrayBuffer = currentVAOArrayBuffer = null; + } + if((mask & CLEAR_BINDING_SHADER_PROGRAM) != 0) { + currentShaderProgram = null; + } + } + + public static final int ATTRIB_TEXTURE = 1; + public static final int ATTRIB_COLOR = 2; + public static final int ATTRIB_NORMAL = 4; + public static final int ATTRIB_LIGHTMAP = 8; + + public static final void renderBuffer(ByteBuffer buffer, int attrib, int mode, int count) { + if(currentList != null) { + if(currentList.attribs == -1) { + currentList.attribs = attrib; + }else if(currentList.attribs != attrib) { + throw new UnsupportedOperationException("Inconsistent vertex format in display list (only one is allowed)"); + } + if(currentList.mode == -1) { + currentList.mode = mode; + }else if(currentList.mode != mode) { + throw new UnsupportedOperationException("Inconsistent draw mode in display list (only one is allowed)"); + } + currentList.count += count; + if(buffer.remaining() > displayListBuffer.remaining()) { + growDisplayListBuffer(buffer.remaining()); + } + displayListBuffer.put(buffer); + lastRender = null; + }else { + lastRender = FixedFunctionPipeline.setupDirect(buffer, attrib).update(); + lastRender.drawDirectArrays(mode, 0, count); + lastMode = mode; + lastCount = count; + } + } + + public static final void optimize() { + FixedFunctionPipeline.optimize(); + } + + private static FixedFunctionPipeline lastRender = null; + private static int lastMode = 0; + private static int lastCount = 0; + + public static final void renderAgain() { + if(lastRender == null) { + throw new UnsupportedOperationException("Cannot render the same verticies twice while generating display list"); + } + EaglercraftGPU.bindGLBufferArray(lastRender.getDirectModeBufferArray()); + lastRender.update().drawDirectArrays(lastMode, 0, lastCount); + } + + private static IBufferGL quad16EmulationBuffer = null; + private static int quad16EmulationBufferSize = 0; + + private static IBufferGL quad32EmulationBuffer = null; + private static int quad32EmulationBufferSize = 0; + + public static final void attachQuad16EmulationBuffer(int vertexCount, boolean bind) { + IBufferGL buf = quad16EmulationBuffer; + if(buf == null) { + quad16EmulationBuffer = buf = _wglGenBuffers(); + int newSize = quad16EmulationBufferSize = (vertexCount & 0xFFFFF000) + 0x2000; + if(newSize > 0xFFFF) { + newSize = 0xFFFF; + } + EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); + resizeQuad16EmulationBuffer(newSize >> 2); + }else { + int cnt = quad16EmulationBufferSize; + if(cnt < vertexCount) { + int newSize = quad16EmulationBufferSize = (vertexCount & 0xFFFFF000) + 0x2000; + if(newSize > 0xFFFF) { + newSize = 0xFFFF; + } + EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); + resizeQuad16EmulationBuffer(newSize >> 2); + }else if(bind) { + EaglercraftGPU.bindVAOGLElementArrayBuffer(buf); + } + } + } + + public static final void attachQuad32EmulationBuffer(int vertexCount, boolean bind) { + IBufferGL buf = quad32EmulationBuffer; + if(buf == null) { + quad32EmulationBuffer = buf = _wglGenBuffers(); + int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000; + EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); + resizeQuad32EmulationBuffer(newSize >> 2); + }else { + int cnt = quad32EmulationBufferSize; + if(cnt < vertexCount) { + int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000; + EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); + resizeQuad32EmulationBuffer(newSize >> 2); + }else if(bind) { + EaglercraftGPU.bindVAOGLElementArrayBuffer(buf); + } + } + } + + private static final void resizeQuad16EmulationBuffer(int quadCount) { + IntBuffer buf = EagRuntime.allocateIntBuffer(quadCount * 3); + int v1, v2, v3, v4; + for(int i = 0; i < quadCount; ++i) { + v1 = i << 2; + v2 = v1 + 1; + v3 = v2 + 1; + v4 = v3 + 1; + buf.put(v1 | (v2 << 16)); + buf.put(v4 | (v2 << 16)); + buf.put(v3 | (v4 << 16)); + } + buf.flip(); + _wglBufferData(GL_ELEMENT_ARRAY_BUFFER, buf, GL_STATIC_DRAW); + EagRuntime.freeIntBuffer(buf); + } + + private static final void resizeQuad32EmulationBuffer(int quadCount) { + IntBuffer buf = EagRuntime.allocateIntBuffer(quadCount * 6); + int v1, v2, v3, v4; + for(int i = 0; i < quadCount; ++i) { + v1 = i << 2; + v2 = v1 + 1; + v3 = v2 + 1; + v4 = v3 + 1; + buf.put(v1); buf.put(v2); + buf.put(v4); buf.put(v2); + buf.put(v3); buf.put(v4); + } + buf.flip(); + _wglBufferData(GL_ELEMENT_ARRAY_BUFFER, buf, GL_STATIC_DRAW); + EagRuntime.freeIntBuffer(buf); + } + + public static final ITextureGL getNativeTexture(int tex) { + return mapTexturesGL.get(tex); + } + + public static final void regenerateTexture(int tex) { + ITextureGL webglTex = mapTexturesGL.get(tex); + if(webglTex != null) { + GlStateManager.unbindTextureIfCached(tex); + _wglDeleteTextures(webglTex); + mapTexturesGL.set(tex, _wglGenTextures()); + }else { + logger.error("Tried to regenerate a missing texture!"); + } + } + + public static final void drawHighPoly(HighPolyMesh mesh) { + if(mesh.vertexCount == 0 || mesh.indexCount == 0 || mesh.vertexArray == null) { + return; + } + FixedFunctionPipeline p = FixedFunctionPipeline.setupRenderDisplayList(mesh.getAttribBits()).update(); + EaglercraftGPU.bindGLBufferArray(mesh.vertexArray); + p.drawElements(GL_TRIANGLES, mesh.indexCount, GL_UNSIGNED_SHORT, 0); + } + + static int glesVers = -1; + static boolean hasFramebufferHDR16FSupport = false; + static boolean hasFramebufferHDR32FSupport = false; + static boolean hasLinearHDR16FSupport = false; + static boolean hasLinearHDR32FSupport = false; + static boolean fboRenderMipmapCapable = false; + static boolean vertexArrayCapable = false; + static boolean instancingCapable = false; + static boolean texStorageCapable = false; + static boolean textureLODCapable = false; + static boolean shader5Capable = false; + static boolean npotCapable = false; + static int uniformBufferOffsetAlignment = -1; + + public static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, boolean allow32bitFallback) { + createFramebufferHDR16FTexture(target, level, w, h, format, allow32bitFallback, null); + } + + public static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, ByteBuffer pixelData) { + createFramebufferHDR16FTexture(target, level, w, h, format, false, pixelData); + } + + private static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, boolean allow32bitFallback, ByteBuffer pixelData) { + if(hasFramebufferHDR16FSupport) { + int internalFormat; + switch(format) { + case GL_RED: + if(glesVers == 200) { + format = GL_LUMINANCE; + internalFormat = GL_LUMINANCE; + }else { + internalFormat = glesVers == 200 ? GL_LUMINANCE : 0x822D; // GL_R16F + } + break; + case 0x8227: // GL_RG + internalFormat = glesVers == 200 ? 0x8227 : 0x822F; // GL_RG16F + case GL_RGB: + throw new UnsupportedOperationException("GL_RGB16F isn't supported specifically in WebGL 2.0 for some goddamn reason"); + case GL_RGBA: + internalFormat = glesVers == 200 ? GL_RGBA : 0x881A; // GL_RGBA16F + break; + default: + throw new UnsupportedOperationException("Unknown format: " + format); + } + _wglTexImage2Du16(target, level, internalFormat, w, h, 0, format, glesVers == 200 ? 0x8D61 : 0x140B, pixelData); + }else { + if(allow32bitFallback) { + if(hasFramebufferHDR32FSupport) { + createFramebufferHDR32FTexture(target, level, w, h, format, false, null); + }else { + throw new UnsupportedOperationException("No fallback 32-bit HDR (floating point) texture support is available on this device"); + } + }else { + throw new UnsupportedOperationException("16-bit HDR (floating point) textures are not supported on this device"); + } + } + } + + public static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, boolean allow16bitFallback) { + createFramebufferHDR32FTexture(target, level, w, h, format, allow16bitFallback, null); + } + + public static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, ByteBuffer pixelData) { + createFramebufferHDR32FTexture(target, level, w, h, format, false, pixelData); + } + + private static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, boolean allow16bitFallback, ByteBuffer pixelData) { + if(hasFramebufferHDR32FSupport) { + int internalFormat; + switch(format) { + case GL_RED: + internalFormat = 0x822E; // GL_R32F + break; + case 0x8227: // GL_RG + internalFormat = 0x8230; // GL_RG32F + case GL_RGB: + throw new UnsupportedOperationException("GL_RGB32F isn't supported specifically in WebGL 2.0 for some goddamn reason"); + case GL_RGBA: + internalFormat = 0x8814; //GL_RGBA32F + break; + default: + throw new UnsupportedOperationException("Unknown format: " + format); + } + _wglTexImage2Df32(target, level, internalFormat, w, h, 0, format, GL_FLOAT, pixelData); + }else { + if(allow16bitFallback) { + if(hasFramebufferHDR16FSupport) { + createFramebufferHDR16FTexture(target, level, w, h, format, false); + }else { + throw new UnsupportedOperationException("No fallback 16-bit HDR (floating point) texture support is available on this device"); + } + }else { + throw new UnsupportedOperationException("32-bit HDR (floating point) textures are not supported on this device"); + } + } + } + + public static final void warmUpCache() { + EaglercraftGPU.glGetString(7936); + EaglercraftGPU.glGetString(7937); + EaglercraftGPU.glGetString(7938); + glesVers = PlatformOpenGL.checkOpenGLESVersion(); + vertexArrayCapable = PlatformOpenGL.checkVAOCapable(); + emulatedVAOs = !vertexArrayCapable; + fboRenderMipmapCapable = PlatformOpenGL.checkFBORenderMipmapCapable(); + instancingCapable = PlatformOpenGL.checkInstancingCapable(); + texStorageCapable = PlatformOpenGL.checkTexStorageCapable(); + textureLODCapable = PlatformOpenGL.checkTextureLODCapable(); + shader5Capable = PlatformOpenGL.checkOESGPUShader5Capable() || PlatformOpenGL.checkEXTGPUShader5Capable(); + npotCapable = PlatformOpenGL.checkNPOTCapable(); + uniformBufferOffsetAlignment = glesVers >= 300 ? _wglGetInteger(0x8A34) : -1; + if(!npotCapable) { + logger.warn("NPOT texture support detected as false, texture wrapping must be set to GL_CLAMP_TO_EDGE if the texture's width or height is not a power of 2"); + } + hasFramebufferHDR16FSupport = PlatformOpenGL.checkHDRFramebufferSupport(16); + if(hasFramebufferHDR16FSupport) { + logger.info("16-bit HDR render target support: true"); + }else { + logger.error("16-bit HDR render target support: false"); + } + hasLinearHDR16FSupport = PlatformOpenGL.checkLinearHDRFilteringSupport(16); + if(hasLinearHDR16FSupport) { + logger.info("16-bit HDR linear filter support: true"); + }else { + logger.error("16-bit HDR linear filter support: false"); + } + hasFramebufferHDR32FSupport = PlatformOpenGL.checkHDRFramebufferSupport(32); + if(hasFramebufferHDR32FSupport) { + logger.info("32-bit HDR render target support: true"); + }else { + logger.error("32-bit HDR render target support: false"); + } + hasLinearHDR32FSupport = PlatformOpenGL.checkLinearHDRFilteringSupport(32); + if(hasLinearHDR32FSupport) { + logger.info("32-bit HDR linear filter support: true"); + }else { + logger.error("32-bit HDR linear filter support: false"); + } + if(!checkHasHDRFramebufferSupportWithFilter()) { + logger.error("No HDR render target support was detected! Shaders will be disabled."); + } + if(emulatedVAOs) { + logger.info("Note: Could not unlock VAOs via OpenGL extensions, emulating them instead"); + } + if(!instancingCapable) { + logger.info("Note: Could not unlock instancing via OpenGL extensions, using slow vanilla font and particle rendering"); + } + emulatedVAOState = emulatedVAOs ? new SoftGLBufferState() : null; + PlatformOpenGL.enterVAOEmulationHook(); + GLSLHeader.init(); + DrawUtils.init(); + SpriteLevelMixer.initialize(); + if(instancingCapable) { + InstancedFontRenderer.initialize(); + InstancedParticleRenderer.initialize(); + } + EffectPipelineFXAA.initialize(); + TextureCopyUtil.initialize(); + DrawUtils.vshLocal.free(); + DrawUtils.vshLocal = null; + } + + public static final void destroyCache() { + stringCache.clear(); + mapTexturesGL.clear(); + mapQueriesGL.clear(); + mapDisplayListsGL.clear(); + emulatedVAOs = false; + emulatedVAOState = null; + glesVers = -1; + fboRenderMipmapCapable = false; + vertexArrayCapable = false; + instancingCapable = false; + hasFramebufferHDR16FSupport = false; + hasFramebufferHDR32FSupport = false; + hasLinearHDR32FSupport = false; + GLSLHeader.destroy(); + DrawUtils.destroy(); + SpriteLevelMixer.destroy(); + InstancedFontRenderer.destroy(); + InstancedParticleRenderer.destroy(); + EffectPipelineFXAA.destroy(); + TextureCopyUtil.destroy(); + } + + public static final int checkOpenGLESVersion() { + return glesVers; + } + + public static final boolean checkFBORenderMipmapCapable() { + return fboRenderMipmapCapable; + } + + public static final boolean checkVAOCapable() { + return vertexArrayCapable; + } + + public static final boolean checkInstancingCapable() { + return instancingCapable; + } + + public static final boolean checkTexStorageCapable() { + return texStorageCapable; + } + + public static final boolean checkTextureLODCapable() { + return textureLODCapable; + } + + public static final boolean checkShader5Capable() { + return shader5Capable; + } + + public static final boolean checkNPOTCapable() { + return npotCapable; + } + + public static final int getUniformBufferOffsetAlignment() { + return uniformBufferOffsetAlignment; + } + + public static final boolean checkHDRFramebufferSupport(int bits) { + switch(bits) { + case 16: + return hasFramebufferHDR16FSupport; + case 32: + return hasFramebufferHDR32FSupport; + default: + return false; + } + } + + public static final boolean checkLinearHDRFilteringSupport(int bits) { + switch(bits) { + case 16: + return hasLinearHDR16FSupport; + case 32: + return hasLinearHDR32FSupport; + default: + return false; + } + } + + public static final boolean checkHasHDRFramebufferSupport() { + return hasFramebufferHDR16FSupport || hasFramebufferHDR32FSupport; + } + + public static final boolean checkHasHDRFramebufferSupportWithFilter() { + return (hasFramebufferHDR16FSupport && hasLinearHDR16FSupport) || (hasFramebufferHDR32FSupport && hasLinearHDR32FSupport); + } + + //legacy + public static final boolean checkLinearHDR32FSupport() { + return hasLinearHDR32FSupport; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java index f61fe14..f3f8421 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java @@ -1,158 +1,180 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EffectPipelineFXAA { - - private static final Logger logger = LogManager.getLogger("EffectPipelineFXAA"); - - public static final String fragmentShaderPath = "/assets/eagler/glsl/post_fxaa.fsh"; - - private static final int _GL_FRAMEBUFFER = 0x8D40; - private static final int _GL_RENDERBUFFER = 0x8D41; - private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; - private static final int _GL_DEPTH_ATTACHMENT = 0x8D00; - private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC; - - private static IProgramGL shaderProgram = null; - private static IUniformGL u_screenSize2f = null; - - private static IFramebufferGL framebuffer = null; - private static int framebufferColor = -1; - private static IRenderbufferGL framebufferDepth = null; - - private static int currentWidth = -1; - private static int currentHeight = -1; - - static void initialize() { - String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath); - if(fragmentSource == null) { - throw new RuntimeException("EffectPipelineFXAA shader \"" + fragmentShaderPath + "\" is missing!"); - } - - IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); - - _wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource); - _wglCompileShader(frag); - - if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { - logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for EffectPipelineFXAA!"); - String log = _wglGetShaderInfoLog(frag); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - logger.error("[FRAG] {}", lines[i]); - } - } - throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); - } - - shaderProgram = _wglCreateProgram(); - - _wglAttachShader(shaderProgram, DrawUtils.vshLocal); - _wglAttachShader(shaderProgram, frag); - - _wglLinkProgram(shaderProgram); - - _wglDetachShader(shaderProgram, DrawUtils.vshLocal); - _wglDetachShader(shaderProgram, frag); - - _wglDeleteShader(frag); - - if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { - logger.error("Failed to link shader program for EffectPipelineFXAA!"); - String log = _wglGetProgramInfoLog(shaderProgram); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - logger.error("[LINK] {}", lines[i]); - } - } - throw new IllegalStateException("Shader program for EffectPipelineFXAA could not be linked!"); - } - - u_screenSize2f = _wglGetUniformLocation(shaderProgram, "u_screenSize2f"); - - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_screenTexture"), 0); - - framebuffer = _wglCreateFramebuffer(); - framebufferColor = GlStateManager.generateTexture(); - - GlStateManager.bindTexture(framebufferColor); - - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - framebufferDepth = _wglCreateRenderbuffer(); - _wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth); - - _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(framebufferColor), 0); - _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, framebufferDepth); - - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - } - - public static void begin(int width, int height) { - if(currentWidth != width || currentHeight != height) { - currentWidth = width; - currentHeight = height; - - GlStateManager.bindTexture(framebufferColor); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); - - _wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth); - _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT32F, width, height); - } - - _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); - - GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); - GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - } - - public static void end() { - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - GlStateManager.bindTexture(framebufferColor); - - _wglUniform2f(u_screenSize2f, 1.0f / currentWidth, 1.0f / currentHeight); - - DrawUtils.drawStandardQuad2D(); - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + +/** + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EffectPipelineFXAA { + + private static final Logger logger = LogManager.getLogger("EffectPipelineFXAA"); + + public static final String fragmentShaderPath = "/assets/eagler/glsl/post_fxaa.fsh"; + public static final String fragmentShaderPrecision = "precision lowp int;\nprecision mediump float;\nprecision mediump sampler2D;\n"; + + private static final int _GL_FRAMEBUFFER = 0x8D40; + private static final int _GL_RENDERBUFFER = 0x8D41; + private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; + private static final int _GL_DEPTH_ATTACHMENT = 0x8D00; + private static final int _GL_DEPTH_COMPONENT16 = 0x81A5; + private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC; + + private static IProgramGL shaderProgram = null; + private static IUniformGL u_screenSize2f = null; + + private static IFramebufferGL framebuffer = null; + private static int framebufferColor = -1; + private static IRenderbufferGL framebufferDepth = null; + + private static int currentWidth = -1; + private static int currentHeight = -1; + + static void initialize() { + String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath); + + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + + _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision)); + _wglCompileShader(frag); + + if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for EffectPipelineFXAA!"); + String log = _wglGetShaderInfoLog(frag); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + logger.error("[FRAG] {}", lines[i]); + } + } + throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); + } + + shaderProgram = _wglCreateProgram(); + + _wglAttachShader(shaderProgram, DrawUtils.vshLocal); + _wglAttachShader(shaderProgram, frag); + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + VSHInputLayoutParser.applyLayout(shaderProgram, DrawUtils.vshLocalLayout); + } + + _wglLinkProgram(shaderProgram); + + _wglDetachShader(shaderProgram, DrawUtils.vshLocal); + _wglDetachShader(shaderProgram, frag); + + _wglDeleteShader(frag); + + if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + logger.error("Failed to link shader program for EffectPipelineFXAA!"); + String log = _wglGetProgramInfoLog(shaderProgram); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + logger.error("[LINK] {}", lines[i]); + } + } + throw new IllegalStateException("Shader program for EffectPipelineFXAA could not be linked!"); + } + + u_screenSize2f = _wglGetUniformLocation(shaderProgram, "u_screenSize2f"); + + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_screenTexture"), 0); + + framebuffer = _wglCreateFramebuffer(); + framebufferColor = GlStateManager.generateTexture(); + + GlStateManager.bindTexture(framebufferColor); + + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + framebufferDepth = _wglCreateRenderbuffer(); + _wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth); + + _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(framebufferColor), 0); + _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, framebufferDepth); + + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + } + + public static void begin(int width, int height) { + if(currentWidth != width || currentHeight != height) { + currentWidth = width; + currentHeight = height; + + GlStateManager.bindTexture(framebufferColor); + EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + + _wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth); + _wglRenderbufferStorage(_GL_RENDERBUFFER, EaglercraftGPU.checkOpenGLESVersion() == 200 ? _GL_DEPTH_COMPONENT16 : _GL_DEPTH_COMPONENT32F, width, height); + } + + _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + + GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + } + + public static void end() { + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + GlStateManager.bindTexture(framebufferColor); + + _wglUniform2f(u_screenSize2f, 1.0f / currentWidth, 1.0f / currentHeight); + + DrawUtils.drawStandardQuad2D(); + } + + public static void destroy() { + if(shaderProgram != null) { + _wglDeleteProgram(shaderProgram); + shaderProgram = null; + } + u_screenSize2f = null; + if(framebuffer != null) { + _wglDeleteFramebuffer(framebuffer); + framebuffer = null; + } + if(framebufferColor != -1) { + GlStateManager.deleteTexture(framebufferColor); + framebufferColor = -2; + } + if(framebufferDepth != null) { + _wglDeleteRenderbuffer(framebufferDepth); + framebufferDepth = null; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java index 035cafc..8b65c2e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java @@ -30,27 +30,20 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunc /** * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class FixedFunctionPipeline { - + private static final Logger LOGGER = LogManager.getLogger("FixedFunctionPipeline"); static final int getFragmentState() { @@ -58,284 +51,268 @@ public class FixedFunctionPipeline { (GlStateManager.stateTexture[1] ? STATE_ENABLE_LIGHTMAP : 0) | (GlStateManager.stateAlphaTest ? STATE_ENABLE_ALPHA_TEST : 0) | ((GlStateManager.stateLighting && GlStateManager.stateMaterial) - ? STATE_ENABLE_MC_LIGHTING - : 0) - | + ? STATE_ENABLE_MC_LIGHTING : 0) | ((GlStateManager.stateTexture[0] && GlStateManager.stateTexGen) - ? STATE_ENABLE_END_PORTAL - : 0) - | + ? STATE_ENABLE_END_PORTAL : 0) | /* TODO: (GlStateManager.??? ? STATE_ENABLE_ANISOTROPIC_FIX : 0) | */ ((GlStateManager.stateFog && GlStateManager.stateFogDensity > 0.0f) - ? STATE_ENABLE_FOG - : 0) - | + ? STATE_ENABLE_FOG : 0) | (GlStateManager.stateEnableShaderBlendColor ? STATE_ENABLE_BLEND_ADD : 0); } - + static FixedFunctionPipeline setupDirect(ByteBuffer buffer, int attrib) { FixedFunctionPipeline self; int baseState = attrib | getFragmentState(); - if (GlStateManager.stateUseExtensionPipeline) { - if (extensionProvider != null) { + if(GlStateManager.stateUseExtensionPipeline) { + if(extensionProvider != null) { self = getPipelineInstanceExt(baseState, extensionProvider.getCurrentExtensionStateBits(baseState)); - } else { + }else { throw new IllegalStateException("No extension pipeline is available!"); } - } else { + }else { self = getPipelineInstanceCore(baseState); } - + StreamBufferInstance sb = self.streamBuffer.getBuffer(buffer.remaining()); self.currentVertexArray = sb; - + EaglercraftGPU.bindGLBufferArray(sb.vertexArray); EaglercraftGPU.bindGLArrayBuffer(sb.vertexBuffer); - + _wglBufferSubData(GL_ARRAY_BUFFER, 0, buffer); - + return self; } - + static void setupDisplayList(DisplayList list) { FixedFunctionPipeline self; int baseState = list.attribs | getFragmentState(); - if (GlStateManager.stateUseExtensionPipeline) { - if (extensionProvider != null) { + if(GlStateManager.stateUseExtensionPipeline) { + if(extensionProvider != null) { self = getPipelineInstanceExt(baseState, extensionProvider.getCurrentExtensionStateBits(baseState)); - } else { + }else { throw new IllegalStateException("No extension pipeline is available!"); } - } else { + }else { self = getPipelineInstanceCore(baseState); } - + EaglercraftGPU.bindGLBufferArray(list.vertexArray); - EaglercraftGPU.bindGLArrayBuffer(list.vertexBuffer); - - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE, + EaglercraftGPU.bindVAOGLArrayBuffer(list.vertexBuffer); + + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE, VertexFormat.COMPONENT_POSITION_FORMAT, false, self.attribStride, 0); - if (self.attribTextureIndex != -1) { - _wglEnableVertexAttribArray(self.attribTextureIndex); - _wglVertexAttribPointer(self.attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE, + if(self.attribTextureIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(self.attribTextureIndex); + EaglercraftGPU.vertexAttribPointer(self.attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE, VertexFormat.COMPONENT_TEX_FORMAT, false, self.attribStride, self.attribTextureOffset); } - - if (self.attribColorIndex != -1) { - _wglEnableVertexAttribArray(self.attribColorIndex); - _wglVertexAttribPointer(self.attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE, + + if(self.attribColorIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(self.attribColorIndex); + EaglercraftGPU.vertexAttribPointer(self.attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE, VertexFormat.COMPONENT_COLOR_FORMAT, true, self.attribStride, self.attribColorOffset); } - - if (self.attribNormalIndex != -1) { - _wglEnableVertexAttribArray(self.attribNormalIndex); - _wglVertexAttribPointer(self.attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE, + + if(self.attribNormalIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(self.attribNormalIndex); + EaglercraftGPU.vertexAttribPointer(self.attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE, VertexFormat.COMPONENT_NORMAL_FORMAT, true, self.attribStride, self.attribNormalOffset); } - - if (self.attribLightmapIndex != -1) { - _wglEnableVertexAttribArray(self.attribLightmapIndex); - _wglVertexAttribPointer(self.attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE, + + if(self.attribLightmapIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(self.attribLightmapIndex); + EaglercraftGPU.vertexAttribPointer(self.attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE, VertexFormat.COMPONENT_LIGHTMAP_FORMAT, false, self.attribStride, self.attribLightmapOffset); } - + } - + static FixedFunctionPipeline setupRenderDisplayList(int attribs) { int baseState = attribs | getFragmentState(); - if (GlStateManager.stateUseExtensionPipeline) { - if (extensionProvider != null) { + if(GlStateManager.stateUseExtensionPipeline) { + if(extensionProvider != null) { return getPipelineInstanceExt(baseState, extensionProvider.getCurrentExtensionStateBits(baseState)); - } else { + }else { throw new IllegalStateException("No extension pipeline is available!"); } - } else { + }else { return getPipelineInstanceCore(baseState); } } - + void drawArrays(int mode, int offset, int count) { EaglercraftGPU.bindGLShaderProgram(shaderProgram); - PlatformOpenGL._wglDrawArrays(mode, offset, count); + EaglercraftGPU.doDrawArrays(mode, offset, count); } - + void drawDirectArrays(int mode, int offset, int count) { EaglercraftGPU.bindGLShaderProgram(shaderProgram); - if (mode == GL_QUADS) { + if(mode == GL_QUADS) { StreamBufferInstance sb = currentVertexArray; - if (count > 0xFFFF) { - if (!sb.bindQuad32) { + if(count > 0xFFFF) { + if(!sb.bindQuad32) { sb.bindQuad16 = false; sb.bindQuad32 = true; EaglercraftGPU.attachQuad32EmulationBuffer(count, true); - } else { + }else { EaglercraftGPU.attachQuad32EmulationBuffer(count, false); } - PlatformOpenGL._wglDrawElements(GL_TRIANGLES, count + (count >> 1), + EaglercraftGPU.doDrawElements(GL_TRIANGLES, count + (count >> 1), GL_UNSIGNED_INT, 0); - } else { - if (!sb.bindQuad16) { + }else { + if(!sb.bindQuad16) { sb.bindQuad16 = true; sb.bindQuad32 = false; EaglercraftGPU.attachQuad16EmulationBuffer(count, true); - } else { + }else { EaglercraftGPU.attachQuad16EmulationBuffer(count, false); } - PlatformOpenGL._wglDrawElements(GL_TRIANGLES, count + (count >> 1), + EaglercraftGPU.doDrawElements(GL_TRIANGLES, count + (count >> 1), GL_UNSIGNED_SHORT, 0); } - } else { - PlatformOpenGL._wglDrawArrays(mode, offset, count); + }else { + EaglercraftGPU.doDrawArrays(mode, offset, count); } } - + void drawElements(int mode, int count, int type, int offset) { EaglercraftGPU.bindGLShaderProgram(shaderProgram); - PlatformOpenGL._wglDrawElements(mode, count, type, offset); + EaglercraftGPU.doDrawElements(mode, count, type, offset); } - + private static IExtPipelineCompiler extensionProvider; - + public static void loadExtensionPipeline(IExtPipelineCompiler provider) { flushCache(); extensionProvider = provider; } - private static final FixedFunctionPipeline[] pipelineStateCache = new FixedFunctionPipeline[fixedFunctionStatesBits - + 1]; - private static final FixedFunctionPipeline[][] pipelineExtStateCache = new FixedFunctionPipeline[fixedFunctionStatesBits - + 1][]; - private static final List pipelineListTracker = new ArrayList(1024); + private static final FixedFunctionPipeline[] pipelineStateCache = new FixedFunctionPipeline[fixedFunctionStatesBits + 1]; + private static final FixedFunctionPipeline[][] pipelineExtStateCache = new FixedFunctionPipeline[fixedFunctionStatesBits + 1][]; + private static final List pipelineListTracker = new ArrayList<>(1024); private static String shaderSourceCacheVSH = null; private static String shaderSourceCacheFSH = null; - + private static FixedFunctionPipeline getPipelineInstanceCore(int bits) { FixedFunctionPipeline pp = pipelineStateCache[bits]; - if (pp == null) { + if(pp == null) { pipelineStateCache[bits] = pp = makeNewPipeline(bits, 0, false); } return pp; } - + private static FixedFunctionPipeline getPipelineInstanceExt(int coreBits, int extBits) { coreBits &= (15 | extensionProvider.getCoreStateMask(extBits)); FixedFunctionPipeline[] pp = pipelineExtStateCache[coreBits]; - if (pp == null) { - pipelineExtStateCache[coreBits] = pp = new FixedFunctionPipeline[1 << extensionProvider - .getExtensionStatesCount()]; + if(pp == null) { + pipelineExtStateCache[coreBits] = pp = new FixedFunctionPipeline[1 << extensionProvider.getExtensionStatesCount()]; return pp[extBits] = makeNewPipeline(coreBits, extBits, true); - } else { + }else { FixedFunctionPipeline ppp = pp[extBits]; - if (ppp == null) { + if(ppp == null) { pp[extBits] = ppp = makeNewPipeline(coreBits, extBits, true); } return ppp; } } - + private static FixedFunctionPipeline makeNewPipeline(int coreBits, int extBits, boolean enableExt) { String vshSource; String fshSource; - + Object[] extProviderUserPointer = null; - if (enableExt) { + if(enableExt) { extProviderUserPointer = new Object[1]; String[] extSource = extensionProvider.getShaderSource(coreBits, extBits, extProviderUserPointer); vshSource = extSource[0]; fshSource = extSource[1]; - } else { - if (shaderSourceCacheVSH == null) { - shaderSourceCacheVSH = EagRuntime.getResourceString(FILENAME_VSH); - if (shaderSourceCacheVSH == null) { - throw new RuntimeException("Could not load: " + FILENAME_VSH); - } + }else { + if(shaderSourceCacheVSH == null) { + shaderSourceCacheVSH = EagRuntime.getRequiredResourceString(FILENAME_VSH); } vshSource = shaderSourceCacheVSH; - if (shaderSourceCacheFSH == null) { - shaderSourceCacheFSH = EagRuntime.getResourceString(FILENAME_FSH); - if (shaderSourceCacheFSH == null) { - throw new RuntimeException("Could not load: " + FILENAME_FSH); - } + if(shaderSourceCacheFSH == null) { + shaderSourceCacheFSH = EagRuntime.getRequiredResourceString(FILENAME_FSH); } fshSource = shaderSourceCacheFSH; } - - StringBuilder macros = new StringBuilder(VERSION + "\n"); - if ((coreBits & STATE_HAS_ATTRIB_TEXTURE) != 0) { + + StringBuilder macros = new StringBuilder(); + if((coreBits & STATE_HAS_ATTRIB_TEXTURE) != 0) { macros.append("#define " + MACRO_ATTRIB_TEXTURE + "\n"); } - if ((coreBits & STATE_HAS_ATTRIB_COLOR) != 0) { + if((coreBits & STATE_HAS_ATTRIB_COLOR) != 0) { macros.append("#define " + MACRO_ATTRIB_COLOR + "\n"); } - if ((coreBits & STATE_HAS_ATTRIB_NORMAL) != 0) { + if((coreBits & STATE_HAS_ATTRIB_NORMAL) != 0) { macros.append("#define " + MACRO_ATTRIB_NORMAL + "\n"); } - if ((coreBits & STATE_HAS_ATTRIB_LIGHTMAP) != 0) { + if((coreBits & STATE_HAS_ATTRIB_LIGHTMAP) != 0) { macros.append("#define " + MACRO_ATTRIB_LIGHTMAP + "\n"); } - if ((coreBits & STATE_ENABLE_TEXTURE2D) != 0) { + if((coreBits & STATE_ENABLE_TEXTURE2D) != 0) { macros.append("#define " + MACRO_ENABLE_TEXTURE2D + "\n"); } - if ((coreBits & STATE_ENABLE_LIGHTMAP) != 0) { + if((coreBits & STATE_ENABLE_LIGHTMAP) != 0) { macros.append("#define " + MACRO_ENABLE_LIGHTMAP + "\n"); } - if ((coreBits & STATE_ENABLE_ALPHA_TEST) != 0) { + if((coreBits & STATE_ENABLE_ALPHA_TEST) != 0) { macros.append("#define " + MACRO_ENABLE_ALPHA_TEST + "\n"); } - if ((coreBits & STATE_ENABLE_MC_LIGHTING) != 0) { + if((coreBits & STATE_ENABLE_MC_LIGHTING) != 0) { macros.append("#define " + MACRO_ENABLE_MC_LIGHTING + "\n"); } - if ((coreBits & STATE_ENABLE_END_PORTAL) != 0) { + if((coreBits & STATE_ENABLE_END_PORTAL) != 0) { macros.append("#define " + MACRO_ENABLE_END_PORTAL + "\n"); } - if ((coreBits & STATE_ENABLE_ANISOTROPIC_FIX) != 0) { + if((coreBits & STATE_ENABLE_ANISOTROPIC_FIX) != 0) { macros.append("#define " + MACRO_ENABLE_ANISOTROPIC_FIX + "\n"); } - if ((coreBits & STATE_ENABLE_FOG) != 0) { + if((coreBits & STATE_ENABLE_FOG) != 0) { macros.append("#define " + MACRO_ENABLE_FOG + "\n"); } - if ((coreBits & STATE_ENABLE_BLEND_ADD) != 0) { + if((coreBits & STATE_ENABLE_BLEND_ADD) != 0) { macros.append("#define " + MACRO_ENABLE_BLEND_ADD + "\n"); } macros.append("precision " + PRECISION_INT + " int;\n"); macros.append("precision " + PRECISION_FLOAT + " float;\n"); macros.append("precision " + PRECISION_SAMPLER + " sampler2D;\n\n"); - + IShaderGL vsh = _wglCreateShader(GL_VERTEX_SHADER); - - _wglShaderSource(vsh, macros.toString() + vshSource); + + String macrosStr = macros.toString(); + _wglShaderSource(vsh, GLSLHeader.getVertexHeaderCompat(vshSource, macrosStr)); _wglCompileShader(vsh); - - if (_wglGetShaderi(vsh, GL_COMPILE_STATUS) != GL_TRUE) { - LOGGER.error("Failed to compile GL_VERTEX_SHADER for state {} !", - (visualizeBits(coreBits) + (enableExt && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); + + if(_wglGetShaderi(vsh, GL_COMPILE_STATUS) != GL_TRUE) { + LOGGER.error("Failed to compile GL_VERTEX_SHADER for state {} !", (visualizeBits(coreBits) + (enableExt && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); String log = _wglGetShaderInfoLog(vsh); - if (log != null) { + if(log != null) { String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for (int i = 0; i < lines.length; ++i) { + for(int i = 0; i < lines.length; ++i) { LOGGER.error("[VERT] {}", lines[i]); } } _wglDeleteShader(vsh); throw new IllegalStateException("Vertex shader could not be compiled!"); } - + IShaderGL fsh = _wglCreateShader(GL_FRAGMENT_SHADER); - - _wglShaderSource(fsh, macros.toString() + fshSource); + + _wglShaderSource(fsh, GLSLHeader.getFragmentHeaderCompat(fshSource, macrosStr)); _wglCompileShader(fsh); - - if (_wglGetShaderi(fsh, GL_COMPILE_STATUS) != GL_TRUE) { - LOGGER.error("Failed to compile GL_FRAGMENT_SHADER for state {} !", - (visualizeBits(coreBits) + (enableExt && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); + + if(_wglGetShaderi(fsh, GL_COMPILE_STATUS) != GL_TRUE) { + LOGGER.error("Failed to compile GL_FRAGMENT_SHADER for state {} !", (visualizeBits(coreBits) + (enableExt && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); String log = _wglGetShaderInfoLog(fsh); - if (log != null) { + if(log != null) { String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for (int i = 0; i < lines.length; ++i) { + for(int i = 0; i < lines.length; ++i) { LOGGER.error("[FRAG] {}", lines[i]); } } @@ -343,30 +320,30 @@ public class FixedFunctionPipeline { _wglDeleteShader(vsh); throw new IllegalStateException("Fragment shader could not be compiled!"); } - + IProgramGL prog = _wglCreateProgram(); _wglAttachShader(prog, vsh); _wglAttachShader(prog, fsh); - + FixedFunctionPipeline pp = null; IllegalStateException err = null; try { pp = new FixedFunctionPipeline(coreBits, extBits, prog); - } catch (IllegalStateException t) { + }catch(IllegalStateException t) { err = t; } - + _wglDetachShader(prog, vsh); _wglDetachShader(prog, fsh); _wglDeleteShader(fsh); _wglDeleteShader(vsh); - - if (err != null) { + + if(err != null) { _wglDeleteProgram(prog); throw err; - } else { - if (extProviderUserPointer != null) { + }else { + if(extProviderUserPointer != null) { pp.extensionPointer = extProviderUserPointer; extensionProvider.initializeNewShader(prog, pp.stateCoreBits, pp.stateExtBits, extProviderUserPointer); } @@ -376,21 +353,21 @@ public class FixedFunctionPipeline { } public static String visualizeBits(int i) { - if (i == 0) { + if(i == 0) { return "0"; } StringBuilder sb = new StringBuilder(); int j = 0, k = 0, l = 0; do { k = i & (1 << j); - if (k > 0) { - if (l++ > 0) { + if(k > 0) { + if(l++ > 0) { sb.append(' '); } sb.append(k); } ++j; - } while (i >= (1 << j)); + }while(i >= (1 << j)); return sb.toString(); } @@ -419,11 +396,11 @@ public class FixedFunctionPipeline { private final int attribNormalOffset; private final int attribLightmapIndex; private final int attribLightmapOffset; - + private final int attribStride; private final IProgramGL shaderProgram; - + private final IUniformGL stateColorUniform4f; private float stateColorR = -999.0f; private float stateColorG = -999.0f; @@ -457,7 +434,7 @@ public class FixedFunctionPipeline { private float stateLightingAmbientG = -999.0f; private float stateLightingAmbientB = -999.0f; private int stateLightingAmbientSerial = -1; - + private final IUniformGL stateNormalUniform3f; private float stateNormalX = -999.0f; private float stateNormalY = -999.0f; @@ -507,7 +484,7 @@ public class FixedFunctionPipeline { private final IUniformGL stateTextureMatrix01UniformMat4f; private final IUniformGL stateTextureMatrix02UniformMat4f; private final int[] stateTextureMatrixSerial = new int[8]; - + private final IUniformGL stateTextureCoords01Uniform2f; private final IUniformGL stateTextureCoords02Uniform2f; private final float[] stateTextureCoordsX = new float[8]; @@ -527,226 +504,223 @@ public class FixedFunctionPipeline { private FixedFunctionPipeline(int bits, int extBits, IProgramGL compiledProg) { shaderProgram = compiledProg; - + stateBits = bits; stateHasAttribTexture = (bits & STATE_HAS_ATTRIB_TEXTURE) != 0; stateHasAttribColor = (bits & STATE_HAS_ATTRIB_COLOR) != 0; stateHasAttribNormal = (bits & STATE_HAS_ATTRIB_NORMAL) != 0; stateHasAttribLightmap = (bits & STATE_HAS_ATTRIB_LIGHTMAP) != 0; - + stateCoreBits = bits; stateExtBits = extBits; - + int index = 0; int stride = 0; - + _wglBindAttribLocation(compiledProg, index, ATTRIB_POSITION); - + stride += VertexFormat.COMPONENT_POSITION_STRIDE; // vec3f - if (stateHasAttribColor) { + if(stateHasAttribColor) { attribColorIndex = ++index; attribColorOffset = stride; _wglBindAttribLocation(compiledProg, index, ATTRIB_COLOR); stride += VertexFormat.COMPONENT_COLOR_STRIDE; // vec4b - } else { + }else { attribColorIndex = -1; attribColorOffset = -1; } - if (stateHasAttribTexture) { + if(stateHasAttribTexture) { attribTextureIndex = ++index; attribTextureOffset = stride; _wglBindAttribLocation(compiledProg, index, ATTRIB_TEXTURE); stride += VertexFormat.COMPONENT_TEX_STRIDE; // vec2f - } else { + }else { attribTextureIndex = -1; attribTextureOffset = -1; } - if (stateHasAttribNormal) { + if(stateHasAttribNormal) { attribNormalIndex = ++index; attribNormalOffset = stride; _wglBindAttribLocation(compiledProg, index, ATTRIB_NORMAL); stride += VertexFormat.COMPONENT_NORMAL_STRIDE; // vec4b - } else { + }else { attribNormalIndex = -1; attribNormalOffset = -1; } - if (stateHasAttribLightmap) { + if(stateHasAttribLightmap) { attribLightmapIndex = ++index; attribLightmapOffset = stride; _wglBindAttribLocation(compiledProg, index, ATTRIB_LIGHTMAP); stride += VertexFormat.COMPONENT_LIGHTMAP_STRIDE; // vec2s - } else { + }else { attribLightmapIndex = -1; attribLightmapOffset = -1; } - + attribStride = stride; - + _wglLinkProgram(compiledProg); - - if (_wglGetProgrami(compiledProg, GL_LINK_STATUS) != GL_TRUE) { - LOGGER.error("Program could not be linked for state {} !", (visualizeBits(bits) - + (extensionProvider != null && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); + + if(_wglGetProgrami(compiledProg, GL_LINK_STATUS) != GL_TRUE) { + LOGGER.error("Program could not be linked for state {} !", (visualizeBits(bits) + (extensionProvider != null && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); String log = _wglGetProgramInfoLog(compiledProg); - if (log != null) { + if(log != null) { String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for (int i = 0; i < lines.length; ++i) { + for(int i = 0; i < lines.length; ++i) { LOGGER.error("[LINK] {}", lines[i]); } } throw new IllegalStateException("Program could not be linked!"); } - + streamBuffer = new StreamBuffer(FixedFunctionShader.initialSize, FixedFunctionShader.initialCount, FixedFunctionShader.maxCount, (vertexArray, vertexBuffer) -> { EaglercraftGPU.bindGLBufferArray(vertexArray); - EaglercraftGPU.bindGLArrayBuffer(vertexBuffer); + EaglercraftGPU.bindVAOGLArrayBuffer(vertexBuffer); - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE, + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE, VertexFormat.COMPONENT_POSITION_FORMAT, false, attribStride, 0); - if (attribTextureIndex != -1) { - _wglEnableVertexAttribArray(attribTextureIndex); - _wglVertexAttribPointer(attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE, + if(attribTextureIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(attribTextureIndex); + EaglercraftGPU.vertexAttribPointer(attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE, VertexFormat.COMPONENT_TEX_FORMAT, false, attribStride, attribTextureOffset); } - - if (attribColorIndex != -1) { - _wglEnableVertexAttribArray(attribColorIndex); - _wglVertexAttribPointer(attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE, + + if(attribColorIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(attribColorIndex); + EaglercraftGPU.vertexAttribPointer(attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE, VertexFormat.COMPONENT_COLOR_FORMAT, true, attribStride, attribColorOffset); } - - if (attribNormalIndex != -1) { - _wglEnableVertexAttribArray(attribNormalIndex); - _wglVertexAttribPointer(attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE, + + if(attribNormalIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(attribNormalIndex); + EaglercraftGPU.vertexAttribPointer(attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE, VertexFormat.COMPONENT_NORMAL_FORMAT, true, attribStride, attribNormalOffset); } - - if (attribLightmapIndex != -1) { - _wglEnableVertexAttribArray(attribLightmapIndex); - _wglVertexAttribPointer(attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE, + + if(attribLightmapIndex != -1) { + EaglercraftGPU.enableVertexAttribArray(attribLightmapIndex); + EaglercraftGPU.vertexAttribPointer(attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE, VertexFormat.COMPONENT_LIGHTMAP_FORMAT, false, attribStride, attribLightmapOffset); } }); - stateEnableTexture2D = (bits & STATE_ENABLE_TEXTURE2D) == STATE_ENABLE_TEXTURE2D; - stateEnableLightmap = (bits & STATE_ENABLE_LIGHTMAP) == STATE_ENABLE_LIGHTMAP; - stateEnableAlphaTest = (bits & STATE_ENABLE_ALPHA_TEST) == STATE_ENABLE_ALPHA_TEST; - stateEnableMCLighting = (bits & STATE_ENABLE_MC_LIGHTING) == STATE_ENABLE_MC_LIGHTING; - stateEnableEndPortal = (bits & STATE_ENABLE_END_PORTAL) == STATE_ENABLE_END_PORTAL; - stateEnableAnisotropicFix = (bits & STATE_ENABLE_ANISOTROPIC_FIX) == STATE_ENABLE_ANISOTROPIC_FIX; - stateEnableFog = (bits & STATE_ENABLE_FOG) == STATE_ENABLE_FOG; - stateEnableBlendAdd = (bits & STATE_ENABLE_BLEND_ADD) == STATE_ENABLE_BLEND_ADD; - - for (int i = 0; i < stateLightsVectors.length; ++i) { + stateEnableTexture2D = (bits & STATE_ENABLE_TEXTURE2D) != 0; + stateEnableLightmap = (bits & STATE_ENABLE_LIGHTMAP) != 0; + stateEnableAlphaTest = (bits & STATE_ENABLE_ALPHA_TEST) != 0; + stateEnableMCLighting = (bits & STATE_ENABLE_MC_LIGHTING) != 0; + stateEnableEndPortal = (bits & STATE_ENABLE_END_PORTAL) != 0; + stateEnableAnisotropicFix = (bits & STATE_ENABLE_ANISOTROPIC_FIX) != 0; + stateEnableFog = (bits & STATE_ENABLE_FOG) != 0; + stateEnableBlendAdd = (bits & STATE_ENABLE_BLEND_ADD) != 0; + + for(int i = 0; i < stateLightsVectors.length; ++i) { stateLightsVectors[i] = new Vector4f(-999.0f, -999.0f, -999.0f, 0.0f); } - - for (int i = 0; i < stateTextureMatrixSerial.length; ++i) { + + for(int i = 0; i < stateTextureMatrixSerial.length; ++i) { stateTextureMatrixSerial[i] = -1; } stateColorUniform4f = _wglGetUniformLocation(compiledProg, UNIFORM_COLOR_NAME); - + stateAlphaTestUniform1f = stateEnableAlphaTest ? _wglGetUniformLocation(compiledProg, UNIFORM_ALPHA_TEST_NAME) : null; - + stateLightsEnabledUniform1i = stateEnableMCLighting ? _wglGetUniformLocation(compiledProg, UNIFORM_LIGHTS_ENABLED_NAME) : null; - - if (stateEnableMCLighting) { - for (int i = 0; i < stateLightsVectorsArrayUniform4f.length; ++i) { - stateLightsVectorsArrayUniform4f[i] = _wglGetUniformLocation(compiledProg, + + if(stateEnableMCLighting) { + for(int i = 0; i < stateLightsVectorsArrayUniform4f.length; ++i) { + stateLightsVectorsArrayUniform4f[i] =_wglGetUniformLocation(compiledProg, UNIFORM_LIGHTS_VECTORS_NAME + "[" + i + "]"); } } - + stateLightingAmbientUniform3f = stateEnableMCLighting ? _wglGetUniformLocation(compiledProg, UNIFORM_LIGHTS_AMBIENT_NAME) : null; - + stateNormalUniform3f = (!stateHasAttribNormal && stateEnableMCLighting) ? _wglGetUniformLocation(compiledProg, UNIFORM_CONSTANT_NORMAL_NAME) : null; - + stateFogParamUniform4f = stateEnableFog ? _wglGetUniformLocation(compiledProg, UNIFORM_FOG_PARAM_NAME) : null; - + stateFogColorUniform4f = stateEnableFog ? _wglGetUniformLocation(compiledProg, UNIFORM_FOG_COLOR_NAME) : null; - + stateTexGenPlaneUniform4i = stateEnableEndPortal ? _wglGetUniformLocation(compiledProg, UNIFORM_TEX_GEN_PLANE_NAME) : null; - + stateTexGenSVectorUniform4f = stateEnableEndPortal ? _wglGetUniformLocation(compiledProg, UNIFORM_TEX_GEN_S_NAME) : null; - + stateTexGenTVectorUniform4f = stateEnableEndPortal ? _wglGetUniformLocation(compiledProg, UNIFORM_TEX_GEN_T_NAME) : null; - + stateTexGenRVectorUniform4f = stateEnableEndPortal ? _wglGetUniformLocation(compiledProg, UNIFORM_TEX_GEN_R_NAME) : null; - + stateTexGenQVectorUniform4f = stateEnableEndPortal ? _wglGetUniformLocation(compiledProg, UNIFORM_TEX_GEN_Q_NAME) : null; - + stateModelMatrixUniformMat4f = _wglGetUniformLocation(compiledProg, UNIFORM_MODEL_MATRIX_NAME); - + stateProjectionMatrixUniformMat4f = _wglGetUniformLocation(compiledProg, UNIFORM_PROJECTION_MATRIX_NAME); - + stateModelProjectionMatrixUniformMat4f = _wglGetUniformLocation(compiledProg, UNIFORM_MODEL_PROJECTION_MATRIX_NAME); - - stateTextureMatrix01UniformMat4f = (stateEnableEndPortal || stateHasAttribTexture) - ? _wglGetUniformLocation(compiledProg, - UNIFORM_TEXTURE_MATRIX_01_NAME) - : null; - + + stateTextureMatrix01UniformMat4f = (stateEnableEndPortal || stateHasAttribTexture) ? _wglGetUniformLocation(compiledProg, + UNIFORM_TEXTURE_MATRIX_01_NAME) : null; + stateTextureMatrix02UniformMat4f = stateHasAttribLightmap ? _wglGetUniformLocation(compiledProg, UNIFORM_TEXTURE_MATRIX_02_NAME) : null; - + stateTextureCoords01Uniform2f = (!stateHasAttribTexture && stateEnableTexture2D) ? _wglGetUniformLocation( compiledProg, UNIFORM_TEXTURE_COORDS_01_NAME) : null; - + stateTextureCoords02Uniform2f = (!stateHasAttribLightmap && stateEnableLightmap) ? _wglGetUniformLocation( compiledProg, UNIFORM_TEXTURE_COORDS_02_NAME) : null; - + stateAnisotropicFix2f = stateEnableAnisotropicFix ? _wglGetUniformLocation(compiledProg, UNIFORM_TEXTURE_ANISOTROPIC_FIX) : null; - + stateShaderBlendSrcColorUniform4f = stateEnableBlendAdd ? _wglGetUniformLocation(compiledProg, UNIFORM_BLEND_SRC_COLOR_NAME) : null; - + stateShaderBlendAddColorUniform4f = stateEnableBlendAdd ? _wglGetUniformLocation(compiledProg, UNIFORM_BLEND_ADD_COLOR_NAME) : null; - - if (stateEnableTexture2D) { + + if(stateEnableTexture2D) { EaglercraftGPU.bindGLShaderProgram(compiledProg); _wglUniform1i(_wglGetUniformLocation(compiledProg, UNIFORM_TEXTURE_UNIT_01_NAME), 0); } - - if (stateEnableLightmap) { + + if(stateEnableLightmap) { EaglercraftGPU.bindGLShaderProgram(compiledProg); _wglUniform1i(_wglGetUniformLocation(compiledProg, UNIFORM_TEXTURE_UNIT_02_NAME), 1); } } - + public FixedFunctionPipeline update() { - + EaglercraftGPU.bindGLShaderProgram(shaderProgram); - + int serial = GlStateManager.stateColorSerial; - if (stateColorSerial != serial) { + if(stateColorSerial != serial) { stateColorSerial = serial; float r = GlStateManager.stateColorR; float g = GlStateManager.stateColorG; float b = GlStateManager.stateColorB; float a = GlStateManager.stateColorA; - if (stateColorR != r || stateColorG != g || - stateColorB != b || stateColorA != a) { + if(stateColorR != r || stateColorG != g || + stateColorB != b || stateColorA != a) { _wglUniform4f(stateColorUniform4f, r, g, b, a); stateColorR = r; stateColorG = g; @@ -754,69 +728,68 @@ public class FixedFunctionPipeline { stateColorA = a; } } - - if (matrixCopyBuffer == null) { + + if(matrixCopyBuffer == null) { matrixCopyBuffer = PlatformRuntime.allocateFloatBuffer(16); } - + int ptr; - if (stateModelProjectionMatrixUniformMat4f == null) { + if(stateModelProjectionMatrixUniformMat4f == null) { ptr = GlStateManager.modelMatrixStackPointer; serial = GlStateManager.modelMatrixStackAccessSerial[ptr]; - if (stateModelMatrixSerial != serial) { + if(stateModelMatrixSerial != serial) { stateModelMatrixSerial = serial; matrixCopyBuffer.clear(); GlStateManager.modelMatrixStack[ptr].store(matrixCopyBuffer); matrixCopyBuffer.flip(); _wglUniformMatrix4fv(stateModelMatrixUniformMat4f, false, matrixCopyBuffer); } - + ptr = GlStateManager.projectionMatrixStackPointer; serial = GlStateManager.projectionMatrixStackAccessSerial[ptr]; - if (stateProjectionMatrixSerial != serial) { + if(stateProjectionMatrixSerial != serial) { stateProjectionMatrixSerial = serial; matrixCopyBuffer.clear(); GlStateManager.projectionMatrixStack[ptr].store(matrixCopyBuffer); matrixCopyBuffer.flip(); _wglUniformMatrix4fv(stateProjectionMatrixUniformMat4f, false, matrixCopyBuffer); } - } else { + }else { ptr = GlStateManager.modelMatrixStackPointer; serial = GlStateManager.modelMatrixStackAccessSerial[ptr]; int ptr2 = GlStateManager.projectionMatrixStackPointer; int serial2 = GlStateManager.projectionMatrixStackAccessSerial[ptr2]; boolean b = stateModelMatrixSerial != serial; - if (b || stateProjectionMatrixSerial != serial2) { + if(b || stateProjectionMatrixSerial != serial2) { stateModelMatrixSerial = serial; stateProjectionMatrixSerial = serial2; - if (b && stateModelMatrixUniformMat4f != null) { + if(b && stateModelMatrixUniformMat4f != null) { matrixCopyBuffer.clear(); GlStateManager.modelMatrixStack[ptr].store(matrixCopyBuffer); matrixCopyBuffer.flip(); _wglUniformMatrix4fv(stateModelMatrixUniformMat4f, false, matrixCopyBuffer); } - Matrix4f.mul(GlStateManager.projectionMatrixStack[ptr2], GlStateManager.modelMatrixStack[ptr], - tmpMatrixForInv); + Matrix4f.mul(GlStateManager.projectionMatrixStack[ptr2], GlStateManager.modelMatrixStack[ptr], tmpMatrixForInv); matrixCopyBuffer.clear(); tmpMatrixForInv.store(matrixCopyBuffer); matrixCopyBuffer.flip(); _wglUniformMatrix4fv(stateModelProjectionMatrixUniformMat4f, false, matrixCopyBuffer); } } - - if (stateEnableAlphaTest) { + + if(stateEnableAlphaTest) { float v = GlStateManager.stateAlphaTestRef; - if (stateAlphaTestRef != v) { + if(stateAlphaTestRef != v) { stateAlphaTestRef = v; _wglUniform1f(stateAlphaTestUniform1f, v); } } - - if (stateEnableTexture2D) { + + if(stateEnableTexture2D) { ptr = GlStateManager.textureMatrixStackPointer[0]; serial = GlStateManager.textureMatrixStackAccessSerial[0][ptr]; - if (stateHasAttribTexture || stateEnableEndPortal) { - if (stateTextureMatrixSerial[0] != serial) { + if(stateHasAttribTexture || stateEnableEndPortal) { + if(stateTextureMatrixSerial[0] != serial) { stateTextureMatrixSerial[0] = serial; matrixCopyBuffer.clear(); GlStateManager.textureMatrixStack[0][ptr].store(matrixCopyBuffer); @@ -824,9 +797,9 @@ public class FixedFunctionPipeline { _wglUniformMatrix4fv(stateTextureMatrix01UniformMat4f, false, matrixCopyBuffer); } } - if (!stateHasAttribTexture && !stateEnableEndPortal) { + if(!stateHasAttribTexture && !stateEnableEndPortal) { int serial2 = GlStateManager.textureCoordsAccessSerial[0]; - if (stateTextureCoordsAccessSerial[0] != serial2 || stateTextureCoordsMatrixSerial[0] != serial) { + if(stateTextureCoordsAccessSerial[0] != serial2 || stateTextureCoordsMatrixSerial[0] != serial) { stateTextureCoordsAccessSerial[0] = serial2; stateTextureCoordsMatrixSerial[0] = serial; tmpVec4ForTex.x = GlStateManager.textureCoordsX[0]; @@ -836,7 +809,7 @@ public class FixedFunctionPipeline { Matrix4f.transform(GlStateManager.textureMatrixStack[0][ptr], tmpVec4ForTex, tmpVec4ForTex); float x = tmpVec4ForTex.x / tmpVec4ForTex.w; float y = tmpVec4ForTex.y / tmpVec4ForTex.w; - if (x != stateTextureCoordsX[0] || y != stateTextureCoordsY[0]) { + if(x != stateTextureCoordsX[0] || y != stateTextureCoordsY[0]) { stateTextureCoordsX[0] = x; stateTextureCoordsY[0] = y; _wglUniform2f(stateTextureCoords01Uniform2f, x, y); @@ -844,13 +817,13 @@ public class FixedFunctionPipeline { } } } - - if (stateEnableLightmap) { + + if(stateEnableLightmap) { ptr = GlStateManager.textureMatrixStackPointer[1]; serial = GlStateManager.textureMatrixStackAccessSerial[1][ptr]; - if (!stateHasAttribLightmap) { + if(!stateHasAttribLightmap) { int serial2 = GlStateManager.textureCoordsAccessSerial[1]; - if (stateTextureCoordsAccessSerial[1] != serial2 || stateTextureCoordsMatrixSerial[1] != serial) { + if(stateTextureCoordsAccessSerial[1] != serial2 || stateTextureCoordsMatrixSerial[1] != serial) { stateTextureCoordsAccessSerial[1] = serial2; stateTextureCoordsMatrixSerial[1] = serial; tmpVec4ForTex.x = GlStateManager.textureCoordsX[1]; @@ -860,14 +833,14 @@ public class FixedFunctionPipeline { Matrix4f.transform(GlStateManager.textureMatrixStack[1][ptr], tmpVec4ForTex, tmpVec4ForTex); float x = tmpVec4ForTex.x / tmpVec4ForTex.w; float y = tmpVec4ForTex.y / tmpVec4ForTex.w; - if (x != stateTextureCoordsX[1] || y != stateTextureCoordsY[1]) { + if(x != stateTextureCoordsX[1] || y != stateTextureCoordsY[1]) { stateTextureCoordsX[1] = x; stateTextureCoordsY[1] = y; _wglUniform2f(stateTextureCoords02Uniform2f, x, y); } } - } else { - if (stateTextureMatrixSerial[1] != serial) { + }else { + if(stateTextureMatrixSerial[1] != serial) { stateTextureMatrixSerial[1] = serial; matrixCopyBuffer.clear(); GlStateManager.textureMatrixStack[1][ptr].store(matrixCopyBuffer); @@ -876,50 +849,50 @@ public class FixedFunctionPipeline { } } } - - if (stateEnableMCLighting) { + + if(stateEnableMCLighting) { ptr = GlStateManager.stateLightsStackPointer; serial = GlStateManager.stateLightingSerial[ptr]; - if (stateLightingSerial != serial) { + if(stateLightingSerial != serial) { stateLightingSerial = serial; boolean[] en = GlStateManager.stateLightsEnabled[ptr]; int lightsCounter = 0; - for (int i = 0; i < en.length; ++i) { - if (en[i]) { + for(int i = 0; i < en.length; ++i) { + if(en[i]) { Vector4f lightDirOld = stateLightsVectors[lightsCounter]; Vector4f lightDirNew = GlStateManager.stateLightsStack[ptr][i]; float x = lightDirNew.x; float y = lightDirNew.y; float z = lightDirNew.z; float w = lightDirNew.w; - if (lightDirOld.x != x || lightDirOld.y != y || lightDirOld.z != z || lightDirOld.w != w) { + if(lightDirOld.x != x || lightDirOld.y != y || lightDirOld.z != z || lightDirOld.w != w) { lightDirOld.x = x; lightDirOld.y = y; lightDirOld.z = z; lightDirOld.w = w; _wglUniform4f(stateLightsVectorsArrayUniform4f[lightsCounter], x, y, z, w); } - if (++lightsCounter >= stateLightsVectors.length) { + if(++lightsCounter >= stateLightsVectors.length) { break; } } } - - if (stateLightsEnabled != lightsCounter) { + + if(stateLightsEnabled != lightsCounter) { stateLightsEnabled = lightsCounter; _wglUniform1i(stateLightsEnabledUniform1i, lightsCounter); } - + } - + serial = GlStateManager.stateLightingAmbientSerial; - if (stateLightingAmbientSerial != serial) { + if(stateLightingAmbientSerial != serial) { stateLightingAmbientSerial = serial; float r = GlStateManager.stateLightingAmbientR; float g = GlStateManager.stateLightingAmbientG; float b = GlStateManager.stateLightingAmbientB; - if (stateLightingAmbientR != r || stateLightingAmbientG != g || + if(stateLightingAmbientR != r || stateLightingAmbientG != g || stateLightingAmbientB != b) { stateLightingAmbientR = r; stateLightingAmbientG = g; @@ -928,20 +901,18 @@ public class FixedFunctionPipeline { } } } - - if (stateEnableMCLighting || DynamicLightsStateManager.isInDynamicLightsPass()) { - if (!stateHasAttribNormal) { + + if(stateEnableMCLighting || DynamicLightsStateManager.isInDynamicLightsPass()) { + if(!stateHasAttribNormal) { serial = GlStateManager.stateNormalSerial; - if (stateNormalSerial != serial) { + if(stateNormalSerial != serial) { stateNormalSerial = serial; float x = GlStateManager.stateNormalX; float y = GlStateManager.stateNormalY; float z = GlStateManager.stateNormalZ; - float c = MathHelper.Q_rsqrt(x * x + y * y + z * z); - x *= c; - y *= c; - z *= c; - if (stateNormalX != x || stateNormalY != y || stateNormalZ != z) { + float c = 1.0f / MathHelper.sqrt_float(x * x + y * y + z * z); + x *= c; y *= c; z *= c; + if(stateNormalX != x || stateNormalY != y || stateNormalZ != z) { stateNormalX = x; stateNormalY = y; stateNormalZ = z; @@ -950,16 +921,16 @@ public class FixedFunctionPipeline { } } } - - if (stateEnableFog) { + + if(stateEnableFog) { serial = GlStateManager.stateFogSerial; - if (stateFogSerial != serial) { + if(stateFogSerial != serial) { stateFogSerial = serial; boolean fogEXP = GlStateManager.stateFogEXP; float fogDensity = GlStateManager.stateFogDensity; float fogStart = GlStateManager.stateFogStart; float fogEnd = GlStateManager.stateFogEnd; - if (stateFogEXP != fogEXP || stateFogDensity != fogDensity || + if(stateFogEXP != fogEXP || stateFogDensity != fogDensity || stateFogStart != fogStart || stateFogEnd != fogEnd) { stateFogEXP = fogEXP; stateFogDensity = fogDensity; @@ -971,7 +942,7 @@ public class FixedFunctionPipeline { float g = GlStateManager.stateFogColorG; float b = GlStateManager.stateFogColorB; float a = GlStateManager.stateFogColorA; - if (stateFogColorR != r || stateFogColorG != g || + if(stateFogColorR != r || stateFogColorG != g || stateFogColorB != b || stateFogColorA != a) { stateFogColorR = r; stateFogColorG = g; @@ -981,30 +952,30 @@ public class FixedFunctionPipeline { } } } - - if (stateEnableAnisotropicFix) { + + if(stateEnableAnisotropicFix) { serial = GlStateManager.stateAnisotropicFixSerial; - if (stateAnisotropicFixSerial != serial) { + if(stateAnisotropicFixSerial != serial) { stateAnisotropicFixSerial = serial; float w = GlStateManager.stateAnisotropicFixW; float h = GlStateManager.stateAnisotropicFixH; - if (stateAnisotropicFixW != w || stateAnisotropicFixH != h) { + if(stateAnisotropicFixW != w || stateAnisotropicFixH != h) { stateAnisotropicFixW = w; stateAnisotropicFixH = h; _wglUniform2f(stateAnisotropicFix2f, w, h); } } } - - if (stateEnableEndPortal) { + + if(stateEnableEndPortal) { serial = GlStateManager.stateTexGenSerial; - if (stateTexGenSerial != serial) { + if(stateTexGenSerial != serial) { stateTexGenSerial = serial; int planeS = GlStateManager.TexGen.S.plane; int planeT = GlStateManager.TexGen.T.plane; int planeR = GlStateManager.TexGen.R.plane; int planeQ = GlStateManager.TexGen.Q.plane; - if (stateTexGenSPlane != planeS || stateTexGenTPlane != planeT || + if(stateTexGenSPlane != planeS || stateTexGenTPlane != planeT || stateTexGenRPlane != planeR || stateTexGenQPlane != planeQ) { stateTexGenSPlane = planeS; stateTexGenTPlane = planeT; @@ -1052,16 +1023,16 @@ public class FixedFunctionPipeline { } } } - - if (stateEnableBlendAdd) { + + if(stateEnableBlendAdd) { serial = GlStateManager.stateShaderBlendColorSerial; - if (stateShaderBlendColorSerial != serial) { + if(stateShaderBlendColorSerial != serial) { stateShaderBlendColorSerial = serial; float r = GlStateManager.stateShaderBlendSrcColorR; float g = GlStateManager.stateShaderBlendSrcColorG; float b = GlStateManager.stateShaderBlendSrcColorB; float a = GlStateManager.stateShaderBlendSrcColorA; - if (stateShaderBlendSrcColorR != r || stateShaderBlendSrcColorG != g || + if(stateShaderBlendSrcColorR != r || stateShaderBlendSrcColorG != g || stateShaderBlendSrcColorB != b || stateShaderBlendSrcColorA != a) { _wglUniform4f(stateShaderBlendSrcColorUniform4f, r, g, b, a); stateShaderBlendSrcColorR = r; @@ -1073,7 +1044,7 @@ public class FixedFunctionPipeline { g = GlStateManager.stateShaderBlendAddColorG; b = GlStateManager.stateShaderBlendAddColorB; a = GlStateManager.stateShaderBlendAddColorA; - if (stateShaderBlendAddColorR != r || stateShaderBlendAddColorG != g || + if(stateShaderBlendAddColorR != r || stateShaderBlendAddColorG != g || stateShaderBlendAddColorB != b || stateShaderBlendAddColorA != a) { _wglUniform4f(stateShaderBlendAddColorUniform4f, r, g, b, a); stateShaderBlendAddColorR = r; @@ -1083,17 +1054,16 @@ public class FixedFunctionPipeline { } } } - - if (extensionProvider != null && extensionPointer != null) { + + if(extensionProvider != null && extensionPointer != null) { extensionProvider.updatePipeline(shaderProgram, stateCoreBits, stateExtBits, extensionPointer); } - + return this; } static void optimize() { - FixedFunctionPipeline pp; - for (int i = 0, l = pipelineListTracker.size(); i < l; ++i) { + for(int i = 0, l = pipelineListTracker.size(); i < l; ++i) { pipelineListTracker.get(i).streamBuffer.optimize(); } } @@ -1102,23 +1072,22 @@ public class FixedFunctionPipeline { shaderSourceCacheVSH = null; shaderSourceCacheFSH = null; FixedFunctionPipeline pp; - for (int i = 0; i < pipelineStateCache.length; ++i) { + for(int i = 0; i < pipelineStateCache.length; ++i) { pp = pipelineStateCache[i]; - if (pp != null) { + if(pp != null) { pp.destroy(); pipelineStateCache[i] = null; } } - for (int i = 0; i < pipelineExtStateCache.length; ++i) { + for(int i = 0; i < pipelineExtStateCache.length; ++i) { FixedFunctionPipeline[] ppp = pipelineExtStateCache[i]; - if (ppp != null) { - for (int j = 0; j < ppp.length; ++j) { + if(ppp != null) { + for(int j = 0; j < ppp.length; ++j) { FixedFunctionPipeline pppp = ppp[j]; - if (pppp != null) { + if(pppp != null) { pppp.destroy(); - if (extensionProvider != null && pppp.extensionPointer != null) { - extensionProvider.destroyPipeline(pppp.shaderProgram, pppp.stateCoreBits, pppp.stateExtBits, - pppp.extensionPointer); + if(extensionProvider != null && pppp.extensionPointer != null) { + extensionProvider.destroyPipeline(pppp.shaderProgram, pppp.stateCoreBits, pppp.stateExtBits, pppp.extensionPointer); } } } @@ -1127,12 +1096,12 @@ public class FixedFunctionPipeline { } pipelineListTracker.clear(); } - + public void destroy() { PlatformOpenGL._wglDeleteProgram(shaderProgram); streamBuffer.destroy(); } - + public IBufferArrayGL getDirectModeBufferArray() { return currentVertexArray.vertexArray; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java index 17ca478..392ad20 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java @@ -1,24 +1,16 @@ package net.lax1dude.eaglercraft.v1_8.opengl; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -52,19 +44,18 @@ public class FixedFunctionShader { public class FixedFunctionConstants { - public static final String VERSION = "#version 300 es"; public static final String FILENAME_VSH = "/assets/eagler/glsl/core.vsh"; public static final String FILENAME_FSH = "/assets/eagler/glsl/core.fsh"; public static final String PRECISION_INT = "lowp"; public static final String PRECISION_FLOAT = "highp"; public static final String PRECISION_SAMPLER = "mediump"; - + public static final String MACRO_ATTRIB_TEXTURE = "COMPILE_TEXTURE_ATTRIB"; public static final String MACRO_ATTRIB_COLOR = "COMPILE_COLOR_ATTRIB"; public static final String MACRO_ATTRIB_NORMAL = "COMPILE_NORMAL_ATTRIB"; public static final String MACRO_ATTRIB_LIGHTMAP = "COMPILE_LIGHTMAP_ATTRIB"; - + public static final String MACRO_ENABLE_TEXTURE2D = "COMPILE_ENABLE_TEXTURE2D"; public static final String MACRO_ENABLE_LIGHTMAP = "COMPILE_ENABLE_LIGHTMAP"; public static final String MACRO_ENABLE_ALPHA_TEST = "COMPILE_ENABLE_ALPHA_TEST"; @@ -106,7 +97,7 @@ public class FixedFunctionShader { public static final String UNIFORM_TEXTURE_UNIT_01_NAME = "u_samplerTexture"; public static final String UNIFORM_TEXTURE_UNIT_02_NAME = "u_samplerLightmap"; - + public static final String OUTPUT_COLOR = "output4f"; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLSLHeader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLSLHeader.java new file mode 100755 index 0000000..e31a076 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLSLHeader.java @@ -0,0 +1,99 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GLSLHeader { + + public static final String GLES2_COMPAT_FILE_NAME = "/assets/eagler/glsl/gles2_compat.glsl"; + + private static String header = null; + private static String gles2CompatFile = null; + + static void init() { + gles2CompatFile = EagRuntime.getRequiredResourceString(GLES2_COMPAT_FILE_NAME); + int glesVersion = EaglercraftGPU.checkOpenGLESVersion(); + StringBuilder headerBuilder; + if(glesVersion >= 310) { + headerBuilder = new StringBuilder("#version 310 es"); + boolean oes5 = PlatformOpenGL.checkOESGPUShader5Capable(); + boolean ext5 = !oes5 && PlatformOpenGL.checkEXTGPUShader5Capable(); + if(oes5) { + headerBuilder.append("\n#extension GL_OES_gpu_shader5 : enable"); + }else if(ext5) { + headerBuilder.append("\n#extension GL_EXT_gpu_shader5 : enable"); + } + headerBuilder.append("\n#define EAGLER_IS_GLES_310"); + headerBuilder.append("\n#define EAGLER_HAS_GLES_310"); + headerBuilder.append("\n#define EAGLER_HAS_GLES_300"); + headerBuilder.append("\n#define EAGLER_HAS_GLES_200"); + if(oes5 || ext5) { + headerBuilder.append("\n#define EAGLER_HAS_GLES_310_SHADER_5"); + } + }else if(glesVersion == 300) { + headerBuilder = new StringBuilder("#version 300 es"); + headerBuilder.append("\n#define EAGLER_IS_GLES_300"); + headerBuilder.append("\n#define EAGLER_HAS_GLES_300"); + headerBuilder.append("\n#define EAGLER_HAS_GLES_200"); + }else if(glesVersion == 200) { + boolean texLOD = PlatformOpenGL.checkTextureLODCapable(); + headerBuilder = new StringBuilder("#version 100"); + if(texLOD) { + headerBuilder.append("\n#extension GL_EXT_shader_texture_lod : enable"); + } + headerBuilder.append("\n#define EAGLER_HAS_GLES_200"); + headerBuilder.append("\n#define EAGLER_IS_GLES_200"); + if(texLOD) { + headerBuilder.append("\n#define EAGLER_HAS_GLES_200_SHADER_TEXTURE_LOD"); + } + }else { + throw new IllegalStateException("Unsupported OpenGL ES version: " + glesVersion); + } + header = headerBuilder.append('\n').toString(); + } + + static void destroy() { + header = null; + } + + public static String getHeader() { + if(header == null) throw new IllegalStateException(); + return header; + } + + public static String getVertexHeader(String shaderSrc) { + if(header == null) throw new IllegalStateException(); + return header + "#define EAGLER_IS_VERTEX_SHADER\n" + shaderSrc; + } + + public static String getFragmentHeader(String shaderSrc) { + if(header == null) throw new IllegalStateException(); + return header + "#define EAGLER_IS_FRAGMENT_SHADER\n" + shaderSrc; + } + + public static String getVertexHeaderCompat(String shaderSrc, String precisions) { + if(header == null || gles2CompatFile == null) throw new IllegalStateException(); + return header + "#define EAGLER_IS_VERTEX_SHADER\n" + (precisions == null ? "" : precisions + "\n") + gles2CompatFile + "\n" + shaderSrc; + } + + public static String getFragmentHeaderCompat(String shaderSrc, String precisions) { + if(header == null || gles2CompatFile == null) throw new IllegalStateException(); + return header + "#define EAGLER_IS_FRAGMENT_SHADER\n"+ (precisions == null ? "" : precisions + "\n") + gles2CompatFile + "\n" + shaderSrc; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GameOverlayFramebuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GameOverlayFramebuffer.java index 94a5402..cdfb7da 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GameOverlayFramebuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GameOverlayFramebuffer.java @@ -1,97 +1,118 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GameOverlayFramebuffer { - - private static final int _GL_FRAMEBUFFER = 0x8D40; - private static final int _GL_RENDERBUFFER = 0x8D41; - private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; - private static final int _GL_DEPTH_ATTACHMENT = 0x8D00; - private static final int _GL_DEPTH_COMPONENT16 = 0x81A5; - - private long age = -1l; - - private int currentWidth = -1; - private int currentHeight = -1; - - private IFramebufferGL framebuffer = null; - private IRenderbufferGL depthBuffer = null; - - private int framebufferColor = -1; - - public void beginRender(int width, int height) { - if(framebuffer == null) { - framebuffer = _wglCreateFramebuffer(); - depthBuffer = _wglCreateRenderbuffer(); - framebufferColor = GlStateManager.generateTexture(); - _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); - GlStateManager.bindTexture(framebufferColor); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(framebufferColor), 0); - _wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer); - _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, depthBuffer); - } - - if(currentWidth != width || currentHeight != height) { - currentWidth = width; - currentHeight = height; - GlStateManager.bindTexture(framebufferColor); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); - _wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer); - _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, width, height); - } - - _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); - } - - public void endRender() { - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - age = System.currentTimeMillis(); - } - - public long getAge() { - return age == -1l ? -1l : (System.currentTimeMillis() - age); - } - - public int getTexture() { - return framebufferColor; - } - - public void destroy() { - if(framebuffer != null) { - _wglDeleteFramebuffer(framebuffer); - _wglDeleteRenderbuffer(depthBuffer); - GlStateManager.deleteTexture(framebufferColor); - framebuffer = null; - depthBuffer = null; - framebufferColor = -1; - age = -1l; - _wglBindFramebuffer(_GL_FRAMEBUFFER, null); - } - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GameOverlayFramebuffer { + + private static final int _GL_FRAMEBUFFER = 0x8D40; + private static final int _GL_RENDERBUFFER = 0x8D41; + private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; + private static final int _GL_DEPTH_ATTACHMENT = 0x8D00; + private static final int _GL_DEPTH_COMPONENT16 = 0x81A5; + + private long age = -1l; + + private int currentWidth = -1; + private int currentHeight = -1; + + private IFramebufferGL framebuffer = null; + private IRenderbufferGL depthBuffer = null; + + private int framebufferColor = -1; + + private final boolean enableDepth; + + public GameOverlayFramebuffer() { + this(true); + } + + public GameOverlayFramebuffer(boolean enableDepth) { + this.enableDepth = enableDepth; + } + + public boolean beginRender(int width, int height) { + if(framebuffer == null) { + framebuffer = _wglCreateFramebuffer(); + depthBuffer = enableDepth ? _wglCreateRenderbuffer() : null; + framebufferColor = GlStateManager.generateTexture(); + _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + GlStateManager.bindTexture(framebufferColor); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(framebufferColor), 0); + if(enableDepth) { + _wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer); + _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, depthBuffer); + } + } + + boolean resized = currentWidth != width || currentHeight != height; + if(resized) { + currentWidth = width; + currentHeight = height; + GlStateManager.bindTexture(framebufferColor); + EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + if(enableDepth) { + _wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, width, height); + } + } + + _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + return resized; + } + + public void endRender() { + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + age = EagRuntime.steadyTimeMillis(); + } + + public long getAge() { + return age == -1l ? -1l : (EagRuntime.steadyTimeMillis() - age); + } + + public int getTexture() { + return framebufferColor; + } + + public void destroy() { + if(framebuffer != null) { + _wglDeleteFramebuffer(framebuffer); + if(enableDepth) { + _wglDeleteRenderbuffer(depthBuffer); + } + GlStateManager.deleteTexture(framebufferColor); + framebuffer = null; + depthBuffer = null; + framebufferColor = -1; + age = -1l; + _wglBindFramebuffer(_GL_FRAMEBUFFER, null); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java index 94d7e17..327a59d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java @@ -1,1235 +1,1265 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; -import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; -import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GlStateManager { - - static final Logger logger = LogManager.getLogger("GlStateManager"); - - static boolean stateDepthTest = false; - static int stateDepthFunc = -1; - static boolean stateDepthMask = true; - - static boolean stateCull = false; - static int stateCullFace = GL_BACK; - - static boolean statePolygonOffset = false; - static float statePolygonOffsetFactor = 0.0f; - static float statePolygonOffsetUnits = 0.0f; - - static float stateColorR = 1.0f; - static float stateColorG = 1.0f; - static float stateColorB = 1.0f; - static float stateColorA = 1.0f; - static int stateColorSerial = 0; - - static float stateShaderBlendSrcColorR = 1.0f; - static float stateShaderBlendSrcColorG = 1.0f; - static float stateShaderBlendSrcColorB = 1.0f; - static float stateShaderBlendSrcColorA = 1.0f; - static float stateShaderBlendAddColorR = 0.0f; - static float stateShaderBlendAddColorG = 0.0f; - static float stateShaderBlendAddColorB = 0.0f; - static float stateShaderBlendAddColorA = 0.0f; - static int stateShaderBlendColorSerial = 0; - static boolean stateEnableShaderBlendColor = false; - - static boolean stateBlend = false; - static boolean stateGlobalBlend = true; - static int stateBlendEquation = -1; - static int stateBlendSRC = -1; - static int stateBlendDST = -1; - static boolean stateEnableOverlayFramebufferBlending = false; - - static boolean stateAlphaTest = false; - static float stateAlphaTestRef = 0.1f; - - static boolean stateMaterial = false; - static boolean stateLighting = false; - static int stateLightsStackPointer = 0; - static final boolean[][] stateLightsEnabled = new boolean[4][8]; - static final Vector4f[][] stateLightsStack = new Vector4f[4][8]; - static final int[] stateLightingSerial = new int[4]; - - static float stateLightingAmbientR = 0.0f; - static float stateLightingAmbientG = 0.0f; - static float stateLightingAmbientB = 0.0f; - static int stateLightingAmbientSerial = 0; - - static float stateNormalX = 0.0f; - static float stateNormalY = 0.0f; - static float stateNormalZ = -1.0f; - static int stateNormalSerial = 0; - - static boolean stateFog = false; - static boolean stateFogEXP = false; - static float stateFogDensity = 1.0f; - static float stateFogStart = 0.0f; - static float stateFogEnd = 1.0f; - static float stateFogColorR = 1.0f; - static float stateFogColorG = 1.0f; - static float stateFogColorB = 1.0f; - static float stateFogColorA = 1.0f; - static int stateFogSerial = 0; - - static int activeTexture = 0; - static final boolean[] stateTexture = new boolean[16]; - static final int[] boundTexture = new int[] { - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 - }; - - static float stateAnisotropicFixW = -999.0f; - static float stateAnisotropicFixH = -999.0f; - static int stateAnisotropicFixSerial = 0; - - static boolean stateTexGen = false; - - static int viewportX = -1; - static int viewportY = -1; - static int viewportW = -1; - static int viewportH = -1; - - static int colorMaskBits = 15; - - static float clearColorR = 0.0f; - static float clearColorG = 0.0f; - static float clearColorB = 0.0f; - static float clearColorA = 1.0f; - - static float clearDepth = -999.0f; - - public static enum TexGen { - S, T, R, Q; - - int source = GL_OBJECT_LINEAR; - int plane = GL_OBJECT_PLANE; - Vector4f vector = new Vector4f(); - - } - - static float blendConstantR = -999.0f; - static float blendConstantG = -999.0f; - static float blendConstantB = -999.0f; - static float blendConstantA = -999.0f; - - static int stateTexGenSerial = 0; - - static int stateMatrixMode = GL_MODELVIEW; - - static final Matrix4f[] modelMatrixStack = new Matrix4f[48]; - static final int[] modelMatrixStackAccessSerial = new int[48]; - private static int modelMatrixAccessSerial = 0; - static int modelMatrixStackPointer = 0; - - static final Matrix4f[] projectionMatrixStack = new Matrix4f[8]; - static final int[] projectionMatrixStackAccessSerial = new int[8]; - private static int projectionMatrixAccessSerial = 0; - static int projectionMatrixStackPointer = 0; - - static final float[] textureCoordsX = new float[8]; - static final float[] textureCoordsY = new float[8]; - static final int[] textureCoordsAccessSerial = new int[8]; - - static final Matrix4f[][] textureMatrixStack = new Matrix4f[8][8]; - static final int[][] textureMatrixStackAccessSerial = new int[8][8]; - static int[] textureMatrixAccessSerial = new int[8]; - static int[] textureMatrixStackPointer = new int[8]; - - static boolean stateUseExtensionPipeline = false; - - private static final Matrix4f tmpInvertedMatrix = new Matrix4f(); - - static { - populateStack(modelMatrixStack); - populateStack(projectionMatrixStack); - populateStack(textureMatrixStack); - populateStack(stateLightsStack); - } - - static void populateStack(Matrix4f[] stack) { - for (int i = 0; i < stack.length; ++i) { - stack[i] = new Matrix4f(); - } - } - - static void populateStack(Matrix4f[][] stack) { - for (int i = 0; i < stack.length; ++i) { - populateStack(stack[i]); - } - } - - static void populateStack(Vector4f[][] stack) { - for (int i = 0; i < stack.length; ++i) { - for (int j = 0; j < stack[i].length; ++j) { - stack[i][j] = new Vector4f(0.0f, -1.0f, 0.0f, 0.0f); - } - } - } - - public static final void pushLightCoords() { - int push = stateLightsStackPointer + 1; - if (push < stateLightsStack.length) { - Vector4f[] copyFrom = stateLightsStack[stateLightsStackPointer]; - boolean[] copyFrom2 = stateLightsEnabled[stateLightsStackPointer]; - Vector4f[] copyTo = stateLightsStack[push]; - boolean[] copyTo2 = stateLightsEnabled[push]; - for (int i = 0; i < copyFrom.length; ++i) { - if (copyFrom2[i]) { - copyTo[i].set(copyFrom[i]); - copyTo2[i] = true; - } else { - copyTo2[i] = false; - } - } - stateLightingSerial[push] = stateLightingSerial[stateLightsStackPointer]; - stateLightsStackPointer = push; - } else { - Throwable t = new IndexOutOfBoundsException("GL_LIGHT direction stack overflow!" + - " Exceeded " + stateLightsStack.length + " calls to GlStateManager.pushLightCoords"); - logger.error(t); - } - } - - public static final void popLightCoords() { - if (stateLightsStackPointer > 0) { - --stateLightsStackPointer; - } else { - Throwable t = new IndexOutOfBoundsException("GL_LIGHT direction stack underflow!" + - " Called GlStateManager.popLightCoords on an empty light stack"); - logger.error(t); - } - } - - public static final void disableAlpha() { - stateAlphaTest = false; - } - - public static final void enableAlpha() { - stateAlphaTest = true; - } - - public static final void alphaFunc(int func, float ref) { - if (func != GL_GREATER) { - throw new UnsupportedOperationException("Only GL_GREATER alphaFunc is supported"); - } else { - stateAlphaTestRef = ref; - } - } - - public static final void enableLighting() { - stateLighting = true; - } - - public static final void disableLighting() { - stateLighting = false; - } - - public static final void enableExtensionPipeline() { - stateUseExtensionPipeline = true; - } - - public static final void disableExtensionPipeline() { - stateUseExtensionPipeline = false; - } - - public static final boolean isExtensionPipeline() { - return stateUseExtensionPipeline; - } - - private static final Vector4f paramVector4 = new Vector4f(); - - public static final void enableMCLight(int light, float diffuse, double dirX, - double dirY, double dirZ, double dirW) { - paramVector4.x = (float) dirX; - paramVector4.y = (float) dirY; - paramVector4.z = (float) dirZ; - paramVector4.w = (float) dirW; - Matrix4f.transform(modelMatrixStack[modelMatrixStackPointer], paramVector4, paramVector4); - paramVector4.normalise(); - Vector4f dest = stateLightsStack[stateLightsStackPointer][light]; - dest.x = paramVector4.x; - dest.y = paramVector4.y; - dest.z = paramVector4.z; - dest.w = diffuse; - stateLightsEnabled[stateLightsStackPointer][light] = true; - ++stateLightingSerial[stateLightsStackPointer]; - } - - public static final void disableMCLight(int light) { - stateLightsEnabled[stateLightsStackPointer][light] = false; - ++stateLightingSerial[stateLightsStackPointer]; - } - - public static final void setMCLightAmbient(float r, float g, float b) { - stateLightingAmbientR = r; - stateLightingAmbientG = g; - stateLightingAmbientB = b; - ++stateLightingAmbientSerial; - } - - public static final void enableColorMaterial() { - stateMaterial = true; - } - - public static final void disableColorMaterial() { - stateMaterial = false; - } - - public static final void disableDepth() { - if (stateDepthTest) { - _wglDisable(GL_DEPTH_TEST); - stateDepthTest = false; - } - } - - public static final void enableDepth() { - if (!stateDepthTest) { - _wglEnable(GL_DEPTH_TEST); - stateDepthTest = true; - } - } - - public static final void depthFunc(int depthFunc) { - int rev = depthFunc; - switch (depthFunc) { - case GL_GREATER: - rev = GL_LESS; - break; - case GL_GEQUAL: - rev = GL_LEQUAL; - break; - case GL_EQUAL: - rev = GL_EQUAL; - break; - case GL_LEQUAL: - rev = GL_GEQUAL; - break; - case GL_LESS: - rev = GL_GREATER; - break; - } - if (rev != stateDepthFunc) { - _wglDepthFunc(rev); - stateDepthFunc = rev; - } - } - - public static final void depthMask(boolean flagIn) { - if (flagIn != stateDepthMask) { - _wglDepthMask(flagIn); - stateDepthMask = flagIn; - } - } - - public static final void disableBlend() { - if (stateBlend) { - if (stateGlobalBlend) - _wglDisable(GL_BLEND); - stateBlend = false; - } - } - - public static final void enableBlend() { - if (!stateBlend) { - if (stateGlobalBlend) - _wglEnable(GL_BLEND); - stateBlend = true; - } - } - - public static final void globalDisableBlend() { - if (stateBlend) { - _wglDisable(GL_BLEND); - } - stateGlobalBlend = false; - } - - public static final void globalEnableBlend() { - if (stateBlend) { - _wglEnable(GL_BLEND); - } - stateGlobalBlend = true; - } - - public static final void blendFunc(int srcFactor, int dstFactor) { - if (stateEnableOverlayFramebufferBlending) { - tryBlendFuncSeparate(srcFactor, dstFactor, 0, 1); - return; - } - int srcBits = (srcFactor | (srcFactor << 16)); - int dstBits = (dstFactor | (dstFactor << 16)); - if (srcBits != stateBlendSRC || dstBits != stateBlendDST) { - _wglBlendFunc(srcFactor, dstFactor); - stateBlendSRC = srcBits; - stateBlendDST = dstBits; - } - } - - public static final void tryBlendFuncSeparate(int srcFactor, int dstFactor, int srcFactorAlpha, - int dstFactorAlpha) { - if (stateEnableOverlayFramebufferBlending) { // game overlay framebuffer in EntityRenderer.java - srcFactorAlpha = GL_ONE; - dstFactorAlpha = GL_ONE_MINUS_SRC_ALPHA; - } - int srcBits = (srcFactor | (srcFactorAlpha << 16)); - int dstBits = (dstFactor | (dstFactorAlpha << 16)); - if (srcBits != stateBlendSRC || dstBits != stateBlendDST) { - _wglBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha); - stateBlendSRC = srcBits; - stateBlendDST = dstBits; - } - } - - public static final void enableOverlayFramebufferBlending() { - stateEnableOverlayFramebufferBlending = true; - } - - public static final void disableOverlayFramebufferBlending() { - stateEnableOverlayFramebufferBlending = false; - } - - public static final void setShaderBlendSrc(float r, float g, float b, float a) { - stateShaderBlendSrcColorR = r; - stateShaderBlendSrcColorG = g; - stateShaderBlendSrcColorB = b; - stateShaderBlendSrcColorA = a; - ++stateShaderBlendColorSerial; - } - - public static final void setShaderBlendAdd(float r, float g, float b, float a) { - stateShaderBlendAddColorR = r; - stateShaderBlendAddColorG = g; - stateShaderBlendAddColorB = b; - stateShaderBlendAddColorA = a; - ++stateShaderBlendColorSerial; - } - - public static final void enableShaderBlendAdd() { - stateEnableShaderBlendColor = true; - } - - public static final void disableShaderBlendAdd() { - stateEnableShaderBlendColor = false; - } - - public static final void setBlendConstants(float r, float g, float b, float a) { - if (r != blendConstantR || g != blendConstantG || b != blendConstantB || a != blendConstantA) { - _wglBlendColor(r, g, b, a); - blendConstantR = r; - blendConstantG = g; - blendConstantB = b; - blendConstantA = a; - } - } - - public static final void enableFog() { - stateFog = true; - } - - public static final void disableFog() { - stateFog = false; - } - - public static final void setFog(int param) { - stateFogEXP = param == GL_EXP; - ++stateFogSerial; - } - - public static final void setFogDensity(float param) { - stateFogDensity = param; - ++stateFogSerial; - } - - public static final void setFogStart(float param) { - stateFogStart = param; - ++stateFogSerial; - } - - public static final void setFogEnd(float param) { - stateFogEnd = param; - ++stateFogSerial; - } - - public static final void enableCull() { - if (!stateCull) { - _wglEnable(GL_CULL_FACE); - stateCull = true; - } - } - - public static final void disableCull() { - if (stateCull) { - _wglDisable(GL_CULL_FACE); - stateCull = false; - } - } - - public static final void cullFace(int mode) { - if (stateCullFace != mode) { - _wglCullFace(mode); - stateCullFace = mode; - } - } - - public static final void enablePolygonOffset() { - if (!statePolygonOffset) { - _wglEnable(GL_POLYGON_OFFSET_FILL); - statePolygonOffset = true; - } - } - - public static final void disablePolygonOffset() { - if (statePolygonOffset) { - _wglDisable(GL_POLYGON_OFFSET_FILL); - statePolygonOffset = false; - } - } - - public static final void doPolygonOffset(float factor, float units) { - if (factor != statePolygonOffsetFactor || units != statePolygonOffsetUnits) { - _wglPolygonOffset(-factor, units); - statePolygonOffsetFactor = factor; - statePolygonOffsetUnits = units; - } - } - - public static final void enableColorLogic() { - throw new UnsupportedOperationException("Color logic op is not supported in OpenGL ES!"); - } - - public static final void disableColorLogic() { - - } - - public static final void colorLogicOp(int opcode) { - - } - - public static final void enableTexGen() { - stateTexGen = true; - } - - public static final void disableTexGen() { - stateTexGen = false; - } - - public static final void texGen(GlStateManager.TexGen coord, int source) { - coord.source = source; - ++stateTexGenSerial; - } - - public static final void func_179105_a(GlStateManager.TexGen coord, int plane, FloatBuffer vector) { - coord.plane = plane; - coord.vector.load(vector); - if (plane == GL_EYE_PLANE) { - tmpInvertedMatrix.load(GlStateManager.modelMatrixStack[GlStateManager.modelMatrixStackPointer]).invert() - .transpose(); - Matrix4f.transform(tmpInvertedMatrix, coord.vector, coord.vector); - } - ++stateTexGenSerial; - } - - public static final void setActiveTexture(int texture) { - int textureIdx = texture - GL_TEXTURE0; - if (textureIdx != activeTexture) { - _wglActiveTexture(texture); - activeTexture = textureIdx; - } - } - - public static final void enableTexture2D() { - stateTexture[activeTexture] = true; - } - - public static final void disableTexture2D() { - stateTexture[activeTexture] = false; - } - - public static final void texCoords2D(float x, float y) { - textureCoordsX[activeTexture] = x; - textureCoordsY[activeTexture] = y; - ++textureCoordsAccessSerial[activeTexture]; - } - - public static final void texCoords2DDirect(int tex, float x, float y) { - textureCoordsX[tex] = x; - textureCoordsY[tex] = y; - ++textureCoordsAccessSerial[tex]; - } - - public static final float getTexCoordX(int tex) { - return textureCoordsX[tex]; - } - - public static final float getTexCoordY(int tex) { - return textureCoordsY[tex]; - } - - public static final int generateTexture() { - return EaglercraftGPU.mapTexturesGL.register(_wglGenTextures()); - } - - public static final void deleteTexture(int texture) { - _wglDeleteTextures(EaglercraftGPU.mapTexturesGL.free(texture)); - boolean f = false; - for (int i = 0; i < boundTexture.length; ++i) { - if (boundTexture[i] == texture) { - _wglActiveTexture(GL_TEXTURE0 + i); - _wglBindTexture(GL_TEXTURE_2D, null); - _wglBindTexture(GL_TEXTURE_3D, null); - boundTexture[i] = -1; - f = true; - } - } - if (f) { - _wglActiveTexture(GL_TEXTURE0 + activeTexture); - } - } - - public static final void bindTexture(int texture) { - if (texture != boundTexture[activeTexture]) { - _wglBindTexture(GL_TEXTURE_2D, EaglercraftGPU.mapTexturesGL.get(texture)); - boundTexture[activeTexture] = texture; - } - } - - public static final void bindTexture3D(int texture) { - if (texture != boundTexture[activeTexture]) { - _wglBindTexture(GL_TEXTURE_3D, EaglercraftGPU.mapTexturesGL.get(texture)); - boundTexture[activeTexture] = texture; - } - } - - public static final void quickBindTexture(int unit, int texture) { - int unitBase = unit - GL_TEXTURE0; - if (texture != boundTexture[unitBase]) { - if (unitBase != activeTexture) { - _wglActiveTexture(unit); - } - _wglBindTexture(GL_TEXTURE_2D, EaglercraftGPU.mapTexturesGL.get(texture)); - boundTexture[unitBase] = texture; - if (unitBase != activeTexture) { - _wglActiveTexture(GL_TEXTURE0 + activeTexture); - } - } - } - - public static final void shadeModel(int mode) { - - } - - public static final void enableRescaleNormal() { - // still not sure what this is for - } - - public static final void disableRescaleNormal() { - - } - - public static final void viewport(int x, int y, int w, int h) { - if (viewportX != x || viewportY != y || viewportW != w || viewportH != h) { - _wglViewport(x, y, w, h); - viewportX = x; - viewportY = y; - viewportW = w; - viewportH = h; - } - } - - public static final void colorMask(boolean red, boolean green, boolean blue, boolean alpha) { - int bits = (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0); - if (bits != colorMaskBits) { - _wglColorMask(red, green, blue, alpha); - colorMaskBits = bits; - } - } - - public static final void clearDepth(float depth) { - depth = 1.0f - depth; - if (depth != clearDepth) { - _wglClearDepth(depth); - clearDepth = depth; - } - } - - public static final void clearColor(float red, float green, float blue, float alpha) { - if (red != clearColorR || green != clearColorG || blue != clearColorB || alpha != clearColorA) { - _wglClearColor(red, green, blue, alpha); - clearColorR = red; - clearColorG = green; - clearColorB = blue; - clearColorA = alpha; - } - } - - public static final void clear(int mask) { - _wglClear(mask); - } - - public static final void matrixMode(int mode) { - stateMatrixMode = mode; - } - - public static final void loadIdentity() { - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modelMatrixStack[modelMatrixStackPointer].setIdentity(); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - projectionMatrixStack[projectionMatrixStackPointer].setIdentity(); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].setIdentity(); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - public static final void pushMatrix() { - int push; - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - push = modelMatrixStackPointer + 1; - if (push < modelMatrixStack.length) { - modelMatrixStack[push].load(modelMatrixStack[modelMatrixStackPointer]); - modelMatrixStackAccessSerial[push] = modelMatrixStackAccessSerial[modelMatrixStackPointer]; - modelMatrixStackPointer = push; - } else { - Throwable t = new IndexOutOfBoundsException("GL_MODELVIEW matrix stack overflow!" + - " Exceeded " + modelMatrixStack.length + " calls to GlStateManager.pushMatrix"); - logger.error(t); - } - break; - case GL_PROJECTION: - push = projectionMatrixStackPointer + 1; - if (push < projectionMatrixStack.length) { - projectionMatrixStack[push].load(projectionMatrixStack[projectionMatrixStackPointer]); - projectionMatrixStackAccessSerial[push] = projectionMatrixStackAccessSerial[projectionMatrixStackPointer]; - projectionMatrixStackPointer = push; - } else { - Throwable t = new IndexOutOfBoundsException("GL_PROJECTION matrix stack overflow!" + - " Exceeded " + projectionMatrixStack.length + " calls to GlStateManager.pushMatrix"); - logger.error(t); - } - break; - case GL_TEXTURE: - push = textureMatrixStackPointer[activeTexture] + 1; - if (push < textureMatrixStack.length) { - int ptr = textureMatrixStackPointer[activeTexture]; - textureMatrixStack[activeTexture][push].load(textureMatrixStack[activeTexture][ptr]); - textureMatrixStackAccessSerial[activeTexture][push] = textureMatrixStackAccessSerial[activeTexture][ptr]; - textureMatrixStackPointer[activeTexture] = push; - } else { - Throwable t = new IndexOutOfBoundsException( - "GL_TEXTURE #" + activeTexture + " matrix stack overflow!" + - " Exceeded " + textureMatrixStack.length + " calls to GlStateManager.pushMatrix"); - logger.error(t); - } - break; - } - } - - public static final void popMatrix() { - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - if (modelMatrixStackPointer > 0) { - --modelMatrixStackPointer; - } else { - Throwable t = new IndexOutOfBoundsException("GL_MODELVIEW matrix stack underflow!" + - " Called GlStateManager.popMatrix on an empty matrix stack"); - logger.error(t); - } - break; - case GL_PROJECTION: - if (projectionMatrixStackPointer > 0) { - --projectionMatrixStackPointer; - } else { - Throwable t = new IndexOutOfBoundsException("GL_PROJECTION matrix stack underflow!" + - " Called GlStateManager.popMatrix on an empty matrix stack"); - logger.error(t); - } - break; - case GL_TEXTURE: - if (textureMatrixStackPointer[activeTexture] > 0) { - --textureMatrixStackPointer[activeTexture]; - } else { - Throwable t = new IndexOutOfBoundsException("GL_TEXTURE #" + activeTexture + - " matrix stack underflow! Called GlStateManager.popMatrix on an empty matrix stack"); - logger.error(t); - } - break; - } - } - - public static final void getFloat(int pname, float[] params) { - switch (pname) { - case GL_MODELVIEW_MATRIX: - modelMatrixStack[modelMatrixStackPointer].store(params); - break; - case GL_PROJECTION_MATRIX: - projectionMatrixStack[projectionMatrixStackPointer].store(params); - break; - case GL_TEXTURE_MATRIX: - textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].store(params); - break; - default: - throw new UnsupportedOperationException("glGetFloat can only be used to retrieve matricies!"); - } - } - - public static final void getFloat(int pname, FloatBuffer params) { - switch (pname) { - case GL_MODELVIEW_MATRIX: - modelMatrixStack[modelMatrixStackPointer].store(params); - break; - case GL_PROJECTION_MATRIX: - projectionMatrixStack[projectionMatrixStackPointer].store(params); - break; - case GL_TEXTURE_MATRIX: - textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].store(params); - break; - default: - throw new UnsupportedOperationException("glGetFloat can only be used to retrieve matricies!"); - } - } - - public static final void ortho(double left, double right, double bottom, double top, double zNear, double zFar) { - Matrix4f matrix; - switch (stateMatrixMode) { - case GL_MODELVIEW: - matrix = modelMatrixStack[modelMatrixStackPointer]; - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - default: - matrix = projectionMatrixStack[projectionMatrixStackPointer]; - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - matrix = textureMatrixStack[activeTexture][ptr]; - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - paramMatrix.m00 = 2.0f / (float) (right - left); - paramMatrix.m01 = 0.0f; - paramMatrix.m02 = 0.0f; - paramMatrix.m03 = 0.0f; - paramMatrix.m10 = 0.0f; - paramMatrix.m11 = 2.0f / (float) (top - bottom); - paramMatrix.m12 = 0.0f; - paramMatrix.m13 = 0.0f; - paramMatrix.m20 = 0.0f; - paramMatrix.m21 = 0.0f; - paramMatrix.m22 = 2.0f / (float) (zFar - zNear); - paramMatrix.m23 = 0.0f; - paramMatrix.m30 = (float) (-(right + left) / (right - left)); - paramMatrix.m31 = (float) (-(top + bottom) / (top - bottom)); - paramMatrix.m32 = (float) ((zFar + zNear) / (zFar - zNear)); - paramMatrix.m33 = 1.0f; - Matrix4f.mul(matrix, paramMatrix, matrix); - } - - private static final Vector3f paramVector = new Vector3f(); - private static final float toRad = 0.0174532925f; - - public static final void rotate(float angle, float x, float y, float z) { - paramVector.x = x; - paramVector.y = y; - paramVector.z = z; - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modelMatrixStack[modelMatrixStackPointer].rotate(angle * toRad, paramVector); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - projectionMatrixStack[projectionMatrixStackPointer].rotate(angle * toRad, paramVector); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - textureMatrixStack[activeTexture][ptr].rotate(angle * toRad, paramVector); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - public static final void scale(float x, float y, float z) { - paramVector.x = x; - paramVector.y = y; - paramVector.z = z; - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modelMatrixStack[modelMatrixStackPointer].scale(paramVector); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - projectionMatrixStack[projectionMatrixStackPointer].scale(paramVector); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - textureMatrixStack[activeTexture][ptr].scale(paramVector); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - public static final void scale(double x, double y, double z) { - paramVector.x = (float) x; - paramVector.y = (float) y; - paramVector.z = (float) z; - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modelMatrixStack[modelMatrixStackPointer].scale(paramVector); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - projectionMatrixStack[projectionMatrixStackPointer].scale(paramVector); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - textureMatrixStack[activeTexture][ptr].scale(paramVector); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - public static final void translate(float x, float y, float z) { - paramVector.x = x; - paramVector.y = y; - paramVector.z = z; - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modelMatrixStack[modelMatrixStackPointer].translate(paramVector); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - projectionMatrixStack[projectionMatrixStackPointer].translate(paramVector); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - textureMatrixStack[activeTexture][ptr].translate(paramVector); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - public static final void translate(double x, double y, double z) { - paramVector.x = (float) x; - paramVector.y = (float) y; - paramVector.z = (float) z; - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modelMatrixStack[modelMatrixStackPointer].translate(paramVector); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - projectionMatrixStack[projectionMatrixStackPointer].translate(paramVector); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - textureMatrixStack[activeTexture][ptr].translate(paramVector); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - private static final Matrix4f paramMatrix = new Matrix4f(); - - public static final void multMatrix(float[] matrix) { - Matrix4f modeMatrix; - - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modeMatrix = modelMatrixStack[modelMatrixStackPointer]; - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - modeMatrix = projectionMatrixStack[projectionMatrixStackPointer]; - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - modeMatrix = textureMatrixStack[activeTexture][ptr]; - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - - paramMatrix.load(matrix); - - Matrix4f.mul(modeMatrix, paramMatrix, modeMatrix); - } - - public static final void multMatrix(Matrix4f matrix) { - Matrix4f modeMatrix; - - switch (stateMatrixMode) { - case GL_MODELVIEW: - default: - modeMatrix = modelMatrixStack[modelMatrixStackPointer]; - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - modeMatrix = projectionMatrixStack[projectionMatrixStackPointer]; - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - modeMatrix = textureMatrixStack[activeTexture][ptr]; - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - - Matrix4f.mul(modeMatrix, matrix, modeMatrix); - } - - public static final void color(float colorRed, float colorGreen, float colorBlue, float colorAlpha) { - stateColorR = colorRed; - stateColorG = colorGreen; - stateColorB = colorBlue; - stateColorA = colorAlpha; - ++stateColorSerial; - } - - public static final void color(float colorRed, float colorGreen, float colorBlue) { - stateColorR = colorRed; - stateColorG = colorGreen; - stateColorB = colorBlue; - stateColorA = 1.0f; - ++stateColorSerial; - } - - public static final void resetColor() { - stateColorR = 1.0f; - stateColorG = 1.0f; - stateColorB = 1.0f; - stateColorA = 1.0f; - ++stateColorSerial; - } - - public static final void callList(int list) { - EaglercraftGPU.glCallList(list); - } - - public static final void gluPerspective(float fovy, float aspect, float zNear, float zFar) { - Matrix4f matrix; - switch (stateMatrixMode) { - case GL_MODELVIEW: - matrix = modelMatrixStack[modelMatrixStackPointer]; - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - default: - matrix = projectionMatrixStack[projectionMatrixStackPointer]; - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - matrix = textureMatrixStack[activeTexture][ptr]; - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f); - paramMatrix.m00 = cotangent / aspect; - paramMatrix.m01 = 0.0f; - paramMatrix.m02 = 0.0f; - paramMatrix.m03 = 0.0f; - paramMatrix.m10 = 0.0f; - paramMatrix.m11 = cotangent; - paramMatrix.m12 = 0.0f; - paramMatrix.m13 = 0.0f; - paramMatrix.m20 = 0.0f; - paramMatrix.m21 = 0.0f; - paramMatrix.m22 = (zFar + zNear) / (zFar - zNear); - paramMatrix.m23 = -1.0f; - paramMatrix.m30 = 0.0f; - paramMatrix.m31 = 0.0f; - paramMatrix.m32 = 2.0f * zFar * zNear / (zFar - zNear); - paramMatrix.m33 = 0.0f; - Matrix4f.mul(matrix, paramMatrix, matrix); - } - - public static final void gluLookAt(Vector3f eye, Vector3f center, Vector3f up) { - Matrix4f matrix; - switch (stateMatrixMode) { - case GL_MODELVIEW: - matrix = modelMatrixStack[modelMatrixStackPointer]; - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - default: - matrix = projectionMatrixStack[projectionMatrixStackPointer]; - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - int ptr = textureMatrixStackPointer[activeTexture]; - matrix = textureMatrixStack[activeTexture][ptr]; - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - float x = center.x - eye.x; - float y = center.y - eye.y; - float z = center.z - eye.z; - float xyzLen = (float) Math.sqrt(x * x + y * y + z * z); - x /= xyzLen; - y /= xyzLen; - z /= xyzLen; - float ux = up.x; - float uy = up.y; - float uz = up.z; - xyzLen = (float) Math.sqrt(ux * ux + uy * uy + uz * uz); - ux /= xyzLen; - uy /= xyzLen; - uz /= xyzLen; - float lxx = y * uz - z * uy; - float lxy = ux * z - uz * x; - float lxz = x * uy - y * ux; - float lyx = lxy * z - lxz * y; - float lyy = x * lxz - z * lxx; - float lyz = lxx * y - lxy * x; - paramMatrix.m00 = lxx; - paramMatrix.m01 = lyx; - paramMatrix.m02 = -x; - paramMatrix.m03 = 0.0f; - paramMatrix.m10 = lxy; - paramMatrix.m11 = lyy; - paramMatrix.m12 = -y; - paramMatrix.m13 = 0.0f; - paramMatrix.m20 = lxz; - paramMatrix.m21 = lyz; - paramMatrix.m22 = -z; - paramMatrix.m23 = 0.0f; - paramMatrix.m30 = -eye.x; - paramMatrix.m31 = -eye.y; - paramMatrix.m32 = -eye.z; - paramMatrix.m33 = 1.0f; - Matrix4f.mul(matrix, paramMatrix, matrix); - } - - public static final void transform(Vector4f vecIn, Vector4f vecOut) { - Matrix4f matrix; - switch (stateMatrixMode) { - case GL_MODELVIEW: - matrix = modelMatrixStack[modelMatrixStackPointer]; - break; - case GL_PROJECTION: - default: - matrix = projectionMatrixStack[projectionMatrixStackPointer]; - break; - case GL_TEXTURE: - matrix = textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]]; - break; - } - Matrix4f.transform(matrix, vecIn, vecOut); - } - - private static final Matrix4f unprojA = new Matrix4f(); - private static final Matrix4f unprojB = new Matrix4f(); - private static final Vector4f unprojC = new Vector4f(); - - public static final void gluUnProject(float p1, float p2, float p3, float[] modelview, float[] projection, - int[] viewport, float[] objectcoords) { - unprojA.load(modelview); - unprojB.load(projection); - Matrix4f.mul(unprojA, unprojB, unprojB); - unprojB.invert(); - unprojC.set(((p1 - (float) viewport[0]) / (float) viewport[2]) * 2f - 1f, - ((p2 - (float) viewport[1]) / (float) viewport[3]) * 2f - 1f, p3, 1.0f); - Matrix4f.transform(unprojB, unprojC, unprojC); - objectcoords[0] = unprojC.x / unprojC.w; - objectcoords[1] = unprojC.y / unprojC.w; - objectcoords[2] = unprojC.z / unprojC.w; - } - - public static final void getMatrix(Matrix4f mat) { - switch (stateMatrixMode) { - case GL_MODELVIEW: - mat.load(modelMatrixStack[modelMatrixStackPointer]); - break; - case GL_PROJECTION: - default: - mat.load(projectionMatrixStack[projectionMatrixStackPointer]); - break; - case GL_TEXTURE: - mat.load(textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]]); - break; - } - } - - public static final void loadMatrix(Matrix4f mat) { - switch (stateMatrixMode) { - case GL_MODELVIEW: - modelMatrixStack[modelMatrixStackPointer].load(mat); - modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; - break; - case GL_PROJECTION: - default: - projectionMatrixStack[projectionMatrixStackPointer].load(mat); - projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; - break; - case GL_TEXTURE: - textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].load(mat); - textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; - break; - } - } - - public static final int getModelViewSerial() { - return modelMatrixStackAccessSerial[modelMatrixStackPointer]; - } - - public static final Matrix4f getModelViewReference() { - return modelMatrixStack[modelMatrixStackPointer]; - } - - public static void recompileShaders() { - FixedFunctionPipeline.flushCache(); - } +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; +import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; +import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GlStateManager { + + static final Logger logger = LogManager.getLogger("GlStateManager"); + + static boolean stateDepthTest = false; + static boolean stateDepthTestStash = false; + static int stateDepthFunc = -1; + static boolean stateDepthMask = true; + + static boolean stateCull = false; + static boolean stateCullStash = false; + static int stateCullFace = GL_BACK; + + static boolean statePolygonOffset = false; + static float statePolygonOffsetFactor = 0.0f; + static float statePolygonOffsetUnits = 0.0f; + + static float stateColorR = 1.0f; + static float stateColorG = 1.0f; + static float stateColorB = 1.0f; + static float stateColorA = 1.0f; + static int stateColorSerial = 0; + + static float stateShaderBlendSrcColorR = 1.0f; + static float stateShaderBlendSrcColorG = 1.0f; + static float stateShaderBlendSrcColorB = 1.0f; + static float stateShaderBlendSrcColorA = 1.0f; + static float stateShaderBlendAddColorR = 0.0f; + static float stateShaderBlendAddColorG = 0.0f; + static float stateShaderBlendAddColorB = 0.0f; + static float stateShaderBlendAddColorA = 0.0f; + static int stateShaderBlendColorSerial = 0; + static boolean stateEnableShaderBlendColor = false; + + static boolean stateBlend = false; + static boolean stateBlendStash = false; + static boolean stateGlobalBlend = true; + static int stateBlendEquation = -1; + static int stateBlendSRC = -1; + static int stateBlendDST = -1; + static boolean stateEnableOverlayFramebufferBlending = false; + + static boolean stateAlphaTest = false; + static float stateAlphaTestRef = 0.1f; + + static boolean stateMaterial = false; + static boolean stateLighting = false; + static int stateLightsStackPointer = 0; + static final boolean[][] stateLightsEnabled = new boolean[4][8]; + static final Vector4f[][] stateLightsStack = new Vector4f[4][8]; + static final int[] stateLightingSerial = new int[4]; + + static float stateLightingAmbientR = 0.0f; + static float stateLightingAmbientG = 0.0f; + static float stateLightingAmbientB = 0.0f; + static int stateLightingAmbientSerial = 0; + + static float stateNormalX = 0.0f; + static float stateNormalY = 0.0f; + static float stateNormalZ = -1.0f; + static int stateNormalSerial = 0; + + static boolean stateFog = false; + static boolean stateFogEXP = false; + static float stateFogDensity = 1.0f; + static float stateFogStart = 0.0f; + static float stateFogEnd = 1.0f; + static float stateFogColorR = 1.0f; + static float stateFogColorG = 1.0f; + static float stateFogColorB = 1.0f; + static float stateFogColorA = 1.0f; + static int stateFogSerial = 0; + + static int activeTexture = 0; + static final boolean[] stateTexture = new boolean[16]; + static final int[] boundTexture = new int[] { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 + }; + + static float stateAnisotropicFixW = -999.0f; + static float stateAnisotropicFixH = -999.0f; + static int stateAnisotropicFixSerial = 0; + + static boolean stateTexGen = false; + + static int viewportX = -1; + static int viewportY = -1; + static int viewportW = -1; + static int viewportH = -1; + + static int colorMaskBits = 15; + + static float clearColorR = 0.0f; + static float clearColorG = 0.0f; + static float clearColorB = 0.0f; + static float clearColorA = 1.0f; + + static float clearDepth = -999.0f; + + public static enum TexGen { + S, T, R, Q; + + int source = GL_OBJECT_LINEAR; + int plane = GL_OBJECT_PLANE; + Vector4f vector = new Vector4f(); + + } + + static float blendConstantR = -999.0f; + static float blendConstantG = -999.0f; + static float blendConstantB = -999.0f; + static float blendConstantA = -999.0f; + + static int stateTexGenSerial = 0; + + static int stateMatrixMode = GL_MODELVIEW; + + static final Matrix4f[] modelMatrixStack = new Matrix4f[48]; + static final int[] modelMatrixStackAccessSerial = new int[48]; + private static int modelMatrixAccessSerial = 0; + static int modelMatrixStackPointer = 0; + + static final Matrix4f[] projectionMatrixStack = new Matrix4f[8]; + static final int[] projectionMatrixStackAccessSerial = new int[8]; + private static int projectionMatrixAccessSerial = 0; + static int projectionMatrixStackPointer = 0; + + static final float[] textureCoordsX = new float[8]; + static final float[] textureCoordsY = new float[8]; + static final int[] textureCoordsAccessSerial = new int[8]; + + static final Matrix4f[][] textureMatrixStack = new Matrix4f[8][8]; + static final int[][] textureMatrixStackAccessSerial = new int[8][8]; + static int[] textureMatrixAccessSerial = new int[8]; + static int[] textureMatrixStackPointer = new int[8]; + + static boolean stateUseExtensionPipeline = false; + + private static final Matrix4f tmpInvertedMatrix = new Matrix4f(); + + static { + populateStack(modelMatrixStack); + populateStack(projectionMatrixStack); + populateStack(textureMatrixStack); + populateStack(stateLightsStack); + } + + static void populateStack(Matrix4f[] stack) { + for(int i = 0; i < stack.length; ++i) { + stack[i] = new Matrix4f(); + } + } + + static void populateStack(Matrix4f[][] stack) { + for(int i = 0; i < stack.length; ++i) { + populateStack(stack[i]); + } + } + + static void populateStack(Vector4f[][] stack) { + for(int i = 0; i < stack.length; ++i) { + for(int j = 0; j < stack[i].length; ++j) { + stack[i][j] = new Vector4f(0.0f, -1.0f, 0.0f, 0.0f); + } + } + } + + public static final void pushLightCoords() { + int push = stateLightsStackPointer + 1; + if(push < stateLightsStack.length) { + Vector4f[] copyFrom = stateLightsStack[stateLightsStackPointer]; + boolean[] copyFrom2 = stateLightsEnabled[stateLightsStackPointer]; + Vector4f[] copyTo = stateLightsStack[push]; + boolean[] copyTo2 = stateLightsEnabled[push]; + for(int i = 0; i < copyFrom.length; ++i) { + if(copyFrom2[i]) { + copyTo[i].set(copyFrom[i]); + copyTo2[i] = true; + }else { + copyTo2[i] = false; + } + } + stateLightingSerial[push] = stateLightingSerial[stateLightsStackPointer]; + stateLightsStackPointer = push; + }else { + Throwable t = new IndexOutOfBoundsException("GL_LIGHT direction stack overflow!" + + " Exceeded " + stateLightsStack.length + " calls to GlStateManager.pushLightCoords"); + logger.error(t); + } + } + + public static final void popLightCoords() { + if(stateLightsStackPointer > 0) { + --stateLightsStackPointer; + }else { + Throwable t = new IndexOutOfBoundsException("GL_LIGHT direction stack underflow!" + + " Called GlStateManager.popLightCoords on an empty light stack"); + logger.error(t); + } + } + + public static final void disableAlpha() { + stateAlphaTest = false; + } + + public static final void enableAlpha() { + stateAlphaTest = true; + } + + public static final void alphaFunc(int func, float ref) { + if(func != GL_GREATER) { + throw new UnsupportedOperationException("Only GL_GREATER alphaFunc is supported"); + }else { + stateAlphaTestRef = ref; + } + } + + public static final void enableLighting() { + stateLighting = true; + } + + public static final void disableLighting() { + stateLighting = false; + } + + public static final void enableExtensionPipeline() { + stateUseExtensionPipeline = true; + } + + public static final void disableExtensionPipeline() { + stateUseExtensionPipeline = false; + } + + public static final boolean isExtensionPipeline() { + return stateUseExtensionPipeline; + } + + private static final Vector4f paramVector4 = new Vector4f(); + public static final void enableMCLight(int light, float diffuse, double dirX, + double dirY, double dirZ, double dirW) { + paramVector4.x = (float)dirX; + paramVector4.y = (float)dirY; + paramVector4.z = (float)dirZ; + paramVector4.w = (float)dirW; + Matrix4f.transform(modelMatrixStack[modelMatrixStackPointer], paramVector4, paramVector4); + paramVector4.normalise(); + Vector4f dest = stateLightsStack[stateLightsStackPointer][light]; + dest.x = paramVector4.x; + dest.y = paramVector4.y; + dest.z = paramVector4.z; + dest.w = diffuse; + stateLightsEnabled[stateLightsStackPointer][light] = true; + ++stateLightingSerial[stateLightsStackPointer]; + } + + public static final void disableMCLight(int light) { + stateLightsEnabled[stateLightsStackPointer][light] = false; + ++stateLightingSerial[stateLightsStackPointer]; + } + + public static final void setMCLightAmbient(float r, float g, float b) { + stateLightingAmbientR = r; + stateLightingAmbientG = g; + stateLightingAmbientB = b; + ++stateLightingAmbientSerial; + } + + public static final void enableColorMaterial() { + stateMaterial = true; + } + + public static final void disableColorMaterial() { + stateMaterial = false; + } + + public static final void disableDepth() { + if(stateDepthTest) { + _wglDisable(GL_DEPTH_TEST); + stateDepthTest = false; + } + } + + public static final void enableDepth() { + if(!stateDepthTest) { + _wglEnable(GL_DEPTH_TEST); + stateDepthTest = true; + } + } + + public static final void eagPushStateForGLES2BlitHack() { + stateDepthTestStash = stateDepthTest; + stateCullStash = stateCull; + stateBlendStash = stateBlend; + } + + public static final void eagPopStateForGLES2BlitHack() { + if(stateDepthTestStash) { + enableDepth(); + }else { + disableDepth(); + } + if(stateCullStash) { + enableCull(); + }else { + disableCull(); + } + if(stateBlendStash) { + enableBlend(); + }else { + disableBlend(); + } + } + + public static final void depthFunc(int depthFunc) { + int rev = depthFunc; + switch(depthFunc) { + case GL_GREATER: + rev = GL_LESS; + break; + case GL_GEQUAL: + rev = GL_LEQUAL; + break; + case GL_EQUAL: + rev = GL_EQUAL; + break; + case GL_LEQUAL: + rev = GL_GEQUAL; + break; + case GL_LESS: + rev = GL_GREATER; + break; + } + if(rev != stateDepthFunc) { + _wglDepthFunc(rev); + stateDepthFunc = rev; + } + } + + public static final void depthMask(boolean flagIn) { + if(flagIn != stateDepthMask) { + _wglDepthMask(flagIn); + stateDepthMask = flagIn; + } + } + + public static final void disableBlend() { + if(stateBlend) { + if(stateGlobalBlend) _wglDisable(GL_BLEND); + stateBlend = false; + } + } + + public static final void enableBlend() { + if(!stateBlend) { + if(stateGlobalBlend) _wglEnable(GL_BLEND); + stateBlend = true; + } + } + + public static final void globalDisableBlend() { + if(stateBlend) { + _wglDisable(GL_BLEND); + } + stateGlobalBlend = false; + } + + public static final void globalEnableBlend() { + if(stateBlend) { + _wglEnable(GL_BLEND); + } + stateGlobalBlend = true; + } + + public static final void blendFunc(int srcFactor, int dstFactor) { + if(stateEnableOverlayFramebufferBlending) { + tryBlendFuncSeparate(srcFactor, dstFactor, 0, 1); + return; + } + int srcBits = (srcFactor | (srcFactor << 16)); + int dstBits = (dstFactor | (dstFactor << 16)); + if(srcBits != stateBlendSRC || dstBits != stateBlendDST) { + _wglBlendFunc(srcFactor, dstFactor); + stateBlendSRC = srcBits; + stateBlendDST = dstBits; + } + } + + public static final void tryBlendFuncSeparate(int srcFactor, int dstFactor, int srcFactorAlpha, int dstFactorAlpha) { + if(stateEnableOverlayFramebufferBlending) { // game overlay framebuffer in EntityRenderer.java + srcFactorAlpha = GL_ONE; + dstFactorAlpha = GL_ONE_MINUS_SRC_ALPHA; + } + int srcBits = (srcFactor | (srcFactorAlpha << 16)); + int dstBits = (dstFactor | (dstFactorAlpha << 16)); + if(srcBits != stateBlendSRC || dstBits != stateBlendDST) { + _wglBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha); + stateBlendSRC = srcBits; + stateBlendDST = dstBits; + } + } + + public static final void enableOverlayFramebufferBlending() { + stateEnableOverlayFramebufferBlending = true; + } + + public static final void disableOverlayFramebufferBlending() { + stateEnableOverlayFramebufferBlending = false; + } + + public static final void setShaderBlendSrc(float r, float g, float b, float a) { + stateShaderBlendSrcColorR = r; + stateShaderBlendSrcColorG = g; + stateShaderBlendSrcColorB = b; + stateShaderBlendSrcColorA = a; + ++stateShaderBlendColorSerial; + } + + public static final void setShaderBlendAdd(float r, float g, float b, float a) { + stateShaderBlendAddColorR = r; + stateShaderBlendAddColorG = g; + stateShaderBlendAddColorB = b; + stateShaderBlendAddColorA = a; + ++stateShaderBlendColorSerial; + } + + public static final void enableShaderBlendAdd() { + stateEnableShaderBlendColor = true; + } + + public static final void disableShaderBlendAdd() { + stateEnableShaderBlendColor = false; + } + + public static final void setBlendConstants(float r, float g, float b, float a) { + if(r != blendConstantR || g != blendConstantG || b != blendConstantB || a != blendConstantA) { + _wglBlendColor(r, g, b, a); + blendConstantR = r; + blendConstantG = g; + blendConstantB = b; + blendConstantA = a; + } + } + + public static final void enableFog() { + stateFog = true; + } + + public static final void disableFog() { + stateFog = false; + } + + public static final void setFog(int param) { + stateFogEXP = param == GL_EXP; + ++stateFogSerial; + } + + public static final void setFogDensity(float param) { + stateFogDensity = param; + ++stateFogSerial; + } + + public static final void setFogStart(float param) { + stateFogStart = param; + ++stateFogSerial; + } + + public static final void setFogEnd(float param) { + stateFogEnd = param; + ++stateFogSerial; + } + + public static final void enableCull() { + if(!stateCull) { + _wglEnable(GL_CULL_FACE); + stateCull = true; + } + } + + public static final void disableCull() { + if(stateCull) { + _wglDisable(GL_CULL_FACE); + stateCull = false; + } + } + + public static final void cullFace(int mode) { + if(stateCullFace != mode) { + _wglCullFace(mode); + stateCullFace = mode; + } + } + + public static final void enablePolygonOffset() { + if(!statePolygonOffset) { + _wglEnable(GL_POLYGON_OFFSET_FILL); + statePolygonOffset = true; + } + } + + public static final void disablePolygonOffset() { + if(statePolygonOffset) { + _wglDisable(GL_POLYGON_OFFSET_FILL); + statePolygonOffset = false; + } + } + + public static final void doPolygonOffset(float factor, float units) { + if(factor != statePolygonOffsetFactor || units != statePolygonOffsetUnits) { + _wglPolygonOffset(-factor, units); + statePolygonOffsetFactor = factor; + statePolygonOffsetUnits = units; + } + } + + public static final void enableColorLogic() { + throw new UnsupportedOperationException("Color logic op is not supported in OpenGL ES!"); + } + + public static final void disableColorLogic() { + + } + + public static final void colorLogicOp(int opcode) { + + } + + public static final void enableTexGen() { + stateTexGen = true; + } + + public static final void disableTexGen() { + stateTexGen = false; + } + + public static final void texGen(GlStateManager.TexGen coord, int source) { + coord.source = source; + ++stateTexGenSerial; + } + + public static final void func_179105_a(GlStateManager.TexGen coord, int plane, FloatBuffer vector) { + coord.plane = plane; + coord.vector.load(vector); + if(plane == GL_EYE_PLANE) { + tmpInvertedMatrix.load(GlStateManager.modelMatrixStack[GlStateManager.modelMatrixStackPointer]).invert().transpose(); + Matrix4f.transform(tmpInvertedMatrix, coord.vector, coord.vector); + } + ++stateTexGenSerial; + } + + public static final void setActiveTexture(int texture) { + int textureIdx = texture - GL_TEXTURE0; + if(textureIdx != activeTexture) { + _wglActiveTexture(texture); + activeTexture = textureIdx; + } + } + + public static final void enableTexture2D() { + stateTexture[activeTexture] = true; + } + + public static final void disableTexture2D() { + stateTexture[activeTexture] = false; + } + + public static final void texCoords2D(float x, float y) { + textureCoordsX[activeTexture] = x; + textureCoordsY[activeTexture] = y; + ++textureCoordsAccessSerial[activeTexture]; + } + + public static final void texCoords2DDirect(int tex, float x, float y) { + textureCoordsX[tex] = x; + textureCoordsY[tex] = y; + ++textureCoordsAccessSerial[tex]; + } + + public static final float getTexCoordX(int tex) { + return textureCoordsX[tex]; + } + + public static final float getTexCoordY(int tex) { + return textureCoordsY[tex]; + } + + public static final int generateTexture() { + return EaglercraftGPU.mapTexturesGL.register(_wglGenTextures()); + } + + public static final void deleteTexture(int texture) { + unbindTextureIfCached(texture); + _wglDeleteTextures(EaglercraftGPU.mapTexturesGL.free(texture)); + } + + static final void unbindTextureIfCached(int texture) { + boolean f1, f2 = false; + for(int i = 0; i < boundTexture.length; ++i) { + if(boundTexture[i] == texture) { + f1 = i != activeTexture; + if(f2 || f1) { + _wglActiveTexture(GL_TEXTURE0 + i); + f2 = f1; + } + _wglBindTexture(GL_TEXTURE_2D, null); + if(EaglercraftGPU.checkOpenGLESVersion() >= 300) { + _wglBindTexture(GL_TEXTURE_3D, null); + } + boundTexture[i] = -1; + } + } + if(f2) { + _wglActiveTexture(GL_TEXTURE0 + activeTexture); + } + } + + public static final void bindTexture(int texture) { + if(texture != boundTexture[activeTexture]) { + _wglBindTexture(GL_TEXTURE_2D, EaglercraftGPU.mapTexturesGL.get(texture)); + boundTexture[activeTexture] = texture; + } + } + + public static final void bindTexture3D(int texture) { + if(texture != boundTexture[activeTexture]) { + _wglBindTexture(GL_TEXTURE_3D, EaglercraftGPU.mapTexturesGL.get(texture)); + boundTexture[activeTexture] = texture; + } + } + + public static final void quickBindTexture(int unit, int texture) { + int unitBase = unit - GL_TEXTURE0; + if(texture != boundTexture[unitBase]) { + if(unitBase != activeTexture) { + _wglActiveTexture(unit); + } + _wglBindTexture(GL_TEXTURE_2D, EaglercraftGPU.mapTexturesGL.get(texture)); + boundTexture[unitBase] = texture; + if(unitBase != activeTexture) { + _wglActiveTexture(GL_TEXTURE0 + activeTexture); + } + } + } + + public static final void shadeModel(int mode) { + + } + + public static final void enableRescaleNormal() { + // still not sure what this is for + } + + public static final void disableRescaleNormal() { + + } + + public static final void viewport(int x, int y, int w, int h) { + if(viewportX != x || viewportY != y || viewportW != w || viewportH != h) { + _wglViewport(x, y, w, h); + viewportX = x; + viewportY = y; + viewportW = w; + viewportH = h; + } + } + + public static final void colorMask(boolean red, boolean green, boolean blue, boolean alpha) { + int bits = (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0); + if(bits != colorMaskBits) { + _wglColorMask(red, green, blue, alpha); + colorMaskBits = bits; + } + } + + public static final void clearDepth(float depth) { + depth = 1.0f - depth; + if(depth != clearDepth) { + _wglClearDepth(depth); + clearDepth = depth; + } + } + + public static final void clearColor(float red, float green, float blue, float alpha) { + if(red != clearColorR || green != clearColorG || blue != clearColorB || alpha != clearColorA) { + _wglClearColor(red, green, blue, alpha); + clearColorR = red; + clearColorG = green; + clearColorB = blue; + clearColorA = alpha; + } + } + + public static final void clear(int mask) { + _wglClear(mask); + } + + public static final void matrixMode(int mode) { + stateMatrixMode = mode; + } + + public static final void loadIdentity() { + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modelMatrixStack[modelMatrixStackPointer].setIdentity(); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + projectionMatrixStack[projectionMatrixStackPointer].setIdentity(); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].setIdentity(); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + public static final void pushMatrix() { + int push; + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + push = modelMatrixStackPointer + 1; + if(push < modelMatrixStack.length) { + modelMatrixStack[push].load(modelMatrixStack[modelMatrixStackPointer]); + modelMatrixStackAccessSerial[push] = modelMatrixStackAccessSerial[modelMatrixStackPointer]; + modelMatrixStackPointer = push; + }else { + Throwable t = new IndexOutOfBoundsException("GL_MODELVIEW matrix stack overflow!" + + " Exceeded " + modelMatrixStack.length + " calls to GlStateManager.pushMatrix"); + logger.error(t); + } + break; + case GL_PROJECTION: + push = projectionMatrixStackPointer + 1; + if(push < projectionMatrixStack.length) { + projectionMatrixStack[push].load(projectionMatrixStack[projectionMatrixStackPointer]); + projectionMatrixStackAccessSerial[push] = projectionMatrixStackAccessSerial[projectionMatrixStackPointer]; + projectionMatrixStackPointer = push; + }else { + Throwable t = new IndexOutOfBoundsException("GL_PROJECTION matrix stack overflow!" + + " Exceeded " + projectionMatrixStack.length + " calls to GlStateManager.pushMatrix"); + logger.error(t); + } + break; + case GL_TEXTURE: + push = textureMatrixStackPointer[activeTexture] + 1; + if(push < textureMatrixStack.length) { + int ptr = textureMatrixStackPointer[activeTexture]; + textureMatrixStack[activeTexture][push].load(textureMatrixStack[activeTexture][ptr]); + textureMatrixStackAccessSerial[activeTexture][push] = textureMatrixStackAccessSerial[activeTexture][ptr]; + textureMatrixStackPointer[activeTexture] = push; + }else { + Throwable t = new IndexOutOfBoundsException("GL_TEXTURE #" + activeTexture + " matrix stack overflow!" + + " Exceeded " + textureMatrixStack.length + " calls to GlStateManager.pushMatrix"); + logger.error(t); + } + break; + } + } + + public static final void popMatrix() { + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + if(modelMatrixStackPointer > 0) { + --modelMatrixStackPointer; + }else { + Throwable t = new IndexOutOfBoundsException("GL_MODELVIEW matrix stack underflow!" + + " Called GlStateManager.popMatrix on an empty matrix stack"); + logger.error(t); + } + break; + case GL_PROJECTION: + if(projectionMatrixStackPointer > 0) { + --projectionMatrixStackPointer; + }else { + Throwable t = new IndexOutOfBoundsException("GL_PROJECTION matrix stack underflow!" + + " Called GlStateManager.popMatrix on an empty matrix stack"); + logger.error(t); + } + break; + case GL_TEXTURE: + if(textureMatrixStackPointer[activeTexture] > 0) { + --textureMatrixStackPointer[activeTexture]; + }else { + Throwable t = new IndexOutOfBoundsException("GL_TEXTURE #" + activeTexture + + " matrix stack underflow! Called GlStateManager.popMatrix on an empty matrix stack"); + logger.error(t); + } + break; + } + } + + public static final void getFloat(int pname, float[] params) { + switch(pname) { + case GL_MODELVIEW_MATRIX: + modelMatrixStack[modelMatrixStackPointer].store(params); + break; + case GL_PROJECTION_MATRIX: + projectionMatrixStack[projectionMatrixStackPointer].store(params); + break; + case GL_TEXTURE_MATRIX: + textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].store(params); + break; + default: + throw new UnsupportedOperationException("glGetFloat can only be used to retrieve matricies!"); + } + } + + public static final void getFloat(int pname, FloatBuffer params) { + switch(pname) { + case GL_MODELVIEW_MATRIX: + modelMatrixStack[modelMatrixStackPointer].store(params); + break; + case GL_PROJECTION_MATRIX: + projectionMatrixStack[projectionMatrixStackPointer].store(params); + break; + case GL_TEXTURE_MATRIX: + textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].store(params); + break; + default: + throw new UnsupportedOperationException("glGetFloat can only be used to retrieve matricies!"); + } + } + + public static final void ortho(double left, double right, double bottom, double top, double zNear, double zFar) { + Matrix4f matrix; + switch(stateMatrixMode) { + case GL_MODELVIEW: + matrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + default: + matrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + matrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + paramMatrix.m00 = 2.0f / (float)(right - left); + paramMatrix.m01 = 0.0f; + paramMatrix.m02 = 0.0f; + paramMatrix.m03 = 0.0f; + paramMatrix.m10 = 0.0f; + paramMatrix.m11 = 2.0f / (float)(top - bottom); + paramMatrix.m12 = 0.0f; + paramMatrix.m13 = 0.0f; + paramMatrix.m20 = 0.0f; + paramMatrix.m21 = 0.0f; + paramMatrix.m22 = 2.0f / (float)(zFar - zNear); + paramMatrix.m23 = 0.0f; + paramMatrix.m30 = (float)(-(right + left) / (right - left)); + paramMatrix.m31 = (float)(-(top + bottom) / (top - bottom)); + paramMatrix.m32 = (float)((zFar + zNear) / (zFar - zNear)); + paramMatrix.m33 = 1.0f; + Matrix4f.mul(matrix, paramMatrix, matrix); + } + + private static final Vector3f paramVector = new Vector3f(); + private static final float toRad = 0.0174532925f; + public static final void rotate(float angle, float x, float y, float z) { + paramVector.x = x; + paramVector.y = y; + paramVector.z = z; + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modelMatrixStack[modelMatrixStackPointer].rotate(angle * toRad, paramVector); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + projectionMatrixStack[projectionMatrixStackPointer].rotate(angle * toRad, paramVector); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + textureMatrixStack[activeTexture][ptr].rotate(angle * toRad, paramVector); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + public static final void scale(float x, float y, float z) { + paramVector.x = x; + paramVector.y = y; + paramVector.z = z; + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modelMatrixStack[modelMatrixStackPointer].scale(paramVector); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + projectionMatrixStack[projectionMatrixStackPointer].scale(paramVector); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + textureMatrixStack[activeTexture][ptr].scale(paramVector); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + public static final void scale(double x, double y, double z) { + paramVector.x = (float)x; + paramVector.y = (float)y; + paramVector.z = (float)z; + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modelMatrixStack[modelMatrixStackPointer].scale(paramVector); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + projectionMatrixStack[projectionMatrixStackPointer].scale(paramVector); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + textureMatrixStack[activeTexture][ptr].scale(paramVector); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + public static final void translate(float x, float y, float z) { + paramVector.x = x; + paramVector.y = y; + paramVector.z = z; + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modelMatrixStack[modelMatrixStackPointer].translate(paramVector); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + projectionMatrixStack[projectionMatrixStackPointer].translate(paramVector); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + textureMatrixStack[activeTexture][ptr].translate(paramVector); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + public static final void translate(double x, double y, double z) { + paramVector.x = (float)x; + paramVector.y = (float)y; + paramVector.z = (float)z; + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modelMatrixStack[modelMatrixStackPointer].translate(paramVector); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + projectionMatrixStack[projectionMatrixStackPointer].translate(paramVector); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + textureMatrixStack[activeTexture][ptr].translate(paramVector); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + private static final Matrix4f paramMatrix = new Matrix4f(); + public static final void multMatrix(float[] matrix) { + Matrix4f modeMatrix; + + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modeMatrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + modeMatrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + modeMatrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + + paramMatrix.load(matrix); + + Matrix4f.mul(modeMatrix, paramMatrix, modeMatrix); + } + + public static final void multMatrix(Matrix4f matrix) { + Matrix4f modeMatrix; + + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modeMatrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + modeMatrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + modeMatrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + + Matrix4f.mul(modeMatrix, matrix, modeMatrix); + } + + public static final void color(float colorRed, float colorGreen, float colorBlue, float colorAlpha) { + stateColorR = colorRed; + stateColorG = colorGreen; + stateColorB = colorBlue; + stateColorA = colorAlpha; + ++stateColorSerial; + } + + public static final void color(float colorRed, float colorGreen, float colorBlue) { + stateColorR = colorRed; + stateColorG = colorGreen; + stateColorB = colorBlue; + stateColorA = 1.0f; + ++stateColorSerial; + } + + public static final void resetColor() { + stateColorR = 1.0f; + stateColorG = 1.0f; + stateColorB = 1.0f; + stateColorA = 1.0f; + ++stateColorSerial; + } + + public static final void callList(int list) { + EaglercraftGPU.glCallList(list); + } + + public static final void gluPerspective(float fovy, float aspect, float zNear, float zFar) { + Matrix4f matrix; + switch(stateMatrixMode) { + case GL_MODELVIEW: + matrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + default: + matrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + matrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f); + paramMatrix.m00 = cotangent / aspect; + paramMatrix.m01 = 0.0f; + paramMatrix.m02 = 0.0f; + paramMatrix.m03 = 0.0f; + paramMatrix.m10 = 0.0f; + paramMatrix.m11 = cotangent; + paramMatrix.m12 = 0.0f; + paramMatrix.m13 = 0.0f; + paramMatrix.m20 = 0.0f; + paramMatrix.m21 = 0.0f; + paramMatrix.m22 = (zFar + zNear) / (zFar - zNear); + paramMatrix.m23 = -1.0f; + paramMatrix.m30 = 0.0f; + paramMatrix.m31 = 0.0f; + paramMatrix.m32 = 2.0f * zFar * zNear / (zFar - zNear); + paramMatrix.m33 = 0.0f; + Matrix4f.mul(matrix, paramMatrix, matrix); + } + + public static final void gluLookAt(Vector3f eye, Vector3f center, Vector3f up) { + Matrix4f matrix; + switch(stateMatrixMode) { + case GL_MODELVIEW: + matrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + default: + matrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + matrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + float x = center.x - eye.x; + float y = center.y - eye.y; + float z = center.z - eye.z; + float xyzLen = (float) Math.sqrt(x * x + y * y + z * z); + x /= xyzLen; + y /= xyzLen; + z /= xyzLen; + float ux = up.x; + float uy = up.y; + float uz = up.z; + xyzLen = (float) Math.sqrt(ux * ux + uy * uy + uz * uz); + ux /= xyzLen; + uy /= xyzLen; + uz /= xyzLen; + float lxx = y * uz - z * uy; + float lxy = ux * z - uz * x; + float lxz = x * uy - y * ux; + float lyx = lxy * z - lxz * y; + float lyy = x * lxz - z * lxx; + float lyz = lxx * y - lxy * x; + paramMatrix.m00 = lxx; + paramMatrix.m01 = lyx; + paramMatrix.m02 = -x; + paramMatrix.m03 = 0.0f; + paramMatrix.m10 = lxy; + paramMatrix.m11 = lyy; + paramMatrix.m12 = -y; + paramMatrix.m13 = 0.0f; + paramMatrix.m20 = lxz; + paramMatrix.m21 = lyz; + paramMatrix.m22 = -z; + paramMatrix.m23 = 0.0f; + paramMatrix.m30 = -eye.x; + paramMatrix.m31 = -eye.y; + paramMatrix.m32 = -eye.z; + paramMatrix.m33 = 1.0f; + Matrix4f.mul(matrix, paramMatrix, matrix); + } + + public static final void transform(Vector4f vecIn, Vector4f vecOut) { + Matrix4f matrix; + switch(stateMatrixMode) { + case GL_MODELVIEW: + matrix = modelMatrixStack[modelMatrixStackPointer]; + break; + case GL_PROJECTION: + default: + matrix = projectionMatrixStack[projectionMatrixStackPointer]; + break; + case GL_TEXTURE: + matrix = textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]]; + break; + } + Matrix4f.transform(matrix, vecIn, vecOut); + } + + private static final Matrix4f unprojA = new Matrix4f(); + private static final Matrix4f unprojB = new Matrix4f(); + private static final Vector4f unprojC = new Vector4f(); + public static final void gluUnProject(float p1, float p2, float p3, float[] modelview, float[] projection, + int[] viewport, float[] objectcoords) { + unprojA.load(modelview); + unprojB.load(projection); + Matrix4f.mul(unprojA, unprojB, unprojB); + unprojB.invert(); + unprojC.set(((p1 - (float)viewport[0]) / (float)viewport[2]) * 2f - 1f, + ((p2 - (float)viewport[1]) / (float)viewport[3]) * 2f - 1f, p3, 1.0f); + Matrix4f.transform(unprojB, unprojC, unprojC); + objectcoords[0] = unprojC.x / unprojC.w; + objectcoords[1] = unprojC.y / unprojC.w; + objectcoords[2] = unprojC.z / unprojC.w; + } + + public static final void getMatrix(Matrix4f mat) { + switch(stateMatrixMode) { + case GL_MODELVIEW: + mat.load(modelMatrixStack[modelMatrixStackPointer]); + break; + case GL_PROJECTION: + default: + mat.load(projectionMatrixStack[projectionMatrixStackPointer]); + break; + case GL_TEXTURE: + mat.load(textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]]); + break; + } + } + + public static final void loadMatrix(Matrix4f mat) { + switch(stateMatrixMode) { + case GL_MODELVIEW: + modelMatrixStack[modelMatrixStackPointer].load(mat); + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + default: + projectionMatrixStack[projectionMatrixStackPointer].load(mat); + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + textureMatrixStack[activeTexture][textureMatrixStackPointer[activeTexture]].load(mat); + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = ++textureMatrixAccessSerial[activeTexture]; + break; + } + } + + public static final int getModelViewSerial() { + return modelMatrixStackAccessSerial[modelMatrixStackPointer]; + } + + public static final Matrix4f getModelViewReference() { + return modelMatrixStack[modelMatrixStackPointer]; + } + + public static void recompileShaders() { + FixedFunctionPipeline.flushCache(); + } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ImageData.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ImageData.java index ca5a5b1..28d0cb3 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ImageData.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ImageData.java @@ -1,151 +1,201 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import java.io.InputStream; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ImageData { - - public final int width; - public final int height; - public final int[] pixels; - public final boolean alpha; - - public ImageData(int width, int height, int[] pixels, boolean alpha) { - this.width = width; - this.height = height; - this.pixels = pixels; - this.alpha = alpha; - } - - public ImageData(int width, int height, boolean alpha) { - this.width = width; - this.height = height; - this.pixels = new int[width * height]; - this.alpha = alpha; - } - - public ImageData fillAlpha() { - for(int i = 0; i < pixels.length; ++i) { - pixels[i] = pixels[i] | 0xFF000000; - } - return this; - } - - public ImageData getSubImage(int x, int y, int pw, int ph) { - int[] img = new int[pw * ph]; - for(int i = 0; i < ph; ++i) { - System.arraycopy(pixels, (i + y) * this.width + x, img, i * pw, pw); - } - return new ImageData(pw, ph, img, alpha); - } - - public static final ImageData loadImageFile(String path) { - byte[] fileData = EagRuntime.getResourceBytes(path); - if(fileData != null) { - return loadImageFile(fileData); - }else { - return null; - } - } - - public static final ImageData loadImageFile(InputStream data) { - return PlatformAssets.loadImageFile(data); - } - - public static final ImageData loadImageFile(byte[] data) { - return PlatformAssets.loadImageFile(data); - } - - public void getRGB(int startX, int startY, int w, int h, - int[] rgbArray, int offset, int scansize) { - for(int y = 0; y < h; ++y) { - System.arraycopy(pixels, offset + (y + startY) * scansize + startX, rgbArray, y * w, w); - } - } - - public void copyPixelsFrom(ImageData input, int dx1, int dy1, int dx2, int dy2, - int sx1, int sy1, int sx2, int sy2) { - if(sx2 - sx1 != dx2 - dx1) { - throw new IllegalArgumentException("Width of the copied region must match the" - + "width of the pasted region"); - } - int cw = sx2 - sx1; - if(sy2 - sy1 != dy2 - dy1) { - throw new IllegalArgumentException("Height of the copied region must match the" - + "height of the pasted region"); - } - int ch = sy2 - sy1; - for(int y = 0; y < ch; ++y) { - System.arraycopy(input.pixels, sx1 + (sy1 + y) * cw, pixels, dx1 + (dy1 + y) * cw, cw); - } - } - - public void drawLayer(ImageData input, int dx1, int dy1, int dx2, int dy2, - int sx1, int sy1, int sx2, int sy2) { - if(sx2 - sx1 != dx2 - dx1) { - throw new IllegalArgumentException("Width of the copied region must match the" - + "width of the pasted region"); - } - int cw = sx2 - sx1; - if(sy2 - sy1 != dy2 - dy1) { - throw new IllegalArgumentException("Height of the copied region must match the" - + "height of the pasted region"); - } - int ch = sy2 - sy1; - for(int y = 0; y < ch; ++y) { - for(int x = 0; x < cw; ++x) { - int si = (sy1 + y) * cw + sx1 + x; - int di = (dy1 + y) * cw + dx1 + x; - int spx = input.pixels[si]; - int dpx = pixels[di]; - if((spx & 0xFF000000) == 0xFF000000 || (dpx & 0xFF000000) == 0) { - pixels[di] = spx; - }else { - int sa = (spx >>> 24) & 0xFF; - int da = (dpx >>> 24) & 0xFF; - int r = ((spx >>> 16) & 0xFF) * sa / 255; - int g = ((spx >>> 8) & 0xFF) * sa / 255; - int b = (spx & 0xFF) * sa / 255; - int aa = (255 - sa) * da; - r += ((dpx >>> 16) & 0xFF) * aa / 65025; - g += ((dpx >>> 8) & 0xFF) * aa / 65025; - b += (dpx & 0xFF) * aa / 65025; - sa += da; - if(sa > 0xFF) sa = 0xFF; - pixels[di] = ((sa) << 24) | (r << 16) | (g << 8) | b; - } - } - } - } - - public ImageData swapRB() { - for(int i = 0; i < pixels.length; ++i) { - int j = pixels[i]; - pixels[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >>> 16) | - ((j & 0x000000FF) << 16); - } - return this; - } - - public static int swapRB(int c) { - return (c & 0xFF00FF00) | ((c & 0x00FF0000) >>> 16) | ((c & 0x000000FF) << 16); - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import java.io.InputStream; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; + +/** + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ImageData { + + public final int width; + public final int height; + public final int[] pixels; + public final boolean alpha; + + public ImageData(int width, int height, int[] pixels, boolean alpha) { + this.width = width; + this.height = height; + this.pixels = pixels; + this.alpha = alpha; + } + + public ImageData(int width, int height, boolean alpha) { + this.width = width; + this.height = height; + this.pixels = new int[width * height]; + this.alpha = alpha; + } + + public ImageData fillAlpha() { + for(int i = 0; i < pixels.length; ++i) { + pixels[i] = pixels[i] | 0xFF000000; + } + return this; + } + + public ImageData getSubImage(int x, int y, int pw, int ph) { + int[] img = new int[pw * ph]; + for(int i = 0; i < ph; ++i) { + System.arraycopy(pixels, (i + y) * this.width + x, img, i * pw, pw); + } + return new ImageData(pw, ph, img, alpha); + } + + public static String getMimeFromType(String nameOrPath) { + if(nameOrPath == null) return "image/png"; + nameOrPath = nameOrPath.toLowerCase(); + if(nameOrPath.endsWith(".png")) { + return "image/png"; + }else if(nameOrPath.endsWith(".jpg") || nameOrPath.endsWith(".jpeg")) { + return "image/jpeg"; + }else if(nameOrPath.endsWith(".gif")) { + return "image/gif"; + }else if(nameOrPath.endsWith(".bmp")) { + return "image/bmp"; + }else { + return "image/png"; // rip + } + } + + public static final ImageData loadImageFile(String path) { + byte[] fileData = EagRuntime.getResourceBytes(path); + if(fileData != null) { + return loadImageFile(fileData); + }else { + return null; + } + } + + public static final ImageData loadImageFile(InputStream data) { + return PlatformAssets.loadImageFile(data); + } + + public static final ImageData loadImageFile(byte[] data) { + return PlatformAssets.loadImageFile(data); + } + + public static final ImageData loadImageFile(String path, String mime) { + byte[] fileData = EagRuntime.getResourceBytes(path); + if(fileData != null) { + return loadImageFile(fileData, mime); + }else { + return null; + } + } + + public static final ImageData loadImageFile(InputStream data, String mime) { + return PlatformAssets.loadImageFile(data, mime); + } + + public static final ImageData loadImageFile(byte[] data, String mime) { + return PlatformAssets.loadImageFile(data, mime); + } + + public void getRGB(int startX, int startY, int w, int h, + int[] rgbArray, int offset, int scansize) { + for(int y = 0; y < h; ++y) { + System.arraycopy(pixels, offset + (y + startY) * scansize + startX, rgbArray, y * w, w); + } + } + + public void copyPixelsFrom(ImageData input, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2) { + if(sx2 - sx1 != dx2 - dx1) { + throw new IllegalArgumentException("Width of the copied region must match the" + + "width of the pasted region"); + } + int cw = sx2 - sx1; + if(sy2 - sy1 != dy2 - dy1) { + throw new IllegalArgumentException("Height of the copied region must match the" + + "height of the pasted region"); + } + int ch = sy2 - sy1; + for(int y = 0; y < ch; ++y) { + System.arraycopy(input.pixels, sx1 + (sy1 + y) * cw, pixels, dx1 + (dy1 + y) * cw, cw); + } + } + + public void drawLayer(ImageData input, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2) { + if(sx2 - sx1 != dx2 - dx1) { + throw new IllegalArgumentException("Width of the copied region must match the" + + "width of the pasted region"); + } + int cw = sx2 - sx1; + if(sy2 - sy1 != dy2 - dy1) { + throw new IllegalArgumentException("Height of the copied region must match the" + + "height of the pasted region"); + } + int ch = sy2 - sy1; + for(int y = 0; y < ch; ++y) { + for(int x = 0; x < cw; ++x) { + int si = (sy1 + y) * cw + sx1 + x; + int di = (dy1 + y) * cw + dx1 + x; + int spx = input.pixels[si]; + int dpx = pixels[di]; + if((spx & 0xFF000000) == 0xFF000000 || (dpx & 0xFF000000) == 0) { + pixels[di] = spx; + }else { + int sa = (spx >>> 24) & 0xFF; + int da = (dpx >>> 24) & 0xFF; + int r = ((spx >>> 16) & 0xFF) * sa / 255; + int g = ((spx >>> 8) & 0xFF) * sa / 255; + int b = (spx & 0xFF) * sa / 255; + int aa = (255 - sa) * da; + r += ((dpx >>> 16) & 0xFF) * aa / 65025; + g += ((dpx >>> 8) & 0xFF) * aa / 65025; + b += (dpx & 0xFF) * aa / 65025; + sa += da; + if(sa > 0xFF) sa = 0xFF; + pixels[di] = ((sa) << 24) | (r << 16) | (g << 8) | b; + } + } + } + } + + public ImageData swapRB() { + for(int i = 0; i < pixels.length; ++i) { + int j = pixels[i]; + pixels[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >>> 16) | + ((j & 0x000000FF) << 16); + } + return this; + } + + public static int swapRB(int c) { + return (c & 0xFF00FF00) | ((c & 0x00FF0000) >>> 16) | ((c & 0x000000FF) << 16); + } + + public static int[] swapRB(int[] arr) { + for(int i = 0; i < arr.length; ++i) { + int j = arr[i]; + arr[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >>> 16) | + ((j & 0x000000FF) << 16); + } + return arr; + } + + public boolean isNPOT() { + return (width & (width - 1)) != 0 || (height & (height - 1)) != 0; + } + + public static boolean isNPOTStatic(int w, int h) { + return (w & (w - 1)) != 0 || (h & (h - 1)) != 0; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java index 0a36fd0..256098d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java @@ -1,458 +1,494 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; -import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; -import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class InstancedFontRenderer { - - private static final Logger logger = LogManager.getLogger("InstancedFontRenderer"); - - public static final String vertexShaderPath = "/assets/eagler/glsl/accel_font.vsh"; - public static final String fragmentShaderPath = "/assets/eagler/glsl/accel_font.fsh"; - - private static final int BYTES_PER_CHARACTER = 10; - private static final int CHARACTER_LIMIT = 6553; - - private static IProgramGL shaderProgram = null; - private static IUniformGL u_matrixTransform = null; - private static FloatBuffer matrixCopyBuffer = null; - private static IUniformGL u_charSize2f = null; - private static IUniformGL u_charCoordSize2f = null; - private static IUniformGL u_color4f = null; - private static IUniformGL u_colorBias4f = null; - - private static IBufferArrayGL vertexArray = null; - private static IBufferGL vertexBuffer = null; - - private static IBufferGL instancesBuffer = null; - - private static float stateColorR = -999.0f; - private static float stateColorG = -999.0f; - private static float stateColorB = -999.0f; - private static float stateColorA = -999.0f; - private static int stateColorSerial = -1; - - private static float stateColorBiasR = -999.0f; - private static float stateColorBiasG = -999.0f; - private static float stateColorBiasB = -999.0f; - private static float stateColorBiasA = -999.0f; - - private static final Matrix4f tmpMatrix = new Matrix4f(); - private static final Vector4f tmpVector = new Vector4f(); - private static int stateModelMatrixSerial = -1; - private static int stateProjectionMatrixSerial = -1; - - private static float charWidthValue = -1; - private static float charHeightValue = -1; - private static float charCoordWidthValue = -1.0f; - private static float charCoordHeightValue = -1.0f; - - static void initialize() { - - String vertexSource = EagRuntime.getResourceString(vertexShaderPath); - if(vertexSource == null) { - throw new RuntimeException("InstancedFontRenderer shader \"" + vertexShaderPath + "\" is missing!"); - } - - String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath); - if(fragmentSource == null) { - throw new RuntimeException("InstancedFontRenderer shader \"" + fragmentShaderPath + "\" is missing!"); - } - - IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER); - IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); - - _wglShaderSource(vert, FixedFunctionConstants.VERSION + "\n" + vertexSource); - _wglCompileShader(vert); - - if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { - logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for InstancedFontRenderer!"); - String log = _wglGetShaderInfoLog(vert); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - logger.error("[VERT] {}", lines[i]); - } - } - throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); - } - - _wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource); - _wglCompileShader(frag); - - if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { - logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for InstancedFontRenderer!"); - String log = _wglGetShaderInfoLog(frag); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - logger.error("[FRAG] {}", lines[i]); - } - } - throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); - } - - shaderProgram = _wglCreateProgram(); - - _wglAttachShader(shaderProgram, vert); - _wglAttachShader(shaderProgram, frag); - - _wglLinkProgram(shaderProgram); - - _wglDetachShader(shaderProgram, vert); - _wglDetachShader(shaderProgram, frag); - - _wglDeleteShader(vert); - _wglDeleteShader(frag); - - if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { - logger.error("Failed to link shader program for InstancedFontRenderer!"); - String log = _wglGetProgramInfoLog(shaderProgram); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - logger.error("[LINK] {}", lines[i]); - } - } - throw new IllegalStateException("Shader program for InstancedFontRenderer could not be linked!"); - } - - matrixCopyBuffer = EagRuntime.allocateFloatBuffer(16); - fontDataBuffer = EagRuntime.allocateByteBuffer(CHARACTER_LIMIT * BYTES_PER_CHARACTER); - fontBoldDataBuffer = EagRuntime.allocateByteBuffer(CHARACTER_LIMIT * BYTES_PER_CHARACTER); - - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform"); - u_charSize2f = _wglGetUniformLocation(shaderProgram, "u_charSize2f"); - u_charCoordSize2f = _wglGetUniformLocation(shaderProgram, "u_charCoordSize2f"); - u_color4f = _wglGetUniformLocation(shaderProgram, "u_color4f"); - u_colorBias4f = _wglGetUniformLocation(shaderProgram, "u_colorBias4f"); - - _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0); - - vertexArray = _wglGenVertexArrays(); - vertexBuffer = _wglGenBuffers(); - instancesBuffer = _wglGenBuffers(); - - FloatBuffer verts = EagRuntime.allocateFloatBuffer(108); - verts.put(new float[] { - - // (0 - 6 - 12) regular: - - 0.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 0.0f, 0.25f, - 1.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 1.0f, 0.25f, - 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, - - // (12 - 24 - 36) bold shadow: - - 0.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 0.0f, 0.25f, - 1.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 1.0f, 0.25f, - 0.0f, 0.0f, 0.75f, 0.0f, 1.0f, 0.75f, 1.0f, 0.0f, 0.75f, - 1.0f, 0.0f, 0.75f, 0.0f, 1.0f, 0.75f, 1.0f, 1.0f, 0.75f, - - 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.5f, 0.0f, 1.0f, 0.5f, 1.0f, 0.0f, 0.5f, - 1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f - - }); - verts.flip(); - - EaglercraftGPU.bindGLBufferArray(vertexArray); - - EaglercraftGPU.bindGLArrayBuffer(vertexBuffer); - _wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW); - - EagRuntime.freeFloatBuffer(verts); - - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0); - _wglVertexAttribDivisor(0, 0); - - EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, fontDataBuffer.remaining(), GL_STREAM_DRAW); - - _wglEnableVertexAttribArray(1); - _wglVertexAttribPointer(1, 2, GL_SHORT, false, 10, 0); - _wglVertexAttribDivisor(1, 1); - - _wglEnableVertexAttribArray(2); - _wglVertexAttribPointer(2, 2, GL_UNSIGNED_BYTE, false, 10, 4); - _wglVertexAttribDivisor(2, 1); - - _wglEnableVertexAttribArray(3); - _wglVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, true, 10, 6); - _wglVertexAttribDivisor(3, 1); - - } - - private static ByteBuffer fontDataBuffer = null; - private static int charactersDrawn = 0; - private static ByteBuffer fontBoldDataBuffer = null; - private static int boldCharactersDrawn = 0; - private static boolean hasOverflowed = false; - private static boolean hasBoldOverflowed = false; - - private static boolean fogEnabled = false; - private static int widthCalcLeast = Integer.MAX_VALUE; - private static int heightCalcLeast = Integer.MAX_VALUE; - private static int widthCalcMost = Integer.MAX_VALUE; - private static int heightCalcMost = Integer.MAX_VALUE; - - public static void begin() { - fontDataBuffer.clear(); - charactersDrawn = 0; - fontBoldDataBuffer.clear(); - boldCharactersDrawn = 0; - hasOverflowed = false; - hasBoldOverflowed = false; - fogEnabled = GlStateManager.stateFog && GlStateManager.stateFogDensity > 0.0f; - if(fogEnabled) { - widthCalcLeast = Integer.MAX_VALUE; - heightCalcLeast = Integer.MAX_VALUE; - widthCalcMost = Integer.MAX_VALUE; - heightCalcMost = Integer.MAX_VALUE; - } - } - - public static void render(float charWidth, float charHeight, float charCoordWidth, float charCoordHeight, boolean shadow) { - if(charactersDrawn == 0 && boldCharactersDrawn == 0) { - return; - } - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - if(charWidth != charWidthValue || charHeight != charHeightValue) { - charWidthValue = charWidth; - charHeightValue = charHeight; - _wglUniform2f(u_charSize2f, (float)charWidth, (float)charHeight); - } - - if(charCoordWidth != charCoordWidthValue || charCoordHeight != charCoordHeightValue) { - charCoordWidthValue = charCoordWidth; - charCoordHeightValue = charCoordHeight; - _wglUniform2f(u_charCoordSize2f, charCoordWidth, charCoordHeight); - } - - int ptr1 = GlStateManager.modelMatrixStackPointer; - int serial1 = GlStateManager.modelMatrixStackAccessSerial[ptr1]; - int ptr2 = GlStateManager.projectionMatrixStackPointer; - int serial2 = GlStateManager.projectionMatrixStackAccessSerial[ptr2]; - if(stateModelMatrixSerial != serial1 || stateProjectionMatrixSerial != serial2) { - stateModelMatrixSerial = serial1; - stateProjectionMatrixSerial = serial2; - Matrix4f.mul(GlStateManager.projectionMatrixStack[ptr2], GlStateManager.modelMatrixStack[ptr1], tmpMatrix); - matrixCopyBuffer.clear(); - tmpMatrix.store(matrixCopyBuffer); - matrixCopyBuffer.flip(); - _wglUniformMatrix4fv(u_matrixTransform, false, matrixCopyBuffer); - } - - if(!fogEnabled || DeferredStateManager.isInDeferredPass()) { - int serial = GlStateManager.stateColorSerial; - if(stateColorSerial != serial) { - stateColorSerial = serial; - float r = GlStateManager.stateColorR; - float g = GlStateManager.stateColorG; - float b = GlStateManager.stateColorB; - float a = GlStateManager.stateColorA; - if(stateColorR != r || stateColorG != g || - stateColorB != b || stateColorA != a) { - _wglUniform4f(u_color4f, r, g, b, a); - stateColorR = r; - stateColorG = g; - stateColorB = b; - stateColorA = a; - } - } - if(stateColorBiasR != 0.0f || stateColorBiasG != 0.0f || - stateColorBiasB != 0.0f || stateColorBiasA != 0.0f) { - _wglUniform4f(u_colorBias4f, 0.0f, 0.0f, 0.0f, 0.0f); - stateColorBiasR = 0.0f; - stateColorBiasG = 0.0f; - stateColorBiasB = 0.0f; - stateColorBiasA = 0.0f; - } - }else { - stateColorSerial = -1; - Vector4f vec4 = tmpVector; - vec4.x = (float)(widthCalcLeast + (widthCalcMost - widthCalcLeast + 1.0f) * 0.5f) * charWidth; - vec4.y = (float)(heightCalcLeast + (heightCalcMost - heightCalcLeast + 1.0f) * 0.5f)* charHeight; - vec4.z = 0.0f; - vec4.w = 1.0f; - - Matrix4f.transform(GlStateManager.modelMatrixStack[ptr1], vec4, vec4); - - vec4.x /= vec4.w; - vec4.y /= vec4.w; - vec4.z /= vec4.w; - vec4.w = 1.0f; - - vec4.x *= vec4.x; - vec4.y *= vec4.y; - vec4.z *= vec4.z; - - float fogFactor = (float) Math.sqrt(vec4.x + vec4.y + vec4.z); - if(GlStateManager.stateFogEXP) { - fogFactor = 1.0f - (float) Math.pow(2.718, -(GlStateManager.stateFogDensity * fogFactor)); - }else { - fogFactor = (fogFactor - GlStateManager.stateFogStart) / (GlStateManager.stateFogEnd - GlStateManager.stateFogStart); - } - - if(fogFactor > 1.0f) fogFactor = 1.0f; - if(fogFactor < 0.0f) fogFactor = 0.0f; - - float r = GlStateManager.stateColorR; - float g = GlStateManager.stateColorG; - float b = GlStateManager.stateColorB; - float a = GlStateManager.stateColorA; - - float fogFactor2 = (1.0f - fogFactor) * GlStateManager.stateFogColorA; - r *= fogFactor2; - g *= fogFactor2; - b *= fogFactor2; - - if(stateColorR != r || stateColorG != g || - stateColorB != b || stateColorA != a) { - _wglUniform4f(u_color4f, r, g, b, a); - stateColorR = r; - stateColorG = g; - stateColorB = b; - stateColorA = a; - } - - fogFactor *= GlStateManager.stateFogColorA; - float biasR = GlStateManager.stateFogColorR * fogFactor; - float biasG = GlStateManager.stateFogColorG * fogFactor; - float biasB = GlStateManager.stateFogColorB * fogFactor; - float biasA = 0.0f; - - if(stateColorBiasR != biasR || stateColorBiasG != biasG || - stateColorBiasB != biasB || stateColorBiasA != biasA) { - _wglUniform4f(u_colorBias4f, biasR, biasG, biasB, biasA); - stateColorBiasR = biasR; - stateColorBiasG = biasG; - stateColorBiasB = biasB; - stateColorBiasA = biasA; - } - } - - EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - EaglercraftGPU.bindGLBufferArray(vertexArray); - - if(charactersDrawn > 0) { - int p = fontDataBuffer.position(); - int l = fontDataBuffer.limit(); - - fontDataBuffer.flip(); - _wglBufferSubData(GL_ARRAY_BUFFER, 0, fontDataBuffer); - - fontDataBuffer.position(p); - fontDataBuffer.limit(l); - - _wglDrawArraysInstanced(GL_TRIANGLES, shadow ? 0 : 6, shadow ? 12 : 6, charactersDrawn); - } - - if(boldCharactersDrawn > 0) { - int p = fontBoldDataBuffer.position(); - int l = fontBoldDataBuffer.limit(); - - fontBoldDataBuffer.flip(); - _wglBufferSubData(GL_ARRAY_BUFFER, 0, fontBoldDataBuffer); - - fontBoldDataBuffer.position(p); - fontBoldDataBuffer.limit(l); - - _wglDrawArraysInstanced(GL_TRIANGLES, shadow ? 12 : 24, shadow ? 24 : 12, boldCharactersDrawn); - } - } - - public static void appendQuad(int x, int y, int cx, int cy, int color, boolean italic) { - if(hasOverflowed) { - return; - } - if(charactersDrawn >= CHARACTER_LIMIT) { - hasOverflowed = true; - logger.error("Font renderer buffer has overflowed! Exceeded {} regular characters, no more regular characters will be rendered.", CHARACTER_LIMIT); - return; - } - ++charactersDrawn; - ByteBuffer buf = fontDataBuffer; - buf.putShort((short)x); - buf.putShort((short)y); - buf.put((byte)cx); - buf.put((byte)cy); - color = ((color >>> 1) & 0x7F000000) | (color & 0xFFFFFF); - if(italic) { - color |= 0x80000000; - } - buf.putInt(color); - if(fogEnabled) { - updateBounds(x, y); - } - } - - public static void appendBoldQuad(int x, int y, int cx, int cy, int color, boolean italic) { - if(hasBoldOverflowed) { - return; - } - if(boldCharactersDrawn >= CHARACTER_LIMIT) { - hasBoldOverflowed = true; - logger.error("Font renderer buffer has overflowed! Exceeded {} bold characters, no more bold characters will be rendered.", CHARACTER_LIMIT); - return; - } - ++boldCharactersDrawn; - ByteBuffer buf = fontBoldDataBuffer; - buf.putShort((short)x); - buf.putShort((short)y); - buf.put((byte)cx); - buf.put((byte)cy); - color = ((color >>> 1) & 0x7F000000) | (color & 0xFFFFFF); - if(italic) { - color |= 0x80000000; - } - buf.putInt(color); - if(fogEnabled) { - updateBounds(x, y); - } - } - - private static final void updateBounds(int x, int y) { - if(x < widthCalcLeast || widthCalcLeast == Integer.MAX_VALUE) widthCalcLeast = x; - if(x > widthCalcMost || widthCalcMost == Integer.MAX_VALUE) widthCalcMost = x; - if(y < heightCalcLeast || heightCalcLeast == Integer.MAX_VALUE) heightCalcLeast = y; - if(y > heightCalcMost || heightCalcMost == Integer.MAX_VALUE) heightCalcMost = y; - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; +import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; +import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class InstancedFontRenderer { + + private static final Logger logger = LogManager.getLogger("InstancedFontRenderer"); + + public static final String vertexShaderPath = "/assets/eagler/glsl/accel_font.vsh"; + public static final String vertexShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision mediump sampler2D;\n"; + + public static final String fragmentShaderPath = "/assets/eagler/glsl/accel_font.fsh"; + public static final String fragmentShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision mediump sampler2D;\n"; + + private static final int BYTES_PER_CHARACTER = 10; + private static final int CHARACTER_LIMIT = 6553; + + private static IProgramGL shaderProgram = null; + private static IUniformGL u_matrixTransform = null; + private static FloatBuffer matrixCopyBuffer = null; + private static IUniformGL u_charSize2f = null; + private static IUniformGL u_charCoordSize2f = null; + private static IUniformGL u_color4f = null; + private static IUniformGL u_colorBias4f = null; + + private static IBufferArrayGL vertexArray = null; + private static IBufferGL vertexBuffer = null; + + private static IBufferGL instancesBuffer = null; + + private static float stateColorR = -999.0f; + private static float stateColorG = -999.0f; + private static float stateColorB = -999.0f; + private static float stateColorA = -999.0f; + private static int stateColorSerial = -1; + + private static float stateColorBiasR = -999.0f; + private static float stateColorBiasG = -999.0f; + private static float stateColorBiasB = -999.0f; + private static float stateColorBiasA = -999.0f; + + private static final Matrix4f tmpMatrix = new Matrix4f(); + private static final Vector4f tmpVector = new Vector4f(); + private static int stateModelMatrixSerial = -1; + private static int stateProjectionMatrixSerial = -1; + + private static float charWidthValue = -1; + private static float charHeightValue = -1; + private static float charCoordWidthValue = -1.0f; + private static float charCoordHeightValue = -1.0f; + + static void initialize() { + String vertexSource = EagRuntime.getRequiredResourceString(vertexShaderPath); + String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath); + + IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER); + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + + _wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vertexSource, vertexShaderPrecision)); + _wglCompileShader(vert); + + if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { + logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for InstancedFontRenderer!"); + String log = _wglGetShaderInfoLog(vert); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + logger.error("[VERT] {}", lines[i]); + } + } + throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); + } + + _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision)); + _wglCompileShader(frag); + + if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for InstancedFontRenderer!"); + String log = _wglGetShaderInfoLog(frag); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + logger.error("[FRAG] {}", lines[i]); + } + } + throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); + } + + shaderProgram = _wglCreateProgram(); + + _wglAttachShader(shaderProgram, vert); + _wglAttachShader(shaderProgram, frag); + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + VSHInputLayoutParser.applyLayout(shaderProgram, VSHInputLayoutParser.getShaderInputs(vertexSource)); + } + + _wglLinkProgram(shaderProgram); + + _wglDetachShader(shaderProgram, vert); + _wglDetachShader(shaderProgram, frag); + + _wglDeleteShader(vert); + _wglDeleteShader(frag); + + if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + logger.error("Failed to link shader program for InstancedFontRenderer!"); + String log = _wglGetProgramInfoLog(shaderProgram); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + logger.error("[LINK] {}", lines[i]); + } + } + throw new IllegalStateException("Shader program for InstancedFontRenderer could not be linked!"); + } + + matrixCopyBuffer = EagRuntime.allocateFloatBuffer(16); + fontDataBuffer = EagRuntime.allocateByteBuffer(CHARACTER_LIMIT * BYTES_PER_CHARACTER); + fontBoldDataBuffer = EagRuntime.allocateByteBuffer(CHARACTER_LIMIT * BYTES_PER_CHARACTER); + + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform"); + u_charSize2f = _wglGetUniformLocation(shaderProgram, "u_charSize2f"); + u_charCoordSize2f = _wglGetUniformLocation(shaderProgram, "u_charCoordSize2f"); + u_color4f = _wglGetUniformLocation(shaderProgram, "u_color4f"); + u_colorBias4f = _wglGetUniformLocation(shaderProgram, "u_colorBias4f"); + + _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0); + + vertexArray = EaglercraftGPU.createGLBufferArray(); + vertexBuffer = _wglGenBuffers(); + instancesBuffer = _wglGenBuffers(); + + FloatBuffer verts = EagRuntime.allocateFloatBuffer(108); + float paddingA = 0.005f; + float paddingB = 1.0f - paddingA; + verts.put(new float[] { + + // (0 - 6 - 12) regular: + + paddingA, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingA, 0.25f, + paddingB, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingB, 0.25f, + paddingA, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingA, 0.0f, + paddingB, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingB, 0.0f, + + // (12 - 24 - 36) bold shadow: + + paddingA, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingA, 0.25f, + paddingB, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingB, 0.25f, + paddingA, paddingA, 0.75f, paddingA, paddingB, 0.75f, paddingB, paddingA, 0.75f, + paddingB, paddingA, 0.75f, paddingA, paddingB, 0.75f, paddingB, paddingB, 0.75f, + + paddingA, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingA, 0.0f, + paddingB, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingB, 0.0f, + paddingA, paddingA, 0.5f, paddingA, paddingB, 0.5f, paddingB, paddingA, 0.5f, + paddingB, paddingA, 0.5f, paddingA, paddingB, 0.5f, paddingB, paddingB, 0.5f + + }); + verts.flip(); + + EaglercraftGPU.bindGLBufferArray(vertexArray); + + EaglercraftGPU.bindVAOGLArrayBufferNow(vertexBuffer); + _wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW); + + EagRuntime.freeFloatBuffer(verts); + + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0); + EaglercraftGPU.vertexAttribDivisor(0, 0); + + EaglercraftGPU.bindVAOGLArrayBufferNow(instancesBuffer); + _wglBufferData(GL_ARRAY_BUFFER, fontDataBuffer.remaining(), GL_STREAM_DRAW); + + EaglercraftGPU.enableVertexAttribArray(1); + EaglercraftGPU.vertexAttribPointer(1, 2, GL_SHORT, false, 10, 0); + EaglercraftGPU.vertexAttribDivisor(1, 1); + + EaglercraftGPU.enableVertexAttribArray(2); + EaglercraftGPU.vertexAttribPointer(2, 2, GL_UNSIGNED_BYTE, false, 10, 4); + EaglercraftGPU.vertexAttribDivisor(2, 1); + + EaglercraftGPU.enableVertexAttribArray(3); + EaglercraftGPU.vertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, true, 10, 6); + EaglercraftGPU.vertexAttribDivisor(3, 1); + + } + + private static ByteBuffer fontDataBuffer = null; + private static int charactersDrawn = 0; + private static ByteBuffer fontBoldDataBuffer = null; + private static int boldCharactersDrawn = 0; + private static boolean hasOverflowed = false; + private static boolean hasBoldOverflowed = false; + + private static boolean fogEnabled = false; + private static int widthCalcLeast = Integer.MAX_VALUE; + private static int heightCalcLeast = Integer.MAX_VALUE; + private static int widthCalcMost = Integer.MAX_VALUE; + private static int heightCalcMost = Integer.MAX_VALUE; + + public static void begin() { + fontDataBuffer.clear(); + charactersDrawn = 0; + fontBoldDataBuffer.clear(); + boldCharactersDrawn = 0; + hasOverflowed = false; + hasBoldOverflowed = false; + fogEnabled = GlStateManager.stateFog && GlStateManager.stateFogDensity > 0.0f; + if(fogEnabled) { + widthCalcLeast = Integer.MAX_VALUE; + heightCalcLeast = Integer.MAX_VALUE; + widthCalcMost = Integer.MAX_VALUE; + heightCalcMost = Integer.MAX_VALUE; + } + } + + public static void render(float charWidth, float charHeight, float charCoordWidth, float charCoordHeight, boolean shadow) { + if(charactersDrawn == 0 && boldCharactersDrawn == 0) { + return; + } + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + if(charWidth != charWidthValue || charHeight != charHeightValue) { + charWidthValue = charWidth; + charHeightValue = charHeight; + _wglUniform2f(u_charSize2f, (float)charWidth, (float)charHeight); + } + + if(charCoordWidth != charCoordWidthValue || charCoordHeight != charCoordHeightValue) { + charCoordWidthValue = charCoordWidth; + charCoordHeightValue = charCoordHeight; + _wglUniform2f(u_charCoordSize2f, charCoordWidth, charCoordHeight); + } + + int ptr1 = GlStateManager.modelMatrixStackPointer; + int serial1 = GlStateManager.modelMatrixStackAccessSerial[ptr1]; + int ptr2 = GlStateManager.projectionMatrixStackPointer; + int serial2 = GlStateManager.projectionMatrixStackAccessSerial[ptr2]; + if(stateModelMatrixSerial != serial1 || stateProjectionMatrixSerial != serial2) { + stateModelMatrixSerial = serial1; + stateProjectionMatrixSerial = serial2; + Matrix4f.mul(GlStateManager.projectionMatrixStack[ptr2], GlStateManager.modelMatrixStack[ptr1], tmpMatrix); + matrixCopyBuffer.clear(); + tmpMatrix.store(matrixCopyBuffer); + matrixCopyBuffer.flip(); + _wglUniformMatrix4fv(u_matrixTransform, false, matrixCopyBuffer); + } + + if(!fogEnabled || DeferredStateManager.isInDeferredPass()) { + int serial = GlStateManager.stateColorSerial; + if(stateColorSerial != serial) { + stateColorSerial = serial; + float r = GlStateManager.stateColorR; + float g = GlStateManager.stateColorG; + float b = GlStateManager.stateColorB; + float a = GlStateManager.stateColorA; + if(stateColorR != r || stateColorG != g || + stateColorB != b || stateColorA != a) { + _wglUniform4f(u_color4f, r, g, b, a); + stateColorR = r; + stateColorG = g; + stateColorB = b; + stateColorA = a; + } + } + if(stateColorBiasR != 0.0f || stateColorBiasG != 0.0f || + stateColorBiasB != 0.0f || stateColorBiasA != 0.0f) { + _wglUniform4f(u_colorBias4f, 0.0f, 0.0f, 0.0f, 0.0f); + stateColorBiasR = 0.0f; + stateColorBiasG = 0.0f; + stateColorBiasB = 0.0f; + stateColorBiasA = 0.0f; + } + }else { + stateColorSerial = -1; + Vector4f vec4 = tmpVector; + vec4.x = (float)(widthCalcLeast + (widthCalcMost - widthCalcLeast + 1.0f) * 0.5f) * charWidth; + vec4.y = (float)(heightCalcLeast + (heightCalcMost - heightCalcLeast + 1.0f) * 0.5f)* charHeight; + vec4.z = 0.0f; + vec4.w = 1.0f; + + Matrix4f.transform(GlStateManager.modelMatrixStack[ptr1], vec4, vec4); + + vec4.x /= vec4.w; + vec4.y /= vec4.w; + vec4.z /= vec4.w; + vec4.w = 1.0f; + + vec4.x *= vec4.x; + vec4.y *= vec4.y; + vec4.z *= vec4.z; + + float fogFactor = (float) Math.sqrt(vec4.x + vec4.y + vec4.z); + if(GlStateManager.stateFogEXP) { + fogFactor = 1.0f - (float) Math.pow(2.718, -(GlStateManager.stateFogDensity * fogFactor)); + }else { + fogFactor = (fogFactor - GlStateManager.stateFogStart) / (GlStateManager.stateFogEnd - GlStateManager.stateFogStart); + } + + if(fogFactor > 1.0f) fogFactor = 1.0f; + if(fogFactor < 0.0f) fogFactor = 0.0f; + + float r = GlStateManager.stateColorR; + float g = GlStateManager.stateColorG; + float b = GlStateManager.stateColorB; + float a = GlStateManager.stateColorA; + + float fogFactor2 = (1.0f - fogFactor) * GlStateManager.stateFogColorA; + r *= fogFactor2; + g *= fogFactor2; + b *= fogFactor2; + + if(stateColorR != r || stateColorG != g || + stateColorB != b || stateColorA != a) { + _wglUniform4f(u_color4f, r, g, b, a); + stateColorR = r; + stateColorG = g; + stateColorB = b; + stateColorA = a; + } + + fogFactor *= GlStateManager.stateFogColorA; + float biasR = GlStateManager.stateFogColorR * fogFactor; + float biasG = GlStateManager.stateFogColorG * fogFactor; + float biasB = GlStateManager.stateFogColorB * fogFactor; + float biasA = 0.0f; + + if(stateColorBiasR != biasR || stateColorBiasG != biasG || + stateColorBiasB != biasB || stateColorBiasA != biasA) { + _wglUniform4f(u_colorBias4f, biasR, biasG, biasB, biasA); + stateColorBiasR = biasR; + stateColorBiasG = biasG; + stateColorBiasB = biasB; + stateColorBiasA = biasA; + } + } + + EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); + EaglercraftGPU.bindGLBufferArray(vertexArray); + + if(charactersDrawn > 0) { + int p = fontDataBuffer.position(); + int l = fontDataBuffer.limit(); + + fontDataBuffer.flip(); + _wglBufferSubData(GL_ARRAY_BUFFER, 0, fontDataBuffer); + + fontDataBuffer.position(p); + fontDataBuffer.limit(l); + + EaglercraftGPU.doDrawArraysInstanced(GL_TRIANGLES, shadow ? 0 : 6, shadow ? 12 : 6, charactersDrawn); + } + + if(boldCharactersDrawn > 0) { + int p = fontBoldDataBuffer.position(); + int l = fontBoldDataBuffer.limit(); + + fontBoldDataBuffer.flip(); + _wglBufferSubData(GL_ARRAY_BUFFER, 0, fontBoldDataBuffer); + + fontBoldDataBuffer.position(p); + fontBoldDataBuffer.limit(l); + + EaglercraftGPU.doDrawArraysInstanced(GL_TRIANGLES, shadow ? 12 : 24, shadow ? 24 : 12, boldCharactersDrawn); + } + } + + public static void appendQuad(int x, int y, int cx, int cy, int color, boolean italic) { + if(hasOverflowed) { + return; + } + if(charactersDrawn >= CHARACTER_LIMIT) { + hasOverflowed = true; + logger.error("Font renderer buffer has overflowed! Exceeded {} regular characters, no more regular characters will be rendered.", CHARACTER_LIMIT); + return; + } + ++charactersDrawn; + ByteBuffer buf = fontDataBuffer; + buf.putShort((short)x); + buf.putShort((short)y); + buf.put((byte)cx); + buf.put((byte)cy); + color = ((color >>> 1) & 0x7F000000) | (color & 0xFFFFFF); + if(italic) { + color |= 0x80000000; + } + buf.putInt(color); + if(fogEnabled) { + updateBounds(x, y); + } + } + + public static void appendBoldQuad(int x, int y, int cx, int cy, int color, boolean italic) { + if(hasBoldOverflowed) { + return; + } + if(boldCharactersDrawn >= CHARACTER_LIMIT) { + hasBoldOverflowed = true; + logger.error("Font renderer buffer has overflowed! Exceeded {} bold characters, no more bold characters will be rendered.", CHARACTER_LIMIT); + return; + } + ++boldCharactersDrawn; + ByteBuffer buf = fontBoldDataBuffer; + buf.putShort((short)x); + buf.putShort((short)y); + buf.put((byte)cx); + buf.put((byte)cy); + color = ((color >>> 1) & 0x7F000000) | (color & 0xFFFFFF); + if(italic) { + color |= 0x80000000; + } + buf.putInt(color); + if(fogEnabled) { + updateBounds(x, y); + } + } + + private static final void updateBounds(int x, int y) { + if(x < widthCalcLeast || widthCalcLeast == Integer.MAX_VALUE) widthCalcLeast = x; + if(x > widthCalcMost || widthCalcMost == Integer.MAX_VALUE) widthCalcMost = x; + if(y < heightCalcLeast || heightCalcLeast == Integer.MAX_VALUE) heightCalcLeast = y; + if(y > heightCalcMost || heightCalcMost == Integer.MAX_VALUE) heightCalcMost = y; + } + + public static void destroy() { + if(fontDataBuffer != null) { + EagRuntime.freeByteBuffer(fontDataBuffer); + fontDataBuffer = null; + } + if(fontBoldDataBuffer != null) { + EagRuntime.freeByteBuffer(fontBoldDataBuffer); + fontBoldDataBuffer = null; + } + if(shaderProgram != null) { + _wglDeleteProgram(shaderProgram); + shaderProgram = null; + } + if(matrixCopyBuffer != null) { + EagRuntime.freeFloatBuffer(matrixCopyBuffer); + matrixCopyBuffer = null; + } + u_matrixTransform = null; + u_charSize2f = null; + u_charCoordSize2f = null; + u_color4f = null; + u_colorBias4f = null; + if(vertexArray != null) { + EaglercraftGPU.destroyGLBufferArray(vertexArray); + vertexArray = null; + } + if(vertexBuffer != null) { + _wglDeleteBuffers(vertexBuffer); + vertexBuffer = null; + } + if(instancesBuffer != null) { + _wglDeleteBuffers(instancesBuffer); + instancesBuffer = null; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java index 669b878..1f19150 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java @@ -1,339 +1,369 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants; -import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class InstancedParticleRenderer { - - private static final Logger logger = LogManager.getLogger("InstancedParticleRenderer"); - - public static final String vertexShaderPath = "/assets/eagler/glsl/accel_particle.vsh"; - public static final String fragmentShaderPath = "/assets/eagler/glsl/accel_particle.fsh"; - - private static ByteBuffer particleBuffer = null; - private static int particleCount = 0; - private static boolean particlesHasOverflowed = false; - - private static final int BYTES_PER_PARTICLE = 24; - private static final int PARTICLE_LIMIT = 5461; - - private static IProgramGL shaderProgram = null; - private static IUniformGL u_matrixTransform = null; - private static FloatBuffer matrixCopyBuffer = null; - private static IUniformGL u_texCoordSize2f_particleSize1f = null; - private static IUniformGL u_transformParam_1_2_5_f = null; - private static IUniformGL u_transformParam_3_4_f = null; - private static IUniformGL u_color4f = null; - - private static IBufferArrayGL vertexArray = null; - private static IBufferGL vertexBuffer = null; - - private static IBufferGL instancesBuffer = null; - - private static float stateColorR = -999.0f; - private static float stateColorG = -999.0f; - private static float stateColorB = -999.0f; - private static float stateColorA = -999.0f; - private static int stateColorSerial = -1; - - private static final Matrix4f tmpMatrix = new Matrix4f(); - private static int stateModelMatrixSerial = -1; - private static int stateProjectionMatrixSerial = -1; - - private static float stateTexCoordWidth = -999.0f; - private static float stateTexCoordHeight = -999.0f; - private static float stateParticleCoordSize = -999.0f; - - private static float stateTransformParam1 = -999.0f; - private static float stateTransformParam2 = -999.0f; - private static float stateTransformParam3 = -999.0f; - private static float stateTransformParam4 = -999.0f; - private static float stateTransformParam5 = -999.0f; - - static void initialize() { - String vertexSource = EagRuntime.getResourceString(vertexShaderPath); - if (vertexSource == null) { - throw new RuntimeException("InstancedParticleRenderer shader \"" + vertexShaderPath + "\" is missing!"); - } - - String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath); - if (fragmentSource == null) { - throw new RuntimeException("InstancedParticleRenderer shader \"" + fragmentShaderPath + "\" is missing!"); - } - - IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER); - IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); - - _wglShaderSource(vert, FixedFunctionConstants.VERSION + "\n" + vertexSource); - _wglCompileShader(vert); - - if (_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { - logger.error( - "Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for InstancedParticleRenderer!"); - String log = _wglGetShaderInfoLog(vert); - if (log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for (int i = 0; i < lines.length; ++i) { - logger.error("[VERT] {}", lines[i]); - } - } - throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); - } - - _wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource); - _wglCompileShader(frag); - - if (_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { - logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath - + "\" for InstancedParticleRenderer!"); - String log = _wglGetShaderInfoLog(frag); - if (log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for (int i = 0; i < lines.length; ++i) { - logger.error("[FRAG] {}", lines[i]); - } - } - throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); - } - - shaderProgram = _wglCreateProgram(); - - _wglAttachShader(shaderProgram, vert); - _wglAttachShader(shaderProgram, frag); - - _wglLinkProgram(shaderProgram); - - _wglDetachShader(shaderProgram, vert); - _wglDetachShader(shaderProgram, frag); - - _wglDeleteShader(vert); - _wglDeleteShader(frag); - - if (_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { - logger.error("Failed to link shader program for InstancedParticleRenderer!"); - String log = _wglGetProgramInfoLog(shaderProgram); - if (log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for (int i = 0; i < lines.length; ++i) { - logger.error("[LINK] {}", lines[i]); - } - } - throw new IllegalStateException("Shader program for InstancedParticleRenderer could not be linked!"); - } - - matrixCopyBuffer = EagRuntime.allocateFloatBuffer(16); - particleBuffer = EagRuntime.allocateByteBuffer(PARTICLE_LIMIT * BYTES_PER_PARTICLE); - - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform"); - u_texCoordSize2f_particleSize1f = _wglGetUniformLocation(shaderProgram, "u_texCoordSize2f_particleSize1f"); - u_transformParam_1_2_5_f = _wglGetUniformLocation(shaderProgram, "u_transformParam_1_2_5_f"); - u_transformParam_3_4_f = _wglGetUniformLocation(shaderProgram, "u_transformParam_3_4_f"); - u_color4f = _wglGetUniformLocation(shaderProgram, "u_color4f"); - - _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0); - _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_lightmapTexture"), 1); - - vertexArray = _wglGenVertexArrays(); - vertexBuffer = _wglGenBuffers(); - instancesBuffer = _wglGenBuffers(); - - FloatBuffer verts = EagRuntime.allocateFloatBuffer(12); - verts.put(new float[] { - -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, - -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f - }); - verts.flip(); - - EaglercraftGPU.bindGLBufferArray(vertexArray); - - EaglercraftGPU.bindGLArrayBuffer(vertexBuffer); - _wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW); - - EagRuntime.freeFloatBuffer(verts); - - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); - _wglVertexAttribDivisor(0, 0); - - EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW); - - _wglEnableVertexAttribArray(1); - _wglVertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0); - _wglVertexAttribDivisor(1, 1); - - _wglEnableVertexAttribArray(2); - _wglVertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, false, 24, 12); - _wglVertexAttribDivisor(2, 1); - - _wglEnableVertexAttribArray(3); - _wglVertexAttribPointer(3, 2, GL_UNSIGNED_BYTE, true, 24, 16); - _wglVertexAttribDivisor(3, 1); - - _wglEnableVertexAttribArray(4); - _wglVertexAttribPointer(4, 2, GL_UNSIGNED_BYTE, false, 24, 18); - _wglVertexAttribDivisor(4, 1); - - _wglEnableVertexAttribArray(5); - _wglVertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, true, 24, 20); - _wglVertexAttribDivisor(5, 1); - - } - - public static void begin() { - particleBuffer.clear(); - particleCount = 0; - particlesHasOverflowed = false; - } - - public static void appendParticle(float posX, float posY, float posZ, int particleTextureX, int particleTextureY, - int lightMapX, int lightMapY, int particleSize, int particleTexSize, float r, float g, float b, float a) { - int color = ((int) (a * 255.0f) << 24) | ((int) (r * 255.0f) << 16) | ((int) (g * 255.0f) << 8) - | (int) (b * 255.0f); - appendParticle(posX, posY, posZ, particleTextureX, particleTextureY, lightMapX, lightMapY, particleSize, - particleTexSize, color); - } - - public static void appendParticle(float posX, float posY, float posZ, int particleTextureX, int particleTextureY, - int lightMapX, int lightMapY, int particleSize, int particleTexSize, int rgba) { - if (particlesHasOverflowed) { - return; - } - if (particleCount >= PARTICLE_LIMIT) { - particlesHasOverflowed = true; - logger.error("Particle buffer has overflowed! Exceeded {} particles, no more particles will be rendered.", - PARTICLE_LIMIT); - return; - } - ++particleCount; - ByteBuffer buf = particleBuffer; - buf.putFloat(posX); - buf.putFloat(posY); - buf.putFloat(posZ); - buf.putShort((short) particleTextureX); - buf.putShort((short) particleTextureY); - buf.put((byte) lightMapX); - buf.put((byte) lightMapY); - buf.put((byte) particleSize); - buf.put((byte) particleTexSize); - buf.putInt(rgba); - } - - public static void render(float texCoordWidth, float texCoordHeight, float particleCoordSize, float transformParam1, - float transformParam2, float transformParam3, float transformParam4, float transformParam5) { - if (particleCount == 0) { - return; - } - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - if (texCoordWidth != stateTexCoordWidth || texCoordHeight != stateTexCoordHeight - || particleCoordSize != stateParticleCoordSize) { - _wglUniform3f(u_texCoordSize2f_particleSize1f, texCoordWidth, texCoordHeight, particleCoordSize); - stateTexCoordWidth = texCoordWidth; - stateTexCoordHeight = texCoordHeight; - stateParticleCoordSize = particleCoordSize; - } - - if (transformParam1 != stateTransformParam1 || transformParam2 != stateTransformParam2 - || transformParam5 != stateTransformParam5) { - _wglUniform3f(u_transformParam_1_2_5_f, transformParam1, transformParam2, transformParam5); - stateTransformParam1 = transformParam1; - stateTransformParam2 = transformParam2; - stateTransformParam5 = transformParam5; - } - - if (transformParam3 != stateTransformParam3 || transformParam4 != stateTransformParam4) { - _wglUniform2f(u_transformParam_3_4_f, transformParam3, transformParam4); - stateTransformParam3 = transformParam3; - stateTransformParam4 = transformParam4; - } - - int serial = GlStateManager.stateColorSerial; - if (stateColorSerial != serial) { - stateColorSerial = serial; - float r = GlStateManager.stateColorR; - float g = GlStateManager.stateColorG; - float b = GlStateManager.stateColorB; - float a = GlStateManager.stateColorA; - if (stateColorR != r || stateColorG != g || - stateColorB != b || stateColorA != a) { - _wglUniform4f(u_color4f, r, g, b, a); - stateColorR = r; - stateColorG = g; - stateColorB = b; - stateColorA = a; - } - } - - int ptr1 = GlStateManager.modelMatrixStackPointer; - int serial1 = GlStateManager.modelMatrixStackAccessSerial[ptr1]; - int ptr2 = GlStateManager.projectionMatrixStackPointer; - int serial2 = GlStateManager.projectionMatrixStackAccessSerial[ptr2]; - if (stateModelMatrixSerial != serial1 || stateProjectionMatrixSerial != serial2) { - stateModelMatrixSerial = serial1; - stateProjectionMatrixSerial = serial2; - Matrix4f.mul(GlStateManager.projectionMatrixStack[ptr2], GlStateManager.modelMatrixStack[ptr1], tmpMatrix); - matrixCopyBuffer.clear(); - tmpMatrix.store(matrixCopyBuffer); - matrixCopyBuffer.flip(); - _wglUniformMatrix4fv(u_matrixTransform, false, matrixCopyBuffer); - } - - EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - EaglercraftGPU.bindGLBufferArray(vertexArray); - - int p = particleBuffer.position(); - int l = particleBuffer.limit(); - - particleBuffer.flip(); - _wglBufferSubData(GL_ARRAY_BUFFER, 0, particleBuffer); - - particleBuffer.position(p); - particleBuffer.limit(l); - - _wglDrawArraysInstanced(GL_TRIANGLES, 0, 6, particleCount); - } - - public static void stupidColorSetHack(IUniformGL color4f) { - _wglUniform4f(color4f, GlStateManager.stateColorR, GlStateManager.stateColorG, GlStateManager.stateColorB, - GlStateManager.stateColorA); - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class InstancedParticleRenderer { + + private static final Logger logger = LogManager.getLogger("InstancedParticleRenderer"); + + public static final String vertexShaderPath = "/assets/eagler/glsl/accel_particle.vsh"; + public static final String vertexShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision mediump sampler2D;\n"; + + public static final String fragmentShaderPath = "/assets/eagler/glsl/accel_particle.fsh"; + public static final String fragmentShaderPrecision = "precision lowp int;\nprecision mediump float;\nprecision mediump sampler2D;\n"; + + private static ByteBuffer particleBuffer = null; + private static int particleCount = 0; + private static boolean particlesHasOverflowed = false; + + private static final int BYTES_PER_PARTICLE = 24; + private static final int PARTICLE_LIMIT = 5461; + + private static IProgramGL shaderProgram = null; + private static IUniformGL u_matrixTransform = null; + private static FloatBuffer matrixCopyBuffer = null; + private static IUniformGL u_texCoordSize2f_particleSize1f = null; + private static IUniformGL u_transformParam_1_2_5_f = null; + private static IUniformGL u_transformParam_3_4_f = null; + private static IUniformGL u_color4f = null; + + private static IBufferArrayGL vertexArray = null; + private static IBufferGL vertexBuffer = null; + + private static IBufferGL instancesBuffer = null; + + private static float stateColorR = -999.0f; + private static float stateColorG = -999.0f; + private static float stateColorB = -999.0f; + private static float stateColorA = -999.0f; + private static int stateColorSerial = -1; + + private static final Matrix4f tmpMatrix = new Matrix4f(); + private static int stateModelMatrixSerial = -1; + private static int stateProjectionMatrixSerial = -1; + + private static float stateTexCoordWidth = -999.0f; + private static float stateTexCoordHeight = -999.0f; + private static float stateParticleCoordSize = -999.0f; + + private static float stateTransformParam1 = -999.0f; + private static float stateTransformParam2 = -999.0f; + private static float stateTransformParam3 = -999.0f; + private static float stateTransformParam4 = -999.0f; + private static float stateTransformParam5 = -999.0f; + + static void initialize() { + String vertexSource = EagRuntime.getRequiredResourceString(vertexShaderPath); + String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath); + + IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER); + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + + _wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vertexSource, vertexShaderPrecision)); + _wglCompileShader(vert); + + if (_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { + logger.error( + "Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for InstancedParticleRenderer!"); + String log = _wglGetShaderInfoLog(vert); + if (log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for (int i = 0; i < lines.length; ++i) { + logger.error("[VERT] {}", lines[i]); + } + } + throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); + } + + _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision)); + _wglCompileShader(frag); + + if (_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + + "\" for InstancedParticleRenderer!"); + String log = _wglGetShaderInfoLog(frag); + if (log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for (int i = 0; i < lines.length; ++i) { + logger.error("[FRAG] {}", lines[i]); + } + } + throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); + } + + shaderProgram = _wglCreateProgram(); + + _wglAttachShader(shaderProgram, vert); + _wglAttachShader(shaderProgram, frag); + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + VSHInputLayoutParser.applyLayout(shaderProgram, VSHInputLayoutParser.getShaderInputs(vertexSource)); + } + + _wglLinkProgram(shaderProgram); + + _wglDetachShader(shaderProgram, vert); + _wglDetachShader(shaderProgram, frag); + + _wglDeleteShader(vert); + _wglDeleteShader(frag); + + if (_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + logger.error("Failed to link shader program for InstancedParticleRenderer!"); + String log = _wglGetProgramInfoLog(shaderProgram); + if (log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for (int i = 0; i < lines.length; ++i) { + logger.error("[LINK] {}", lines[i]); + } + } + throw new IllegalStateException("Shader program for InstancedParticleRenderer could not be linked!"); + } + + matrixCopyBuffer = EagRuntime.allocateFloatBuffer(16); + particleBuffer = EagRuntime.allocateByteBuffer(PARTICLE_LIMIT * BYTES_PER_PARTICLE); + + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform"); + u_texCoordSize2f_particleSize1f = _wglGetUniformLocation(shaderProgram, "u_texCoordSize2f_particleSize1f"); + u_transformParam_1_2_5_f = _wglGetUniformLocation(shaderProgram, "u_transformParam_1_2_5_f"); + u_transformParam_3_4_f = _wglGetUniformLocation(shaderProgram, "u_transformParam_3_4_f"); + u_color4f = _wglGetUniformLocation(shaderProgram, "u_color4f"); + + _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0); + _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_lightmapTexture"), 1); + + vertexArray = EaglercraftGPU.createGLBufferArray(); + vertexBuffer = _wglGenBuffers(); + instancesBuffer = _wglGenBuffers(); + + FloatBuffer verts = EagRuntime.allocateFloatBuffer(12); + verts.put(new float[] { + -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f + }); + verts.flip(); + + EaglercraftGPU.bindGLBufferArray(vertexArray); + + EaglercraftGPU.bindVAOGLArrayBufferNow(vertexBuffer); + _wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW); + + EagRuntime.freeFloatBuffer(verts); + + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + EaglercraftGPU.vertexAttribDivisor(0, 0); + + EaglercraftGPU.bindVAOGLArrayBufferNow(instancesBuffer); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW); + + EaglercraftGPU.enableVertexAttribArray(1); + EaglercraftGPU.vertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0); + EaglercraftGPU.vertexAttribDivisor(1, 1); + + EaglercraftGPU.enableVertexAttribArray(2); + EaglercraftGPU.vertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, false, 24, 12); + EaglercraftGPU.vertexAttribDivisor(2, 1); + + EaglercraftGPU.enableVertexAttribArray(3); + EaglercraftGPU.vertexAttribPointer(3, 2, GL_UNSIGNED_BYTE, true, 24, 16); + EaglercraftGPU.vertexAttribDivisor(3, 1); + + EaglercraftGPU.enableVertexAttribArray(4); + EaglercraftGPU.vertexAttribPointer(4, 2, GL_UNSIGNED_BYTE, false, 24, 18); + EaglercraftGPU.vertexAttribDivisor(4, 1); + + EaglercraftGPU.enableVertexAttribArray(5); + EaglercraftGPU.vertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, true, 24, 20); + EaglercraftGPU.vertexAttribDivisor(5, 1); + + } + + public static void begin() { + particleBuffer.clear(); + particleCount = 0; + particlesHasOverflowed = false; + } + + public static void appendParticle(float posX, float posY, float posZ, int particleTextureX, int particleTextureY, + int lightMapX, int lightMapY, int particleSize, int particleTexSize, float r, float g, float b, float a) { + int color = ((int) (a * 255.0f) << 24) | ((int) (r * 255.0f) << 16) | ((int) (g * 255.0f) << 8) + | (int) (b * 255.0f); + appendParticle(posX, posY, posZ, particleTextureX, particleTextureY, lightMapX, lightMapY, particleSize, + particleTexSize, color); + } + + public static void appendParticle(float posX, float posY, float posZ, int particleTextureX, int particleTextureY, + int lightMapX, int lightMapY, int particleSize, int particleTexSize, int rgba) { + if (particlesHasOverflowed) { + return; + } + if (particleCount >= PARTICLE_LIMIT) { + particlesHasOverflowed = true; + logger.error("Particle buffer has overflowed! Exceeded {} particles, no more particles will be rendered.", + PARTICLE_LIMIT); + return; + } + ++particleCount; + ByteBuffer buf = particleBuffer; + buf.putFloat(posX); + buf.putFloat(posY); + buf.putFloat(posZ); + buf.putShort((short) particleTextureX); + buf.putShort((short) particleTextureY); + buf.put((byte) lightMapX); + buf.put((byte) lightMapY); + buf.put((byte) particleSize); + buf.put((byte) particleTexSize); + buf.putInt(rgba); + } + + public static void render(float texCoordWidth, float texCoordHeight, float particleCoordSize, float transformParam1, + float transformParam2, float transformParam3, float transformParam4, float transformParam5) { + if (particleCount == 0) { + return; + } + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + if (texCoordWidth != stateTexCoordWidth || texCoordHeight != stateTexCoordHeight + || particleCoordSize != stateParticleCoordSize) { + _wglUniform3f(u_texCoordSize2f_particleSize1f, texCoordWidth, texCoordHeight, particleCoordSize); + stateTexCoordWidth = texCoordWidth; + stateTexCoordHeight = texCoordHeight; + stateParticleCoordSize = particleCoordSize; + } + + if (transformParam1 != stateTransformParam1 || transformParam2 != stateTransformParam2 + || transformParam5 != stateTransformParam5) { + _wglUniform3f(u_transformParam_1_2_5_f, transformParam1, transformParam2, transformParam5); + stateTransformParam1 = transformParam1; + stateTransformParam2 = transformParam2; + stateTransformParam5 = transformParam5; + } + + if (transformParam3 != stateTransformParam3 || transformParam4 != stateTransformParam4) { + _wglUniform2f(u_transformParam_3_4_f, transformParam3, transformParam4); + stateTransformParam3 = transformParam3; + stateTransformParam4 = transformParam4; + } + + int serial = GlStateManager.stateColorSerial; + if (stateColorSerial != serial) { + stateColorSerial = serial; + float r = GlStateManager.stateColorR; + float g = GlStateManager.stateColorG; + float b = GlStateManager.stateColorB; + float a = GlStateManager.stateColorA; + if (stateColorR != r || stateColorG != g || + stateColorB != b || stateColorA != a) { + _wglUniform4f(u_color4f, r, g, b, a); + stateColorR = r; + stateColorG = g; + stateColorB = b; + stateColorA = a; + } + } + + int ptr1 = GlStateManager.modelMatrixStackPointer; + int serial1 = GlStateManager.modelMatrixStackAccessSerial[ptr1]; + int ptr2 = GlStateManager.projectionMatrixStackPointer; + int serial2 = GlStateManager.projectionMatrixStackAccessSerial[ptr2]; + if (stateModelMatrixSerial != serial1 || stateProjectionMatrixSerial != serial2) { + stateModelMatrixSerial = serial1; + stateProjectionMatrixSerial = serial2; + Matrix4f.mul(GlStateManager.projectionMatrixStack[ptr2], GlStateManager.modelMatrixStack[ptr1], tmpMatrix); + matrixCopyBuffer.clear(); + tmpMatrix.store(matrixCopyBuffer); + matrixCopyBuffer.flip(); + _wglUniformMatrix4fv(u_matrixTransform, false, matrixCopyBuffer); + } + + EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); + EaglercraftGPU.bindGLBufferArray(vertexArray); + + int p = particleBuffer.position(); + int l = particleBuffer.limit(); + + particleBuffer.flip(); + _wglBufferSubData(GL_ARRAY_BUFFER, 0, particleBuffer); + + particleBuffer.position(p); + particleBuffer.limit(l); + + EaglercraftGPU.doDrawArraysInstanced(GL_TRIANGLES, 0, 6, particleCount); + } + + public static void stupidColorSetHack(IUniformGL color4f) { + _wglUniform4f(color4f, GlStateManager.stateColorR, GlStateManager.stateColorG, GlStateManager.stateColorB, GlStateManager.stateColorA); + } + + public static void destroy() { + if(particleBuffer != null) { + EagRuntime.freeByteBuffer(particleBuffer); + particleBuffer = null; + } + if(shaderProgram != null) { + _wglDeleteProgram(shaderProgram); + shaderProgram = null; + } + if(matrixCopyBuffer != null) { + EagRuntime.freeFloatBuffer(matrixCopyBuffer); + matrixCopyBuffer = null; + } + u_matrixTransform = null; + u_texCoordSize2f_particleSize1f = null; + u_transformParam_1_2_5_f = null; + u_transformParam_3_4_f = null; + u_color4f = null; + if(vertexArray != null) { + EaglercraftGPU.destroyGLBufferArray(vertexArray); + vertexArray = null; + } + if(vertexBuffer != null) { + _wglDeleteBuffers(vertexBuffer); + vertexBuffer = null; + } + if(instancesBuffer != null) { + _wglDeleteBuffers(instancesBuffer); + instancesBuffer = null; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/RealOpenGLEnums.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/RealOpenGLEnums.java index 6a04dad..1a45d5b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/RealOpenGLEnums.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/RealOpenGLEnums.java @@ -1,8 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.opengl; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLBufferArray.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLBufferArray.java new file mode 100755 index 0000000..4006214 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLBufferArray.java @@ -0,0 +1,225 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class SoftGLBufferArray implements IBufferArrayGL { + + Attrib[] attribs = new Attrib[4]; + int[] attribDivisors = null; + int hasAttribDivisorMask = 0; + int enabled = 0; + int enabledCnt = -1; + IBufferGL indexBuffer = null; + + SoftGLBufferArray() { + } + + void setAttrib(IBufferGL buffer, int index, int size, int format, boolean normalized, int stride, int offset) { + if(index >= attribs.length) { + int newLen = attribs.length << 1; + while(newLen <= index) { + newLen <<= 1; + } + Attrib[] newAttrib = new Attrib[newLen]; + System.arraycopy(attribs, 0, newAttrib, 0, attribs.length); + attribs = newAttrib; + } + attribs[index] = new Attrib(buffer, size, format, normalized, stride, offset); + } + + void setAttribDivisor(int index, int divisor) { + if(attribDivisors == null) { + if(divisor != 0) { + int newLen = 8; + while(newLen <= index) { + newLen <<= 1; + } + attribDivisors = new int[newLen]; + } + }else if(index >= attribDivisors.length) { + int newLen = attribDivisors.length << 1; + while(newLen <= index) { + newLen <<= 1; + } + int[] newDivisor = new int[newLen]; + System.arraycopy(attribDivisors, 0, newDivisor, 0, attribDivisors.length); + attribDivisors = newDivisor; + } + if(attribDivisors != null) { + attribDivisors[index] = divisor; + if(divisor != 0) { + hasAttribDivisorMask |= (1 << index); + }else { + hasAttribDivisorMask &= ~(1 << index); + } + } + } + + void enableAttrib(int index, boolean en) { + if(en) { + enabled |= (1 << index); + }else { + enabled &= ~(1 << index); + } + enabledCnt = 32 - Integer.numberOfLeadingZeros(enabled); + } + + void setIndexBuffer(IBufferGL buffer) { + indexBuffer = buffer; + } + + void transitionToState(SoftGLBufferState previousState, boolean elements) { + int oldEnabled = previousState.oldEnabled; + int oldEnabledCnt = previousState.oldEnabledCnt; + int[] oldAttribDivisors = previousState.attribDivisors; + int oldHasAttribDivisorMask = previousState.hasAttribDivisorMask; + Attrib[] oldAttrs = previousState.attribs; + boolean instancingCapable = EaglercraftGPU.checkInstancingCapable(); + int enCnt = enabledCnt; + int en = enabled; + Attrib[] attrs = attribs; + int[] divs = attribDivisors; + int hasDivs = hasAttribDivisorMask; + if(oldEnabledCnt >= 0) { + int enMax = Math.max(enCnt, oldEnabledCnt); + for(int i = 0, ii; i < enMax; ++i) { + ii = (1 << i); + boolean old = i < oldEnabledCnt && (oldEnabled & ii) != 0; + boolean _new = i < enCnt && (en & ii) != 0; + if(_new) { + if(!old) { + _wglEnableVertexAttribArray(i); + } + Attrib attr = i < attrs.length ? attrs[i] : null; + if(attr != null) { + Attrib oldAttr = oldAttrs[i]; + if(oldAttr == null || !oldAttr.equalsExplicit(attr)) { + EaglercraftGPU.bindGLArrayBuffer(attr.buffer); + _wglVertexAttribPointer(i, attr.size, attr.format, attr.normalized, attr.stride, attr.offset); + oldAttrs[i] = attr; + } + }else { + oldAttrs[i] = null; + } + if(instancingCapable) { + // instancing is uncommon + if((hasDivs & ii) != 0) { + int newDivisor = divs[i]; + if((oldHasAttribDivisorMask & ii) == 0 || newDivisor != oldAttribDivisors[i]) { + _wglVertexAttribDivisor(i, newDivisor); + oldAttribDivisors[i] = newDivisor; + previousState.hasAttribDivisorMask |= ii; + } + }else { + if((oldHasAttribDivisorMask & ii) != 0) { + _wglVertexAttribDivisor(i, 0); + oldAttribDivisors[i] = 0; + previousState.hasAttribDivisorMask &= ~ii; + } + } + } + }else if(old) { + _wglDisableVertexAttribArray(i); + } + } + }else { + // Bootstrap code for the emulator's first draw + for(int i = 0; i < enCnt; ++i) { + int ii = (1 << i); + if((en & ii) != 0) { + _wglEnableVertexAttribArray(i); + Attrib attr = attrs[i]; + if(attr != null) { + EaglercraftGPU.bindGLArrayBuffer(attr.buffer); + _wglVertexAttribPointer(i, attr.size, attr.format, attr.normalized, attr.stride, attr.offset); + oldAttrs[i] = attr; + }else { + oldAttrs[i] = null; + } + if(instancingCapable) { + if((hasDivs & ii) != 0) { + int newDivisor = divs[i]; + _wglVertexAttribDivisor(i, newDivisor); + oldAttribDivisors[i] = newDivisor; + previousState.hasAttribDivisorMask |= ii; + }else { + _wglVertexAttribDivisor(i, 0); + oldAttribDivisors[i] = 0; + } + } + } + } + } + if(elements) { + IBufferGL indexBufferL = indexBuffer; + if(indexBufferL != null) { + EaglercraftGPU.bindEmulatedVAOIndexBuffer(indexBufferL); + } + } + previousState.oldEnabled = en & ((1 << enCnt) - 1); + previousState.oldEnabledCnt = enCnt; + } + + @Override + public void free() { + } + + static class Attrib { + + final IBufferGL buffer; + final int size; + final int format; + final boolean normalized; + final int stride; + final int offset; + final int hash; + final int checkVal; + + Attrib(IBufferGL buffer, int size, int format, boolean normalized, int stride, int offset) { + this.buffer = buffer; + this.size = size; + this.format = format; + this.normalized = normalized; + this.stride = stride; + this.offset = offset; + this.checkVal = ((size - 1) & 3) | (normalized ? 4 : 0) | (format << 4); + this.hash = (((((31 + buffer.hashCode()) * 31 + size) * 31 + format) * 31 + (normalized ? 1 : 0)) * 31 + + stride) * 31 + offset; + } + + public int hashCode() { + return hash; + } + + public boolean equals(Object obj) { + if(obj == this) return true; + if(!(obj instanceof Attrib)) return false; + Attrib o2 = (Attrib)obj; + return o2.hash == hash && o2.buffer == buffer && o2.checkVal == checkVal && o2.stride == stride && o2.offset == offset; + } + + public boolean equalsExplicit(Attrib o2) { + return o2 == this || (o2.hash == hash && o2.buffer == buffer && o2.checkVal == checkVal && o2.stride == stride && o2.offset == offset); + } + + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLBufferState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLBufferState.java new file mode 100755 index 0000000..615a411 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLBufferState.java @@ -0,0 +1,31 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.opengl.SoftGLBufferArray.Attrib; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class SoftGLBufferState { + + final Attrib[] attribs = new Attrib[24]; + int[] attribDivisors = new int[24]; + int hasAttribDivisorMask = 0; + int oldEnabled = 0; + int oldEnabledCnt = -1; + + SoftGLBufferState() { + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java index 439b314..814543f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java @@ -1,181 +1,203 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants; -import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class SpriteLevelMixer { - - private static final Logger LOGGER = LogManager.getLogger("SpriteLevelMixer"); - - public static final String fragmentShaderPath = "/assets/eagler/glsl/texture_mix.fsh"; - - private static IProgramGL shaderProgram = null; - - private static IUniformGL u_textureLod1f = null; - private static IUniformGL u_blendFactor4f = null; - private static IUniformGL u_blendBias4f = null; - private static IUniformGL u_matrixTransform = null; - - private static FloatBuffer matrixCopyBuffer = null; - - private static boolean blendColorChanged = true; - private static float blendColorR = 1.0f; - private static float blendColorG = 1.0f; - private static float blendColorB = 1.0f; - private static float blendColorA = 1.0f; - - private static boolean biasColorChanged = true; - private static float biasColorR = 0.0f; - private static float biasColorG = 0.0f; - private static float biasColorB = 0.0f; - private static float biasColorA = 0.0f; - - private static boolean matrixChanged = true; - private static final Matrix3f transformMatrix = new Matrix3f(); - - private static final Matrix3f identityMatrix = new Matrix3f(); - - static void initialize() { - - String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath); - if(fragmentSource == null) { - throw new RuntimeException("SpriteLevelMixer shader \"" + fragmentShaderPath + "\" is missing!"); - } - - IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); - - _wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource); - _wglCompileShader(frag); - - if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { - LOGGER.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for SpriteLevelMixer!"); - String log = _wglGetShaderInfoLog(frag); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - LOGGER.error("[FRAG] {}", lines[i]); - } - } - throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); - } - - shaderProgram = _wglCreateProgram(); - - _wglAttachShader(shaderProgram, DrawUtils.vshLocal); - _wglAttachShader(shaderProgram, frag); - - _wglLinkProgram(shaderProgram); - - _wglDetachShader(shaderProgram, DrawUtils.vshLocal); - _wglDetachShader(shaderProgram, frag); - - _wglDeleteShader(frag); - - if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { - LOGGER.error("Failed to link shader program for SpriteLevelMixer!"); - String log = _wglGetProgramInfoLog(shaderProgram); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - LOGGER.error("[LINK] {}", lines[i]); - } - } - throw new IllegalStateException("Shader program for SpriteLevelMixer could not be linked!"); - } - - matrixCopyBuffer = EagRuntime.allocateFloatBuffer(9); - - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - u_textureLod1f = _wglGetUniformLocation(shaderProgram, "u_textureLod1f"); - u_blendFactor4f = _wglGetUniformLocation(shaderProgram, "u_blendFactor4f"); - u_blendBias4f = _wglGetUniformLocation(shaderProgram, "u_blendBias4f"); - u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform"); - - _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0); - - } - - public static void setBlendColor(float r, float g, float b, float a) { - if(r != blendColorR || g != blendColorG || b != blendColorB || a != blendColorA) { - blendColorChanged = true; - blendColorR = r; - blendColorG = g; - blendColorB = b; - blendColorA = a; - } - } - - public static void setBiasColor(float r, float g, float b, float a) { - if(r != biasColorR || g != biasColorG || b != biasColorB || a != biasColorA) { - biasColorChanged = true; - biasColorR = r; - biasColorG = g; - biasColorB = b; - biasColorA = a; - } - } - - public static void setIdentityMatrix() { - setMatrix3f(identityMatrix); - } - - public static void setMatrix3f(Matrix3f matrix) { - if(!matrix.equals(transformMatrix)) { - matrixChanged = true; - transformMatrix.load(matrix); - } - } - - public static void drawSprite(float level) { - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - - _wglUniform1f(u_textureLod1f, level); - - if(blendColorChanged) { - _wglUniform4f(u_blendFactor4f, blendColorR, blendColorG, blendColorB, blendColorA); - blendColorChanged = false; - } - - if(biasColorChanged) { - _wglUniform4f(u_blendBias4f, biasColorR, biasColorG, biasColorB, biasColorA); - biasColorChanged = false; - } - - if(matrixChanged) { - matrixCopyBuffer.clear(); - transformMatrix.store(matrixCopyBuffer); - matrixCopyBuffer.flip(); - _wglUniformMatrix3fv(u_matrixTransform, false, matrixCopyBuffer); - matrixChanged = false; - } - - DrawUtils.drawStandardQuad2D(); - } - -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f; + +/** + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SpriteLevelMixer { + + private static final Logger LOGGER = LogManager.getLogger("SpriteLevelMixer"); + + public static final String fragmentShaderPath = "/assets/eagler/glsl/texture_mix.fsh"; + public static final String fragmentShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision highp sampler2D;\n"; + + private static IProgramGL shaderProgram = null; + + private static IUniformGL u_textureLod1f = null; + private static IUniformGL u_blendFactor4f = null; + private static IUniformGL u_blendBias4f = null; + private static IUniformGL u_matrixTransform = null; + + private static FloatBuffer matrixCopyBuffer = null; + + private static boolean blendColorChanged = true; + private static float blendColorR = 1.0f; + private static float blendColorG = 1.0f; + private static float blendColorB = 1.0f; + private static float blendColorA = 1.0f; + + private static boolean biasColorChanged = true; + private static float biasColorR = 0.0f; + private static float biasColorG = 0.0f; + private static float biasColorB = 0.0f; + private static float biasColorA = 0.0f; + + private static boolean matrixChanged = true; + private static final Matrix3f transformMatrix = new Matrix3f(); + + private static final Matrix3f identityMatrix = new Matrix3f(); + + static void initialize() { + String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath); + + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + + _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision)); + _wglCompileShader(frag); + + if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + LOGGER.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for SpriteLevelMixer!"); + String log = _wglGetShaderInfoLog(frag); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + LOGGER.error("[FRAG] {}", lines[i]); + } + } + throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); + } + + shaderProgram = _wglCreateProgram(); + + _wglAttachShader(shaderProgram, DrawUtils.vshLocal); + _wglAttachShader(shaderProgram, frag); + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + VSHInputLayoutParser.applyLayout(shaderProgram, DrawUtils.vshLocalLayout); + } + + _wglLinkProgram(shaderProgram); + + _wglDetachShader(shaderProgram, DrawUtils.vshLocal); + _wglDetachShader(shaderProgram, frag); + + _wglDeleteShader(frag); + + if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + LOGGER.error("Failed to link shader program for SpriteLevelMixer!"); + String log = _wglGetProgramInfoLog(shaderProgram); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + LOGGER.error("[LINK] {}", lines[i]); + } + } + throw new IllegalStateException("Shader program for SpriteLevelMixer could not be linked!"); + } + + matrixCopyBuffer = EagRuntime.allocateFloatBuffer(9); + + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + u_textureLod1f = _wglGetUniformLocation(shaderProgram, "u_textureLod1f"); + u_blendFactor4f = _wglGetUniformLocation(shaderProgram, "u_blendFactor4f"); + u_blendBias4f = _wglGetUniformLocation(shaderProgram, "u_blendBias4f"); + u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform"); + + _wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0); + + } + + public static void setBlendColor(float r, float g, float b, float a) { + if(r != blendColorR || g != blendColorG || b != blendColorB || a != blendColorA) { + blendColorChanged = true; + blendColorR = r; + blendColorG = g; + blendColorB = b; + blendColorA = a; + } + } + + public static void setBiasColor(float r, float g, float b, float a) { + if(r != biasColorR || g != biasColorG || b != biasColorB || a != biasColorA) { + biasColorChanged = true; + biasColorR = r; + biasColorG = g; + biasColorB = b; + biasColorA = a; + } + } + + public static void setIdentityMatrix() { + setMatrix3f(identityMatrix); + } + + public static void setMatrix3f(Matrix3f matrix) { + if(!matrix.equals(transformMatrix)) { + matrixChanged = true; + transformMatrix.load(matrix); + } + } + + public static void drawSprite(float level) { + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(u_textureLod1f, level); + }else { + if(level != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", level); + } + _wglUniform1f(u_textureLod1f, 0.0f); + } + + if(blendColorChanged) { + _wglUniform4f(u_blendFactor4f, blendColorR, blendColorG, blendColorB, blendColorA); + blendColorChanged = false; + } + + if(biasColorChanged) { + _wglUniform4f(u_blendBias4f, biasColorR, biasColorG, biasColorB, biasColorA); + biasColorChanged = false; + } + + if(matrixChanged) { + matrixCopyBuffer.clear(); + transformMatrix.store(matrixCopyBuffer); + matrixCopyBuffer.flip(); + _wglUniformMatrix3fv(u_matrixTransform, false, matrixCopyBuffer); + matrixChanged = false; + } + + DrawUtils.drawStandardQuad2D(); + } + + public static void destroy() { + if(matrixCopyBuffer != null) { + EagRuntime.freeFloatBuffer(matrixCopyBuffer); + matrixCopyBuffer = null; + } + if(shaderProgram != null) { + _wglDeleteProgram(shaderProgram); + shaderProgram = null; + } + u_textureLod1f = null; + u_blendFactor4f = null; + u_blendBias4f = null; + u_matrixTransform = null; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java index d6a4461..252830e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java @@ -74,7 +74,7 @@ public class StreamBuffer { next.vertexBuffer = _wglGenBuffers(); } if(next.vertexArray == null) { - next.vertexArray = _wglGenVertexArrays(); + next.vertexArray = EaglercraftGPU.createGLBufferArray(); initializer.initialize(next.vertexArray, next.vertexBuffer); } if(next.vertexBufferSize < requiredMemory) { @@ -100,7 +100,7 @@ public class StreamBuffer { newArray[i] = buffers[i]; }else { if(buffers[i].vertexArray != null) { - _wglDeleteVertexArrays(buffers[i].vertexArray); + EaglercraftGPU.destroyGLBufferArray(buffers[i].vertexArray); } if(buffers[i].vertexBuffer != null) { _wglDeleteBuffers(buffers[i].vertexBuffer); @@ -135,7 +135,7 @@ public class StreamBuffer { for(int i = 0; i < buffers.length; ++i) { StreamBufferInstance next = buffers[i]; if(next.vertexArray != null) { - _wglDeleteVertexArrays(next.vertexArray); + EaglercraftGPU.destroyGLBufferArray(next.vertexArray); } if(next.vertexBuffer != null) { _wglDeleteBuffers(next.vertexBuffer); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java index 32fb3ce..4819cc9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java @@ -1,354 +1,435 @@ -package net.lax1dude.eaglercraft.v1_8.opengl; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants; - -/** - * Copyright (c) 2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class TextureCopyUtil { - - private static final Logger LOGGER = LogManager.getLogger("TextureCopyUtil"); - - public static final String vertexShaderPath = "/assets/eagler/glsl/texture_blit.vsh"; - public static final String fragmentShaderPath = "/assets/eagler/glsl/texture_blit.fsh"; - - private static String vshSource = null; - private static String fshSource = null; - - private static IShaderGL vshShader = null; - - private static class TextureCopyShader { - private IProgramGL shaderProgram = null; - private IUniformGL u_srcCoords4f = null; - private IUniformGL u_dstCoords4f = null; - private IUniformGL u_textureLod1f = null; - private IUniformGL u_pixelAlignmentSizes4f = null; - private IUniformGL u_pixelAlignmentOffset2f = null; - private TextureCopyShader(IProgramGL shaderProgram) { - this.shaderProgram = shaderProgram; - EaglercraftGPU.bindGLShaderProgram(shaderProgram); - this.u_srcCoords4f = _wglGetUniformLocation(shaderProgram, "u_srcCoords4f"); - this.u_dstCoords4f = _wglGetUniformLocation(shaderProgram, "u_dstCoords4f"); - this.u_textureLod1f = _wglGetUniformLocation(shaderProgram, "u_textureLod1f"); - this.u_pixelAlignmentSizes4f = _wglGetUniformLocation(shaderProgram, "u_pixelAlignmentSizes4f"); - this.u_pixelAlignmentOffset2f = _wglGetUniformLocation(shaderProgram, "u_pixelAlignmentOffset2f"); - } - } - - private static TextureCopyShader textureBlit = null; - private static TextureCopyShader textureBlitAligned = null; - private static TextureCopyShader textureBlitDepth = null; - private static TextureCopyShader textureBlitDepthAligned = null; - - private static float srcViewW = 100.0f; - private static float srcViewH = 100.0f; - - private static float dstViewW = 50.0f; - private static float dstViewH = 50.0f; - - private static boolean isAligned = false; - private static int alignW = 0; - private static int alignH = 0; - private static float alignOffsetX = 0.0f; - private static float alignOffsetY = 0.0f; - - static void initialize() { - vshSource = EagRuntime.getResourceString(vertexShaderPath); - if(vshSource == null) { - throw new RuntimeException("TextureCopyUtil shader \"" + vertexShaderPath + "\" is missing!"); - } - - fshSource = EagRuntime.getResourceString(fragmentShaderPath); - if(fshSource == null) { - throw new RuntimeException("TextureCopyUtil shader \"" + fragmentShaderPath + "\" is missing!"); - } - - vshShader = _wglCreateShader(GL_VERTEX_SHADER); - - _wglShaderSource(vshShader, FixedFunctionConstants.VERSION + "\n" + vshSource); - _wglCompileShader(vshShader); - - if(_wglGetShaderi(vshShader, GL_COMPILE_STATUS) != GL_TRUE) { - LOGGER.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for TextureCopyUtil!"); - String log = _wglGetShaderInfoLog(vshShader); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - LOGGER.error("[VERT] {}", lines[i]); - } - } - throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); - } - } - - private static TextureCopyShader compileShader(boolean align, boolean depth) { - IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); - - _wglShaderSource(frag, - FixedFunctionConstants.VERSION + "\n" + (align ? "#define COMPILE_PIXEL_ALIGNMENT\n" : "") - + (depth ? "#define COMPILE_BLIT_DEPTH\n" : "") + fshSource); - _wglCompileShader(frag); - - if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { - LOGGER.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for TextureCopyUtil!"); - String log = _wglGetShaderInfoLog(frag); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - LOGGER.error("[FRAG] {}", lines[i]); - } - } - throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); - } - - IProgramGL shaderProgram = _wglCreateProgram(); - - _wglAttachShader(shaderProgram, vshShader); - _wglAttachShader(shaderProgram, frag); - - _wglLinkProgram(shaderProgram); - - _wglDetachShader(shaderProgram, vshShader); - _wglDetachShader(shaderProgram, frag); - - _wglDeleteShader(frag); - - if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { - LOGGER.error("Failed to link shader program for TextureCopyUtil!"); - String log = _wglGetProgramInfoLog(shaderProgram); - if(log != null) { - String[] lines = log.split("(\\r\\n|\\r|\\n)"); - for(int i = 0; i < lines.length; ++i) { - LOGGER.error("[LINK] {}", lines[i]); - } - } - throw new IllegalStateException("Shader program for TextureCopyUtil could not be linked!"); - } - - return new TextureCopyShader(shaderProgram); - } - - private static TextureCopyShader getShaderObj(boolean align, boolean depth) { - if(align) { - if(depth) { - if(textureBlitDepthAligned == null) textureBlitDepthAligned = compileShader(true, true); - return textureBlitDepthAligned; - }else { - if(textureBlitAligned == null) textureBlitAligned = compileShader(true, false); - return textureBlitAligned; - } - }else { - if(depth) { - if(textureBlitDepth == null) textureBlitDepth = compileShader(false, true); - return textureBlitDepth; - }else { - if(textureBlit == null) textureBlit = compileShader(false, false); - return textureBlit; - } - } - } - - public static void srcSize(int w, int h) { - srcViewW = w; - srcViewH = h; - } - - public static void dstSize(int w, int h) { - dstViewW = w * 0.5f; - dstViewH = h * 0.5f; - } - - public static void srcDstSize(int w, int h) { - srcViewW = w; - srcViewH = h; - dstViewW = w * 0.5f; - dstViewH = h * 0.5f; - } - - /** - * this is reset after every blit - */ - public static void alignPixels(int dstW, int dstH, float alignX, float alignY) { - isAligned = true; - alignW = dstW; - alignH = dstH; - alignOffsetX = alignX; - alignOffsetY = alignY; - } - - /** - * this is reset after every blit - */ - public static void alignPixelsTopLeft(int srcW, int srcH, int dstW, int dstH) { - alignPixels(dstW, dstH, (0.5f * dstW) / srcW, (0.5f * dstH) / srcH); - } - - public static void disableAlign() { - isAligned = false; - } - - public static void blitTexture(int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTexture(0, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTexture(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTexture(lvl, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTexture(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - blitTexture(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); - } - - public static void blitTexture(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - TextureCopyShader shaderObj = getShaderObj(isAligned, false); - EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); - _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); - _wglUniform4f(shaderObj.u_dstCoords4f, (float) dstX / dstViewW - 1.0f, (float) dstY / dstViewH - 1.0f, - (float) dstW / dstViewW, (float) dstH / dstViewH); - _wglUniform1f(shaderObj.u_textureLod1f, lvl); - if(isAligned) { - _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); - _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); - isAligned = false; - } - DrawUtils.drawStandardQuad2D(); - } - - public static void blitTexture() { - blitTexture(0); - } - - public static void blitTexture(int lvl) { - TextureCopyShader shaderObj = getShaderObj(isAligned, false); - EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); - _wglUniform4f(shaderObj.u_srcCoords4f, 0.0f, 0.0f, 1.0f, 1.0f); - _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); - _wglUniform1f(shaderObj.u_textureLod1f, lvl); - if(isAligned) { - _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); - _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); - isAligned = false; - } - DrawUtils.drawStandardQuad2D(); - } - - public static void blitTextureUsingViewports(int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTextureUsingViewports(0, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTextureUsingViewports(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTextureUsingViewports(lvl, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTextureUsingViewports(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - blitTextureUsingViewports(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); - } - - public static void blitTextureUsingViewports(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - TextureCopyShader shaderObj = getShaderObj(isAligned, false); - EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); - GlStateManager.viewport(dstX, dstY, dstW, dstH); - _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); - _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); - _wglUniform1f(shaderObj.u_textureLod1f, lvl); - if(isAligned) { - _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); - _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); - isAligned = false; - } - DrawUtils.drawStandardQuad2D(); - } - - public static void blitTextureDepth(int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTextureDepth(0, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTextureDepth(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTextureDepth(lvl, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTextureDepth(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - blitTextureDepth(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); - } - - public static void blitTextureDepth(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - TextureCopyShader shaderObj = getShaderObj(isAligned, true); - EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); - _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); - _wglUniform4f(shaderObj.u_dstCoords4f, (float) dstX / dstViewW - 1.0f, (float) dstY / dstViewH - 1.0f, - (float) dstW / dstViewW, (float) dstH / dstViewH); - _wglUniform1f(shaderObj.u_textureLod1f, lvl); - if(isAligned) { - _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); - _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); - isAligned = false; - } - DrawUtils.drawStandardQuad2D(); - } - - public static void blitTextureDepth() { - blitTextureDepth(0); - } - - public static void blitTextureDepth(int lvl) { - TextureCopyShader shaderObj = getShaderObj(isAligned, true); - EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); - _wglUniform4f(shaderObj.u_srcCoords4f, 0.0f, 0.0f, 1.0f, 1.0f); - _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); - _wglUniform1f(shaderObj.u_textureLod1f, lvl); - if(isAligned) { - _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); - _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); - isAligned = false; - } - DrawUtils.drawStandardQuad2D(); - } - - public static void blitTextureDepthUsingViewports(int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTextureDepthUsingViewports(0, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTextureDepthUsingViewports(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { - blitTextureDepthUsingViewports(lvl, srcX, srcY, w, h, dstX, dstY, w, h); - } - - public static void blitTextureDepthUsingViewports(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - blitTextureDepthUsingViewports(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); - } - - public static void blitTextureDepthUsingViewports(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { - TextureCopyShader shaderObj = getShaderObj(isAligned, true); - EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); - GlStateManager.viewport(dstX, dstY, dstW, dstH); - _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); - _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); - _wglUniform1f(shaderObj.u_textureLod1f, lvl); - if(isAligned) { - _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); - _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); - isAligned = false; - } - DrawUtils.drawStandardQuad2D(); - } -} +package net.lax1dude.eaglercraft.v1_8.opengl; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TextureCopyUtil { + + private static final Logger LOGGER = LogManager.getLogger("TextureCopyUtil"); + + public static final String vertexShaderPath = "/assets/eagler/glsl/texture_blit.vsh"; + public static final String vertexShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision highp sampler2D;\n"; + + public static final String fragmentShaderPath = "/assets/eagler/glsl/texture_blit.fsh"; + public static final String fragmentShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision highp sampler2D;\n"; + + private static String vshSource = null; + private static List vshSourceLayout = null; + private static String fshSource = null; + + private static IShaderGL vshShader = null; + + private static class TextureCopyShader { + private IProgramGL shaderProgram = null; + private IUniformGL u_srcCoords4f = null; + private IUniformGL u_dstCoords4f = null; + private IUniformGL u_textureLod1f = null; + private IUniformGL u_pixelAlignmentSizes4f = null; + private IUniformGL u_pixelAlignmentOffset2f = null; + private TextureCopyShader(IProgramGL shaderProgram) { + this.shaderProgram = shaderProgram; + EaglercraftGPU.bindGLShaderProgram(shaderProgram); + this.u_srcCoords4f = _wglGetUniformLocation(shaderProgram, "u_srcCoords4f"); + this.u_dstCoords4f = _wglGetUniformLocation(shaderProgram, "u_dstCoords4f"); + this.u_textureLod1f = _wglGetUniformLocation(shaderProgram, "u_textureLod1f"); + this.u_pixelAlignmentSizes4f = _wglGetUniformLocation(shaderProgram, "u_pixelAlignmentSizes4f"); + this.u_pixelAlignmentOffset2f = _wglGetUniformLocation(shaderProgram, "u_pixelAlignmentOffset2f"); + } + private void destroy() { + if(shaderProgram != null) { + _wglDeleteProgram(shaderProgram); + shaderProgram = null; + } + u_srcCoords4f = null; + u_dstCoords4f = null; + u_textureLod1f = null; + u_pixelAlignmentSizes4f = null; + u_pixelAlignmentOffset2f = null; + } + } + + private static TextureCopyShader textureBlit = null; + private static TextureCopyShader textureBlitAligned = null; + private static TextureCopyShader textureBlitDepth = null; + private static TextureCopyShader textureBlitDepthAligned = null; + + private static float srcViewW = 100.0f; + private static float srcViewH = 100.0f; + + private static float dstViewW = 50.0f; + private static float dstViewH = 50.0f; + + private static boolean isAligned = false; + private static int alignW = 0; + private static int alignH = 0; + private static float alignOffsetX = 0.0f; + private static float alignOffsetY = 0.0f; + + static void initialize() { + vshSource = EagRuntime.getRequiredResourceString(vertexShaderPath); + fshSource = EagRuntime.getRequiredResourceString(fragmentShaderPath); + + vshShader = _wglCreateShader(GL_VERTEX_SHADER); + + _wglShaderSource(vshShader, GLSLHeader.getVertexHeaderCompat(vshSource, vertexShaderPrecision)); + _wglCompileShader(vshShader); + + if(_wglGetShaderi(vshShader, GL_COMPILE_STATUS) != GL_TRUE) { + LOGGER.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for TextureCopyUtil!"); + String log = _wglGetShaderInfoLog(vshShader); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + LOGGER.error("[VERT] {}", lines[i]); + } + } + throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!"); + } + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + vshSourceLayout = VSHInputLayoutParser.getShaderInputs(vshSource); + } + } + + private static TextureCopyShader compileShader(boolean align, boolean depth) { + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + + _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fshSource, fragmentShaderPrecision + + (align ? "#define COMPILE_PIXEL_ALIGNMENT\n" : "") + (depth ? "#define COMPILE_BLIT_DEPTH\n" : ""))); + _wglCompileShader(frag); + + if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + LOGGER.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for TextureCopyUtil!"); + String log = _wglGetShaderInfoLog(frag); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + LOGGER.error("[FRAG] {}", lines[i]); + } + } + throw new IllegalStateException("Fragment shader \"" + fragmentShaderPath + "\" could not be compiled!"); + } + + IProgramGL shaderProgram = _wglCreateProgram(); + + _wglAttachShader(shaderProgram, vshShader); + _wglAttachShader(shaderProgram, frag); + + if(EaglercraftGPU.checkOpenGLESVersion() == 200) { + VSHInputLayoutParser.applyLayout(shaderProgram, vshSourceLayout); + } + + _wglLinkProgram(shaderProgram); + + _wglDetachShader(shaderProgram, vshShader); + _wglDetachShader(shaderProgram, frag); + + _wglDeleteShader(frag); + + if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + LOGGER.error("Failed to link shader program for TextureCopyUtil!"); + String log = _wglGetProgramInfoLog(shaderProgram); + if(log != null) { + String[] lines = log.split("(\\r\\n|\\r|\\n)"); + for(int i = 0; i < lines.length; ++i) { + LOGGER.error("[LINK] {}", lines[i]); + } + } + throw new IllegalStateException("Shader program for TextureCopyUtil could not be linked!"); + } + + return new TextureCopyShader(shaderProgram); + } + + private static TextureCopyShader getShaderObj(boolean align, boolean depth) { + if(align) { + if(depth) { + if(textureBlitDepthAligned == null) textureBlitDepthAligned = compileShader(true, true); + return textureBlitDepthAligned; + }else { + if(textureBlitAligned == null) textureBlitAligned = compileShader(true, false); + return textureBlitAligned; + } + }else { + if(depth) { + if(textureBlitDepth == null) textureBlitDepth = compileShader(false, true); + return textureBlitDepth; + }else { + if(textureBlit == null) textureBlit = compileShader(false, false); + return textureBlit; + } + } + } + + public static void srcSize(int w, int h) { + srcViewW = w; + srcViewH = h; + } + + public static void dstSize(int w, int h) { + dstViewW = w * 0.5f; + dstViewH = h * 0.5f; + } + + public static void srcDstSize(int w, int h) { + srcViewW = w; + srcViewH = h; + dstViewW = w * 0.5f; + dstViewH = h * 0.5f; + } + + /** + * this is reset after every blit + */ + public static void alignPixels(int dstW, int dstH, float alignX, float alignY) { + isAligned = true; + alignW = dstW; + alignH = dstH; + alignOffsetX = alignX; + alignOffsetY = alignY; + } + + /** + * this is reset after every blit + */ + public static void alignPixelsTopLeft(int srcW, int srcH, int dstW, int dstH) { + alignPixels(dstW, dstH, (0.5f * dstW) / srcW, (0.5f * dstH) / srcH); + } + + public static void disableAlign() { + isAligned = false; + } + + public static void blitTexture(int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTexture(0, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTexture(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTexture(lvl, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTexture(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + blitTexture(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); + } + + public static void blitTexture(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + TextureCopyShader shaderObj = getShaderObj(isAligned, false); + EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); + _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); + _wglUniform4f(shaderObj.u_dstCoords4f, (float) dstX / dstViewW - 1.0f, (float) dstY / dstViewH - 1.0f, + (float) dstW / dstViewW, (float) dstH / dstViewH); + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(shaderObj.u_textureLod1f, lvl); + }else { + if(lvl != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl); + } + _wglUniform1f(shaderObj.u_textureLod1f, 0.0f); + } + if(isAligned) { + _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); + _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); + isAligned = false; + } + DrawUtils.drawStandardQuad2D(); + } + + public static void blitTexture() { + blitTexture(0); + } + + public static void blitTexture(int lvl) { + TextureCopyShader shaderObj = getShaderObj(isAligned, false); + EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); + _wglUniform4f(shaderObj.u_srcCoords4f, 0.0f, 0.0f, 1.0f, 1.0f); + _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(shaderObj.u_textureLod1f, lvl); + }else { + if(lvl != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl); + } + _wglUniform1f(shaderObj.u_textureLod1f, 0.0f); + } + if(isAligned) { + _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); + _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); + isAligned = false; + } + DrawUtils.drawStandardQuad2D(); + } + + public static void blitTextureUsingViewports(int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTextureUsingViewports(0, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTextureUsingViewports(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTextureUsingViewports(lvl, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTextureUsingViewports(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + blitTextureUsingViewports(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); + } + + public static void blitTextureUsingViewports(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + TextureCopyShader shaderObj = getShaderObj(isAligned, false); + EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); + GlStateManager.viewport(dstX, dstY, dstW, dstH); + _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); + _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(shaderObj.u_textureLod1f, lvl); + }else { + if(lvl != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl); + } + _wglUniform1f(shaderObj.u_textureLod1f, 0.0f); + } + if(isAligned) { + _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); + _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); + isAligned = false; + } + DrawUtils.drawStandardQuad2D(); + } + + public static void blitTextureDepth(int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTextureDepth(0, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTextureDepth(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTextureDepth(lvl, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTextureDepth(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + blitTextureDepth(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); + } + + public static void blitTextureDepth(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + TextureCopyShader shaderObj = getShaderObj(isAligned, true); + EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); + _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); + _wglUniform4f(shaderObj.u_dstCoords4f, (float) dstX / dstViewW - 1.0f, (float) dstY / dstViewH - 1.0f, + (float) dstW / dstViewW, (float) dstH / dstViewH); + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(shaderObj.u_textureLod1f, lvl); + }else { + if(lvl != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl); + } + _wglUniform1f(shaderObj.u_textureLod1f, 0.0f); + } + if(isAligned) { + _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); + _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); + isAligned = false; + } + DrawUtils.drawStandardQuad2D(); + } + + public static void blitTextureDepth() { + blitTextureDepth(0); + } + + public static void blitTextureDepth(int lvl) { + TextureCopyShader shaderObj = getShaderObj(isAligned, true); + EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); + _wglUniform4f(shaderObj.u_srcCoords4f, 0.0f, 0.0f, 1.0f, 1.0f); + _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(shaderObj.u_textureLod1f, lvl); + }else { + if(lvl != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl); + } + _wglUniform1f(shaderObj.u_textureLod1f, 0.0f); + } + if(isAligned) { + _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); + _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); + isAligned = false; + } + DrawUtils.drawStandardQuad2D(); + } + + public static void blitTextureDepthUsingViewports(int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTextureDepthUsingViewports(0, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTextureDepthUsingViewports(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) { + blitTextureDepthUsingViewports(lvl, srcX, srcY, w, h, dstX, dstY, w, h); + } + + public static void blitTextureDepthUsingViewports(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + blitTextureDepthUsingViewports(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); + } + + public static void blitTextureDepthUsingViewports(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) { + TextureCopyShader shaderObj = getShaderObj(isAligned, true); + EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram); + GlStateManager.viewport(dstX, dstY, dstW, dstH); + _wglUniform4f(shaderObj.u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH); + _wglUniform4f(shaderObj.u_dstCoords4f, -1.0f, -1.0f, 2.0f, 2.0f); + if(EaglercraftGPU.checkTextureLODCapable()) { + _wglUniform1f(shaderObj.u_textureLod1f, lvl); + }else { + if(lvl != 0.0f) { + LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl); + } + _wglUniform1f(shaderObj.u_textureLod1f, 0.0f); + } + if(isAligned) { + _wglUniform4f(shaderObj.u_pixelAlignmentSizes4f, alignW, alignH, 1.0f / alignW, 1.0f / alignH); + _wglUniform2f(shaderObj.u_pixelAlignmentOffset2f, alignOffsetX, alignOffsetY); + isAligned = false; + } + DrawUtils.drawStandardQuad2D(); + } + + public static void destroy() { + if(vshShader != null) { + _wglDeleteShader(vshShader); + vshShader = null; + } + if(textureBlit != null) { + textureBlit.destroy(); + textureBlit = null; + } + if(textureBlitAligned != null) { + textureBlitAligned.destroy(); + textureBlitAligned = null; + } + if(textureBlitDepth != null) { + textureBlitDepth.destroy(); + textureBlitDepth = null; + } + if(textureBlitDepthAligned != null) { + textureBlitDepthAligned.destroy(); + textureBlitDepthAligned = null; + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureFormatHelper.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureFormatHelper.java new file mode 100755 index 0000000..c8bb39b --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureFormatHelper.java @@ -0,0 +1,81 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ExtGLEnums.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TextureFormatHelper { + + public static int getFormatFromInternal(int internalFormat) { + switch(internalFormat) { + case _GL_R8: + case 0x822D: // GL_R16F + case 0x822E: // GL_R32F + return GL_RED; + case _GL_RG8: + case 0x822F: // GL_RG16F + case 0x8230: // GL_RG32F + return _GL_RG; + case GL_RGB8: + case _GL_RGB16F: + case 0x8815: // GL_RGB32F + return GL_RGB; + case GL_RGBA8: + case 0x881A: // GL_RGBA16F + case 0x8814: // GL_RGBA32F + return GL_RGBA; + default: + throw new UnsupportedOperationException(); + } + } + + public static int getTypeFromInternal(int internalFormat) { + switch(internalFormat) { + case _GL_R8: + case _GL_RG8: + case GL_RGB8: + case GL_RGBA8: + return GL_UNSIGNED_BYTE; + case 0x822D: // GL_R16F + case 0x822F: // GL_RG16F + case _GL_RGB16F: + case 0x881A: // GL_RGBA16F + return _GL_HALF_FLOAT; + case 0x822E: // GL_R32F + case 0x8230: // GL_RG32F + case 0x8815: // GL_RGB32F + case 0x8814: // GL_RGBA32F + return GL_FLOAT; + default: + throw new UnsupportedOperationException(); + } + } + + public static int trivializeInternalFormatToGLES20(int internalFormat) { + switch(internalFormat) { + case _GL_R8: + return GL_LUMINANCE; + case GL_RGB8: + return GL_RGB; + case GL_RGBA8: + return GL_RGBA; + default: + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/VSHInputLayoutParser.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/VSHInputLayoutParser.java new file mode 100755 index 0000000..72fbabd --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/VSHInputLayoutParser.java @@ -0,0 +1,91 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class VSHInputLayoutParser { + + public static class ShaderInput { + + public final int index; + public final String type; + public final String name; + + public ShaderInput(int index, String type, String name) { + this.index = index; + this.type = type; + this.name = name; + } + + } + + public static class ShaderLayoutParseException extends RuntimeException { + + public ShaderLayoutParseException(String message, Throwable cause) { + super(message, cause); + } + + public ShaderLayoutParseException(String message) { + super(message); + } + + } + + public static List getShaderInputs(String vshSource) { + int idx1 = vshSource.indexOf("EAGLER_VSH_LAYOUT_BEGIN()"); + if(idx1 == -1) { + throw new ShaderLayoutParseException("Could not find \"EAGLER_VSH_LAYOUT_BEGIN()\" delimiter!"); + } + int idx2 = vshSource.indexOf("EAGLER_VSH_LAYOUT_END()", idx1 + 25); + if(idx2 == -1) { + throw new ShaderLayoutParseException("Could not find \"EAGLER_VSH_LAYOUT_END()\" delimiter!"); + } + List lines = EagUtils.linesList(vshSource.substring(idx1 + 25, idx2)); + List ret = new ArrayList<>(); + for(int i = 0, l = lines.size(); i < l; ++i) { + String ln = lines.get(i); + ln = ln.trim(); + if(ln.startsWith("EAGLER_IN(") && ln.endsWith(")")) { + String[] tokens = ln.substring(10, ln.length() - 1).split(",", 3); + if(tokens.length == 3) { + int idx; + try { + idx = Integer.parseInt(tokens[0].trim()); + }catch(NumberFormatException ex) { + continue; + } + ret.add(new ShaderInput(idx, tokens[1].trim(), tokens[2].trim())); + } + } + } + return ret; + } + + public static void applyLayout(IProgramGL program, List layout) { + for(int i = 0, l = layout.size(); i < l; ++i) { + ShaderInput itm = layout.get(i); + _wglBindAttribLocation(program, itm.index, itm.name); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java index 8905ee0..3005659 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java @@ -8,51 +8,42 @@ import java.util.BitSet; import java.util.Comparator; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformBufferFunctions; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; import net.minecraft.client.renderer.GLAllocation; import net.minecraft.util.MathHelper; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class WorldRenderer { - + private boolean needsUpdate; private int drawMode; private double xOffset; private double yOffset; private double zOffset; private boolean isDrawing; - + private VertexFormat vertexFormat; - + private int vertexCount; private ByteBuffer byteBuffer; private IntBuffer intBuffer; private FloatBuffer floatBuffer; - + private boolean hasBeenFreed = false; public WorldRenderer(int bufferSizeIn) { @@ -60,14 +51,14 @@ public class WorldRenderer { this.intBuffer = this.byteBuffer.asIntBuffer(); this.floatBuffer = this.byteBuffer.asFloatBuffer(); } - + public void free() { - if (!hasBeenFreed) { + if(!hasBeenFreed) { hasBeenFreed = true; EagRuntime.freeByteBuffer(byteBuffer); } } - + public void finalize() { free(); } @@ -77,7 +68,7 @@ public class WorldRenderer { int i = this.byteBuffer.capacity() >> 2; if (parInt1 > (i - pos)) { int k = (((pos + parInt1 + (parInt1 >> 1)) >> 16) + 1) << 16; - LogManager.getLogger().warn("Needed to grow BufferBuilder buffer: Old size " + (i << 2) + + LogManager.getLogger() .warn("Needed to grow BufferBuilder buffer: Old size " + (i << 2) + " bytes, new size " + (k << 2) + " bytes."); ByteBuffer bytebuffer = GLAllocation.createDirectByteBuffer(k << 2); this.byteBuffer.position(0); @@ -129,7 +120,7 @@ public class WorldRenderer { for (int k1 = ainteger[i1].intValue(); j1 != l1; k1 = ainteger[k1].intValue()) { this.intBuffer.limit(k1 * l + l); this.intBuffer.position(k1 * l); - IntBuffer intbuffer = this.intBuffer.slice(); + IntBuffer intbuffer = this.intBuffer.duplicate(); this.intBuffer.limit(j1 * l + l); this.intBuffer.position(j1 * l); this.intBuffer.put(intbuffer); @@ -186,7 +177,10 @@ public class WorldRenderer { */ public void setVertexState(WorldRenderer.State state) { this.grow(state.getRawBuffer().length); - PlatformBufferFunctions.put(this.intBuffer, 0, state.getRawBuffer()); + int p = intBuffer.position(); + this.intBuffer.position(0); + this.intBuffer.put(state.getRawBuffer()); + this.intBuffer.position(p); this.vertexCount = state.getVertexCount(); this.vertexFormat = state.getVertexFormat(); } @@ -282,7 +276,7 @@ public class WorldRenderer { } this.intBuffer.put(i, j); } - + /** * sets color multiplier of a vertex parInt1 indicies before the current vertex */ @@ -347,9 +341,11 @@ public class WorldRenderer { */ public void addVertexData(int[] vertexData) { this.grow(vertexData.length); - PlatformBufferFunctions.put(this.intBuffer, (this.vertexCount * this.vertexFormat.attribStride) >> 2, - vertexData); - this.vertexCount += vertexData.length / (this.vertexFormat.attribStride >> 2); + int p = this.intBuffer.position(); + this.intBuffer.position((this.vertexCount * this.vertexFormat.attribStride) >> 2); + this.intBuffer.put(vertexData); + this.intBuffer.position(p); + this.vertexCount += vertexData.length / (this.vertexFormat.attribStride >> 2); } /** @@ -392,7 +388,7 @@ public class WorldRenderer { int i = (byte) ((int) (x * 127.0F)) & 255; int j = (byte) ((int) (y * 127.0F)) & 255; int k = (byte) ((int) (z * 127.0F)) & 255; - int l = i | j << 8 | k << 16 | ((byte) id) << 24; + int l = i | j << 8 | k << 16 | ((byte)id) << 24; VertexFormat fmt = this.vertexFormat; int i1 = fmt.attribStride; int j1 = (this.vertexCount - 4) * i1 + fmt.attribNormalOffset; @@ -405,7 +401,7 @@ public class WorldRenderer { /** * set normal of current vertex */ - public WorldRenderer normal(float parFloat1, float parFloat2, float parFloat3) { // TODO: crash with particles + public WorldRenderer normal(float parFloat1, float parFloat2, float parFloat3) { //TODO: crash with particles VertexFormat fmt = this.vertexFormat; int i = this.vertexCount * fmt.attribStride + fmt.attribNormalOffset; this.byteBuffer.put(i, (byte) ((int) parFloat1 * 127 & 255)); @@ -451,11 +447,11 @@ public class WorldRenderer { int jj1 = (this.vertexCount - 4) * i1 + fmt.attribNormalOffset; this.byteBuffer.putInt(jj1, l); this.byteBuffer.putInt(jj1 + i1, l); - if (!b) { + if(!b) { this.byteBuffer.putInt(jj1 + i1 * 2, l); } this.byteBuffer.putInt(jj1 + i1 * 3, l); - if (b) { + if(b) { j1 = (this.vertexCount - 2) * i1; tmpVec1.x = this.byteBuffer.getFloat(j1); tmpVec1.y = this.byteBuffer.getFloat(j1 + 4); @@ -524,10 +520,6 @@ public class WorldRenderer { } - public boolean isDrawing() { - return this.isDrawing; - } - public class State { private final int[] stateRawBuffer; private final VertexFormat stateVertexFormat; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java index 49f1428..2b5a273 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java @@ -16,31 +16,23 @@ import net.minecraft.util.ResourceLocation; /** * Copyright (c) 2023 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ - public class BlockVertexIDs implements IResourceManagerReloadListener { private static final Logger logger = LogManager.getLogger("BlockVertexIDsCSV"); - public static final Map modelToID = new HashMap(); + public static final Map modelToID = new HashMap<>(); public static int builtin_water_still_vertex_id = 0; public static int builtin_water_flow_vertex_id = 0; @@ -54,41 +46,41 @@ public class BlockVertexIDs implements IResourceManagerReloadListener { modelToID.clear(); String line; boolean firstLine = true; - while ((line = reader.readLine()) != null) { - if ((line = line.trim()).length() > 0) { - if (firstLine) { + while((line = reader.readLine()) != null) { + if((line = line.trim()).length() > 0) { + if(firstLine) { firstLine = false; continue; } String[] split = line.split(","); - if (split.length == 2) { + if(split.length == 2) { try { int i = Integer.parseInt(split[1]); - if (i <= 0 || i > 254) { + if(i <= 0 || i > 254) { logger.error("Error: {}: Only IDs 1 to 254 are configurable!", split[0]); throw new NumberFormatException(); } i -= 127; modelToID.put(split[0], i); - switch (split[0]) { - case "eagler:builtin/water_still_vertex_id": - builtin_water_still_vertex_id = i; - break; - case "eagler:builtin/water_flow_vertex_id": - builtin_water_flow_vertex_id = i; - break; - default: - break; + switch(split[0]) { + case "eagler:builtin/water_still_vertex_id": + builtin_water_still_vertex_id = i; + break; + case "eagler:builtin/water_flow_vertex_id": + builtin_water_flow_vertex_id = i; + break; + default: + break; } continue; - } catch (NumberFormatException ex) { + }catch(NumberFormatException ex) { } } logger.error("Skipping bad vertex id entry: {}", line); } } } - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Could not load list of vertex ids!"); logger.error(t); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/CloudRenderWorker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/CloudRenderWorker.java index 789e7fe..05698c4 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/CloudRenderWorker.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/CloudRenderWorker.java @@ -103,7 +103,7 @@ public class CloudRenderWorker { static void initialize() { destroy(); - cloudStartTimer = System.currentTimeMillis(); + cloudStartTimer = EagRuntime.steadyTimeMillis(); cloudRenderProgress = 0; cloudRenderPeriod = 500; cloudRenderPhase = 0; @@ -168,7 +168,7 @@ public class CloudRenderWorker { _wglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); _wglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); _wglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - byte[] cloudShapeTexture = EagRuntime.getResourceBytes("/assets/eagler/glsl/deferred/clouds_shapes.bmp"); + byte[] cloudShapeTexture = EagRuntime.getRequiredResourceBytes("/assets/eagler/glsl/deferred/clouds_shapes.bmp"); cloudNoiseDatBuffer = EagRuntime.allocateByteBuffer(cloudShapeTexture.length); cloudNoiseDatBuffer.put(cloudShapeTexture); cloudNoiseDatBuffer.flip(); @@ -207,7 +207,7 @@ public class CloudRenderWorker { } static void update() { - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); int cloudProgress = (int)(millis - cloudStartTimer); int totalCloudSteps = 32 + 32 - 1; int currentCloudStep = cloudProgress * totalCloudSteps / cloudRenderPeriod; @@ -268,7 +268,7 @@ public class CloudRenderWorker { cloudColorB += (currentSunAngle.z - cloudColorB) * 0.1f; _wglUniform3f(shader_clouds_sample.uniforms.u_sunColor3f, cloudColorR, cloudColorG, cloudColorB); - float cloudDensityTimer = (float)((System.currentTimeMillis() % 10000000l) * 0.001); + float cloudDensityTimer = (float)((EagRuntime.steadyTimeMillis() % 10000000l) * 0.001); cloudDensityTimer += MathHelper.sin(cloudDensityTimer * 1.5f) * 1.5f; float x = cloudDensityTimer * 0.004f; float f1 = MathHelper.sin(x + 0.322f) * 0.544f + MathHelper.sin(x * 4.5f + 1.843f) * 0.69f + MathHelper.sin(x * 3.4f + 0.8f) * 0.6f + MathHelper.sin(x * 6.1f + 1.72f) * 0.7f; @@ -404,7 +404,7 @@ public class CloudRenderWorker { if(b) { cloudRenderProgress = 0; - cloudStartTimer = System.currentTimeMillis(); + cloudStartTimer = EagRuntime.steadyTimeMillis(); cloudProgress = 0; cloudRenderPhase = (cloudRenderPhase + 1) % 3; }else { @@ -539,7 +539,7 @@ public class CloudRenderWorker { } private static void updateShape() { - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); float dt = (float)((millis - shapeUpdateTimer) * 0.001); shapeUpdateTimer = millis; if(millis > nextShapeAppearance) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java index dc19044..1a0913b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java @@ -4,12 +4,12 @@ import java.util.Arrays; import java.util.List; import java.util.function.Consumer; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.opengl.DrawUtils; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderGBufferDebugView; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.ScaledResolution; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; @@ -249,7 +249,7 @@ public class DebugFramebufferView { PipelineShaderGBufferDebugView dbg = pipeline.useDebugViewShader(18); GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture3D(CloudRenderWorker.cloud3DSamplesTexture); - _wglUniform1f(_wglGetUniformLocation(dbg.program, "u_fuckU1f"), (float)((System.currentTimeMillis() % 5000l) / 5000.0)); + _wglUniform1f(_wglGetUniformLocation(dbg.program, "u_fuckU1f"), (float)((EagRuntime.steadyTimeMillis() % 5000l) / 5000.0)); DrawUtils.drawStandardQuad2D(); })), (new DebugFramebufferView("Clouds Back Buffer", (pipeline) -> { @@ -449,19 +449,18 @@ public class DebugFramebufferView { GlStateManager.clear(GL_COLOR_BUFFER_BIT); noData = true; } - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); long elapsed = millis - debugViewNameTimer; if(elapsed < 2000l || noData) { GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.pushMatrix(); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.pushMatrix(); - ScaledResolution scaledresolution = new ScaledResolution(mc); - int w = scaledresolution.getScaledWidth(); + int w = mc.scaledResolution.getScaledWidth(); mc.entityRenderer.setupOverlayRendering(); GlStateManager.enableBlend(); GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - int h = scaledresolution.getScaledHeight() / 2; + int h = mc.scaledResolution.getScaledHeight() / 2; if(noData) { String noDataTxt = "No Data"; @@ -507,13 +506,13 @@ public class DebugFramebufferView { public static void toggleDebugView() { debugViewShown = !debugViewShown; if(debugViewShown) { - debugViewNameTimer = System.currentTimeMillis(); + debugViewNameTimer = EagRuntime.steadyTimeMillis(); } } public static void switchView(int dir) { if(!debugViewShown) return; - debugViewNameTimer = System.currentTimeMillis(); + debugViewNameTimer = EagRuntime.steadyTimeMillis(); currentDebugView += dir; if(currentDebugView < 0) currentDebugView = views.size() - 1; if(currentDebugView >= views.size()) currentDebugView = 0; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DeferredStateManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DeferredStateManager.java index f110e05..e55fadd 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DeferredStateManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DeferredStateManager.java @@ -15,21 +15,14 @@ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; /** * Copyright (c) 2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -126,8 +119,7 @@ public class DeferredStateManager { } public static final boolean isRenderingRealisticWater() { - return EaglerDeferredPipeline.instance != null - && EaglerDeferredPipeline.instance.config.is_rendering_realisticWater; + return EaglerDeferredPipeline.instance != null && EaglerDeferredPipeline.instance.config.is_rendering_realisticWater; } public static final boolean isRenderingGlassHighlights() { @@ -151,29 +143,26 @@ public class DeferredStateManager { public static final void reportForwardRenderObjectPosition(int centerX, int centerY, int centerZ) { EaglerDeferredPipeline instance = EaglerDeferredPipeline.instance; - if (instance != null && enableForwardRender) { + if(instance != null && enableForwardRender) { EaglerDeferredConfig cfg = instance.config; - if (!cfg.is_rendering_dynamicLights || !cfg.shaderPackInfo.DYNAMIC_LIGHTS) { + if(!cfg.is_rendering_dynamicLights || !cfg.shaderPackInfo.DYNAMIC_LIGHTS) { return; } - instance.loadLightSourceBucket(centerX, centerY, centerZ); + instance.bindLightSourceBucket(centerX, centerY, centerZ, 1); } } public static final void reportForwardRenderObjectPosition2(float x, float y, float z) { EaglerDeferredPipeline instance = EaglerDeferredPipeline.instance; - if (instance != null && enableForwardRender) { + if(instance != null && enableForwardRender) { EaglerDeferredConfig cfg = instance.config; - if (!cfg.is_rendering_dynamicLights || !cfg.shaderPackInfo.DYNAMIC_LIGHTS) { + if(!cfg.is_rendering_dynamicLights || !cfg.shaderPackInfo.DYNAMIC_LIGHTS) { return; } - float posX = (float) ((x + TileEntityRendererDispatcher.staticPlayerX) - - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerX / 16.0) << 4)); - float posY = (float) ((y + TileEntityRendererDispatcher.staticPlayerY) - - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerY / 16.0) << 4)); - float posZ = (float) ((z + TileEntityRendererDispatcher.staticPlayerZ) - - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerZ / 16.0) << 4)); - instance.loadLightSourceBucket((int) posX, (int) posY, (int) posZ); + float posX = (float)((x + TileEntityRendererDispatcher.staticPlayerX) - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerX / 16.0) << 4)); + float posY = (float)((y + TileEntityRendererDispatcher.staticPlayerY) - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerY / 16.0) << 4)); + float posZ = (float)((z + TileEntityRendererDispatcher.staticPlayerZ) - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerZ / 16.0) << 4)); + instance.bindLightSourceBucket((int)posX, (int)posY, (int)posZ, 1); } } @@ -368,22 +357,22 @@ public class DeferredStateManager { public static void setCurrentSunAngle(Vector3f vec) { currentSunAngle.set(vec); - if (vec.y > 0.05f) { + if(vec.y > 0.05f) { currentSunLightAngle.x = -vec.x; currentSunLightAngle.y = -vec.y; currentSunLightAngle.z = -vec.z; - } else { + }else { currentSunLightAngle.set(vec); } } public static void setCurrentSunAngle(Vector4f vec) { currentSunAngle.set(vec); - if (vec.y > 0.05f) { + if(vec.y > 0.05f) { currentSunLightAngle.x = -vec.x; currentSunLightAngle.y = -vec.y; currentSunLightAngle.z = -vec.z; - } else { + }else { currentSunLightAngle.set(vec); } } @@ -499,16 +488,16 @@ public class DeferredStateManager { } public static void checkGLError(String section) { - if (!doCheckErrors) { + if(!doCheckErrors) { return; } int i = EaglercraftGPU.glGetError(); - if (i != 0) { + if(i != 0) { EaglerDeferredPipeline.logger.error("########## GL ERROR ##########"); EaglerDeferredPipeline.logger.error("@ {}", section); do { EaglerDeferredPipeline.logger.error("#{} - {}", i, EaglercraftGPU.gluErrorString(i)); - } while ((i = EaglercraftGPU.glGetError()) != 0); + }while((i = EaglercraftGPU.glGetError()) != 0); EaglerDeferredPipeline.logger.error("##############################"); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightInstance.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightInstance.java index e0c45ca..a846bae 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightInstance.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightInstance.java @@ -1,5 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + /** * Copyright (c) 2023 lax1dude. All Rights Reserved. * @@ -35,7 +37,7 @@ class DynamicLightInstance { } public void updateLight(double posX, double posY, double posZ, float red, float green, float blue) { - this.lastCacheHit = System.currentTimeMillis(); + this.lastCacheHit = EagRuntime.steadyTimeMillis(); this.posX = posX; this.posY = posY; this.posZ = posZ; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightManager.java index 1315b34..5b91886 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DynamicLightManager.java @@ -6,6 +6,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + /** * Copyright (c) 2023 lax1dude. All Rights Reserved. * @@ -23,8 +25,8 @@ import java.util.Map; */ public class DynamicLightManager { - static final Map lightRenderers = new HashMap(); - static final List lightRenderList = new LinkedList(); + static final Map lightRenderers = new HashMap<>(); + static final List lightRenderList = new LinkedList<>(); static long renderTimeout = 5000l; static boolean isRenderLightsPass = false; @@ -51,7 +53,7 @@ public class DynamicLightManager { } static void updateTimers() { - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); if(millis - lastTick > 1000l) { lastTick = millis; Iterator itr = lightRenderers.values().iterator(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java index 6d644b6..4e9b63f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java @@ -71,23 +71,16 @@ import java.util.Iterator; import java.util.List; /** - * Copyright (c) 2023 lax1dude. All Rights Reserved. + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -119,6 +112,7 @@ public class EaglerDeferredPipeline { public double currentRenderX = 0.0; public double currentRenderY = 0.0; public double currentRenderZ = 0.0; + public int currentRenderPosSerial = 0; public IFramebufferGL gBufferFramebuffer = null; @@ -314,6 +308,7 @@ public class EaglerDeferredPipeline { private ByteBuffer worldLightingDataCopyBuffer; public IBufferGL buffer_chunkLightingData; + public IBufferGL buffer_chunkLightingDataZero; private ByteBuffer chunkLightingDataCopyBuffer; private boolean isChunkLightingEnabled = false; public ListSerial currentBoundLightSourceBucket; @@ -343,9 +338,20 @@ public class EaglerDeferredPipeline { public static final Vector3f tmpVector4 = new Vector3f(); public final ListSerial[] lightSourceBuckets; + private final int[] lightSourceBucketSerials; + private final int[] lightSourceRenderPosSerials; public ListSerial currentLightSourceBucket; + private int currentLightSourceBucketId = -1; + private int lightingBufferSliceLength = -1; public static final int MAX_LIGHTS_PER_CHUNK = 12; + public static final int LIGHTING_BUFFER_LENGTH = 32 * MAX_LIGHTS_PER_CHUNK + 16; + + private int uniformBufferOffsetAlignment = -1; + + private int uboAlign(int offset) { + return MathHelper.ceiling_float_int((float)offset / (float)uniformBufferOffsetAlignment) * uniformBufferOffsetAlignment; + } private final int lightSourceBucketsWidth; private final int lightSourceBucketsHeight; @@ -372,20 +378,25 @@ public class EaglerDeferredPipeline { public EaglerDeferredPipeline(Minecraft mc) { this.mc = mc; - if (matrixCopyBuffer == null) { + if(matrixCopyBuffer == null) { matrixCopyBuffer = GLAllocation.createDirectFloatBuffer(16); } this.lightSourceBucketsWidth = 5; this.lightSourceBucketsHeight = 3; int cnt = 5 * 3 * 5; this.lightSourceBuckets = new ListSerial[cnt]; - for (int i = 0; i < cnt; ++i) { - this.lightSourceBuckets[i] = new ArrayListSerial(16); + this.lightSourceBucketSerials = new int[cnt]; + this.lightSourceRenderPosSerials = new int[cnt]; + for(int i = 0; i < cnt; ++i) { + this.lightSourceBuckets[i] = new ArrayListSerial<>(16); + this.lightSourceBucketSerials[i] = -1; + this.lightSourceRenderPosSerials[i] = -1; } } public void rebuild(EaglerDeferredConfig config) { destroy(); + uniformBufferOffsetAlignment = EaglercraftGPU.getUniformBufferOffsetAlignment(); DeferredStateManager.doCheckErrors = EagRuntime.getConfiguration().isCheckShaderGLErrors(); DeferredStateManager.checkGLError("Pre: rebuild pipeline"); this.config = config; @@ -400,31 +411,27 @@ public class EaglerDeferredPipeline { gBufferDiffuseTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(gBufferDiffuseTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(gBufferDiffuseTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(gBufferDiffuseTexture), 0); gBufferNormalsTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(gBufferNormalsTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(gBufferNormalsTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(gBufferNormalsTexture), 0); gBufferMaterialTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(gBufferMaterialTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(gBufferMaterialTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(gBufferMaterialTexture), 0); gBufferDrawBuffers = new int[] { _GL_COLOR_ATTACHMENT0, _GL_COLOR_ATTACHMENT1, _GL_COLOR_ATTACHMENT2 }; _wglDrawBuffers(gBufferDrawBuffers); gBufferDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(gBufferDepthTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(gBufferDepthTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(gBufferDepthTexture), 0); DeferredStateManager.checkGLError("Post: rebuild pipeline: gbuffers"); boolean shadowsSun = config.is_rendering_shadowsSun_clamped > 0; - if (shadowsSun) { + if(shadowsSun) { sunShadowFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, sunShadowFramebuffer); sunShadowDepthBuffer = GlStateManager.generateTexture(); @@ -433,83 +440,72 @@ public class EaglerDeferredPipeline { _wglTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_COMPARE_FUNC, GL_GREATER); _wglTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_COMPARE_MODE, _GL_COMPARE_REF_TO_TEXTURE); int lods = config.is_rendering_shadowsSun_clamped; - if (lods > 3) { + if(lods > 3) { lods = 3; } sunShadowDepthBufferRes = 2048; - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT24, sunShadowDepthBufferRes, - sunShadowDepthBufferRes * lods, 0, _GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, (ByteBuffer) null); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(sunShadowDepthBuffer), 0); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT24, sunShadowDepthBufferRes, sunShadowDepthBufferRes * lods, 0, _GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, (ByteBuffer)null); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(sunShadowDepthBuffer), 0); sunLightingShadowFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, sunLightingShadowFramebuffer); sunLightingShadowTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(sunLightingShadowTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(sunLightingShadowTexture), 0); - if (config.is_rendering_shadowsColored) { + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(sunLightingShadowTexture), 0); + if(config.is_rendering_shadowsColored) { sunShadowColorFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, sunShadowColorFramebuffer); GlStateManager.bindTexture(sunShadowDepthBuffer); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(sunShadowDepthBuffer), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(sunShadowDepthBuffer), 0); sunShadowColorBuffer = GlStateManager.generateTexture(); GlStateManager.bindTexture(sunShadowColorBuffer); setNearest(); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sunShadowDepthBufferRes, sunShadowDepthBufferRes * lods, 0, - GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(sunShadowColorBuffer), 0); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sunShadowDepthBufferRes, sunShadowDepthBufferRes * lods, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(sunShadowColorBuffer), 0); } DeferredStateManager.checkGLError("Post: rebuild pipeline: shadowsSun"); } reprojectionEngineEnable = config.is_rendering_ssao || config.is_rendering_raytracing; - if (reprojectionEngineEnable || config.is_rendering_realisticWater) { + if(reprojectionEngineEnable || config.is_rendering_realisticWater) { lastFrameFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, lastFrameFramebuffer); lastFrameColorTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lastFrameColorTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lastFrameColorTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lastFrameColorTexture), 0); lastFrameDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lastFrameDepthTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lastFrameDepthTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lastFrameDepthTexture), 0); lastFrameGBufferFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, lastFrameGBufferFramebuffer); lastFrameGBufferDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lastFrameGBufferDepthTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lastFrameGBufferDepthTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lastFrameGBufferDepthTexture), 0); DeferredStateManager.checkGLError("Post: rebuild pipeline: lastFrame"); } - if (reprojectionEngineEnable) { + if(reprojectionEngineEnable) { gBufferQuarterFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, gBufferQuarterFramebuffer); gBufferQuarterDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(gBufferQuarterDepthTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(gBufferQuarterDepthTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(gBufferQuarterDepthTexture), 0); reprojectionStartup = 0; - for (int i = 0; i < 2; ++i) { + for(int i = 0; i < 2; ++i) { reprojectionControlFramebuffer[i] = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionControlFramebuffer[i]); - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { reprojectionControlSSAOTexture[i] = GlStateManager.generateTexture(); GlStateManager.bindTexture(reprojectionControlSSAOTexture[i]); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(reprojectionControlSSAOTexture[i]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(reprojectionControlSSAOTexture[i]), 0); } - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { reprojectionSSRTexture[i] = GlStateManager.generateTexture(); GlStateManager.bindTexture(reprojectionSSRTexture[0]); // yes this should be 0 _wglFramebufferTexture2D(_GL_FRAMEBUFFER, @@ -525,64 +521,58 @@ public class EaglerDeferredPipeline { _wglDrawBuffers(SSRColorAttachments); GlStateManager.bindTexture(reprojectionSSRTexture[i]); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(reprojectionSSRTexture[i]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(reprojectionSSRTexture[i]), 0); GlStateManager.bindTexture(reprojectionSSRHitVector[i]); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(reprojectionSSRHitVector[i]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(reprojectionSSRHitVector[i]), 0); } } - shader_reproject_control = PipelineShaderReprojControl.compile(config.is_rendering_ssao, - config.is_rendering_raytracing); + shader_reproject_control = PipelineShaderReprojControl.compile(config.is_rendering_ssao, config.is_rendering_raytracing); shader_reproject_control.loadUniforms(); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { shader_reproject_ssr = PipelineShaderReprojSSR.compile(); shader_reproject_ssr.loadUniforms(); } - reprojectionControlDrawBuffers = new int[(config.is_rendering_ssao ? 1 : 0) - + (config.is_rendering_raytracing ? 2 : 0)]; + reprojectionControlDrawBuffers = new int[(config.is_rendering_ssao ? 1 : 0) + (config.is_rendering_raytracing ? 2 : 0)]; int i = 0; - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { reprojectionControlDrawBuffers[i] = _GL_COLOR_ATTACHMENT0; ++i; } - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { reprojectionControlDrawBuffers[i] = _GL_COLOR_ATTACHMENT0 + i; ++i; reprojectionControlDrawBuffers[i] = _GL_COLOR_ATTACHMENT0 + i; } - for (int j = 0; j < 2; ++j) { + for(int j = 0; j < 2; ++j) { _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionControlFramebuffer[j]); _wglDrawBuffers(reprojectionControlDrawBuffers); } DeferredStateManager.checkGLError("Post: rebuild pipeline: reprojectionEngineEnable"); } - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { ssaoGenerateFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, ssaoGenerateFramebuffer); ssaoGenerateTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(ssaoGenerateTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(ssaoGenerateTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(ssaoGenerateTexture), 0); ssaoNoiseTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(ssaoNoiseTexture); setNearest(); int noiseTexSize = 64, noiseTexLen = 16384; - byte[] noiseTexDat = EagRuntime.getResourceBytes("assets/eagler/glsl/deferred/ssao_noise.bmp"); - if (noiseTexDat == null || noiseTexDat.length != noiseTexLen) { + byte[] noiseTexDat = EagRuntime.getRequiredResourceBytes("assets/eagler/glsl/deferred/ssao_noise.bmp"); + if(noiseTexDat == null || noiseTexDat.length != noiseTexLen) { noiseTexDat = new byte[noiseTexLen]; - for (int i = 0; i < 4096; ++i) { - noiseTexDat[(i << 2) + 2] = (byte) 255; // dumb fallback + for(int i = 0; i < 4096; ++i) { + noiseTexDat[(i << 2) + 2] = (byte)255; // dumb fallback } } ByteBuffer noiseTextureBytes = EagRuntime.allocateByteBuffer(noiseTexLen); noiseTextureBytes.put(noiseTexDat); noiseTextureBytes.flip(); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, noiseTexSize, noiseTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, - noiseTextureBytes); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, noiseTexSize, noiseTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, noiseTextureBytes); EagRuntime.freeByteBuffer(noiseTextureBytes); shader_ssao_generate = PipelineShaderSSAOGenerate.compile(); shader_ssao_generate.loadUniforms(); @@ -594,29 +584,24 @@ public class EaglerDeferredPipeline { lightingHDRFramebufferColorTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lightingHDRFramebufferColorTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lightingHDRFramebufferColorTexture), 0); lightingHDRFramebufferDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lightingHDRFramebufferDepthTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lightingHDRFramebufferDepthTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lightingHDRFramebufferDepthTexture), 0); handRenderFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, handRenderFramebuffer); GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lightingHDRFramebufferColorTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lightingHDRFramebufferColorTexture), 0); handRenderFramebufferDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(handRenderFramebufferDepthTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(handRenderFramebufferDepthTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(handRenderFramebufferDepthTexture), 0); shader_hand_depth_mask = PipelineShaderHandDepthMask.compile(); shader_hand_depth_mask.loadUniforms(); - shader_deferred_combine = PipelineShaderGBufferCombine.compile(config.is_rendering_ssao, - config.is_rendering_useEnvMap, config.is_rendering_raytracing); + shader_deferred_combine = PipelineShaderGBufferCombine.compile(config.is_rendering_ssao, config.is_rendering_useEnvMap, config.is_rendering_raytracing); shader_deferred_combine.loadUniforms(); DeferredStateManager.checkGLError("Post: rebuild pipeline: lightingHDRFramebuffer"); @@ -625,11 +610,11 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(brdfTexture); setLinear(); int brdfLutW = 64, brdfLutH = 64, brdfLutLen = 8192; - byte[] brdfLutDat = EagRuntime.getResourceBytes("assets/eagler/glsl/deferred/brdf_lut.bmp"); - if (brdfLutDat == null || brdfLutDat.length != brdfLutLen) { + byte[] brdfLutDat = EagRuntime.getRequiredResourceBytes("assets/eagler/glsl/deferred/brdf_lut.bmp"); + if(brdfLutDat == null || brdfLutDat.length != brdfLutLen) { brdfLutDat = new byte[brdfLutLen]; - for (int i = 0; i < 4096; ++i) { - brdfLutDat[i << 1] = (byte) 192; // dumb fallback + for(int i = 0; i < 4096; ++i) { + brdfLutDat[i << 1] = (byte)192; // dumb fallback } } ByteBuffer brdfLutDatBuffer = EagRuntime.allocateByteBuffer(brdfLutDat.length); @@ -654,7 +639,7 @@ public class EaglerDeferredPipeline { shader_lighting_sun = PipelineShaderLightingSun.compile(shadowsSun ? config.is_rendering_shadowsSun_clamped : 0, config.is_rendering_shadowsColored); shader_lighting_sun.loadUniforms(); - if (shadowsSun) { + if(shadowsSun) { shader_shadows_sun = PipelineShaderShadowsSun.compile(config.is_rendering_shadowsSun_clamped, config.is_rendering_shadowsSmoothed, config.is_rendering_shadowsColored); shader_shadows_sun.loadUniforms(); @@ -672,28 +657,26 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("Post: rebuild pipeline: compile shaders 1"); - if (config.is_rendering_lensFlares) { + if(config.is_rendering_lensFlares) { sunOcclusionValueFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, sunOcclusionValueFramebuffer); sunOcclusionValueTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(sunOcclusionValueTexture); setNearest(); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, (ByteBuffer) null); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(sunOcclusionValueTexture), 0); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, (ByteBuffer)null); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(sunOcclusionValueTexture), 0); shader_lens_sun_occlusion = PipelineShaderLensSunOcclusion.compile(); shader_lens_sun_occlusion.loadUniforms(); DeferredStateManager.checkGLError("Post: rebuild pipeline: sunOcclusionValueFramebuffer"); } - if (config.is_rendering_lensDistortion) { + if(config.is_rendering_lensDistortion) { lensDistortFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, lensDistortFramebuffer); lensDistortTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lensDistortTexture); setLinear(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lensDistortTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lensDistortTexture), 0); shader_post_lens_distort = PipelineShaderLensDistortion.compile(); shader_post_lens_distort.loadUniforms(); DeferredStateManager.checkGLError("Post: rebuild pipeline: lens distortion"); @@ -707,8 +690,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(exposureBlendTexture); setNearest(); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 1, 1, GL_RED, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(exposureBlendTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(exposureBlendTexture), 0); DeferredStateManager.checkGLError("Post: rebuild pipeline: exposureBlendFramebuffer"); @@ -719,8 +701,7 @@ public class EaglerDeferredPipeline { throw new RuntimeException("Failed to load skybox!", e); } - pointLightMesh = new LightSourceMesh(new ResourceLocation("eagler:glsl/deferred/light_point_mesh.dat"), - "light_point_mesh"); + pointLightMesh = new LightSourceMesh(new ResourceLocation("eagler:glsl/deferred/light_point_mesh.dat"), "light_point_mesh"); try { pointLightMesh.load(); } catch (IOException e) { @@ -734,10 +715,8 @@ public class EaglerDeferredPipeline { atmosphereHDRFramebufferColorTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(atmosphereHDRFramebufferColorTexture); setNearest(); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, skybox.getAtmosLUTWidth(), - skybox.getAtmosLUTHeight(), GL_RGBA, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(atmosphereHDRFramebufferColorTexture), 0); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, skybox.getAtmosLUTWidth(), skybox.getAtmosLUTHeight(), GL_RGBA, true); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(atmosphereHDRFramebufferColorTexture), 0); envMapAtmosphereFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, envMapAtmosphereFramebuffer); @@ -745,8 +724,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(envMapAtmosphereTexture); setLinear(); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 128, 256, GL_RGBA, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(envMapAtmosphereTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(envMapAtmosphereTexture), 0); envMapSkyFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, envMapSkyFramebuffer); @@ -754,8 +732,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(envMapSkyTexture); setLinear(); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 128, 256, GL_RGBA, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(envMapSkyTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(envMapSkyTexture), 0); irradiancePhase = 0; @@ -765,8 +742,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(atmosphereIrradianceTexture); setLinear(); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 32, 64, GL_RGBA, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(atmosphereIrradianceTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(atmosphereIrradianceTexture), 0); GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 0.0f); GlStateManager.clear(GL_COLOR_BUFFER_BIT); @@ -776,8 +752,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(skyIrradianceTexture); setLinear(); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 32, 64, GL_RGBA, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(skyIrradianceTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(skyIrradianceTexture), 0); GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 0.0f); GlStateManager.clear(GL_COLOR_BUFFER_BIT); @@ -791,21 +766,22 @@ public class EaglerDeferredPipeline { _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ByteBuffer copyBuffer = EagRuntime.allocateByteBuffer(262144); int mip = 0; - try (DataInputStream dis = new DataInputStream( - EagRuntime.getResourceStream("/assets/eagler/glsl/deferred/eagler_moon.bmp"))) { - while (dis.read() == 'E') { + + try (DataInputStream dis = new DataInputStream(mc.getResourceManager() + .getResource(new ResourceLocation("eagler:glsl/deferred/eagler_moon.bmp")).getInputStream())) { + while(dis.read() == 'E') { int w = dis.readShort(); int h = dis.readShort(); copyBuffer.clear(); - for (int i = 0, l = w * h * 4; i < l; ++i) { - copyBuffer.put((byte) dis.read()); + for(int i = 0, l = w * h * 4; i < l; ++i) { + copyBuffer.put((byte)dis.read()); } copyBuffer.flip(); _wglTexImage2D(GL_TEXTURE_2D, mip++, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, copyBuffer); } - } catch (IOException ex) { + }catch(IOException ex) { throw new RuntimeException("Could not load \"eagler_moon.bmp\"!", ex); - } finally { + }finally { EagRuntime.freeByteBuffer(copyBuffer); } _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mip - 1); @@ -821,8 +797,7 @@ public class EaglerDeferredPipeline { fogDepthCopyTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(fogDepthCopyTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(fogDepthCopyTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(fogDepthCopyTexture), 0); shader_atmosphere_fog = PipelineShaderGBufferFog.compile(false, true, config.is_rendering_lightShafts); shader_atmosphere_fog.loadUniforms(); @@ -833,46 +808,40 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("Post: rebuild pipeline: fog"); - if (config.is_rendering_useEnvMap) { + if(config.is_rendering_useEnvMap) { envMapFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, envMapFramebuffer); envMapColorTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(envMapColorTexture); setLinear(); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 128, 256, GL_RGBA, true); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(envMapColorTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(envMapColorTexture), 0); envMapDepthTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(envMapDepthTexture); setNearest(); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT24, 128, 256, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, - (ByteBuffer) null); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(envMapDepthTexture), 0); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT24, 128, 256, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, (ByteBuffer)null); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(envMapDepthTexture), 0); DeferredStateManager.checkGLError("Post: rebuild pipeline: env map"); } - if (config.is_rendering_realisticWater) { + if(config.is_rendering_realisticWater) { realisticWaterMaskFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterMaskFramebuffer); realisticWaterMaskTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterMaskTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterMaskTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterMaskTexture), 0); realisticWaterDepthBuffer = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterDepthBuffer); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterDepthBuffer), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterDepthBuffer), 0); realisticWaterCombinedNormalsFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterCombinedNormalsFramebuffer); realisticWaterCombinedNormalsTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterCombinedNormalsTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterCombinedNormalsTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterCombinedNormalsTexture), 0); realisticWaterRefractionTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterRefractionTexture); setNearest(); @@ -881,28 +850,23 @@ public class EaglerDeferredPipeline { realisticWaterControlReflectionTexture[0] = GlStateManager.generateTexture(); realisticWaterControlReflectionTexture[1] = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterControlReflectionTexture[0]); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterControlReflectionTexture[0]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterControlReflectionTexture[0]), 0); realisticWaterControlHitVectorTexture[0] = GlStateManager.generateTexture(); realisticWaterControlHitVectorTexture[1] = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[0]); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterControlHitVectorTexture[0]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterControlHitVectorTexture[0]), 0); GlStateManager.bindTexture(realisticWaterRefractionTexture); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterRefractionTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterRefractionTexture), 0); _wglDrawBuffers(new int[] { _GL_COLOR_ATTACHMENT0, _GL_COLOR_ATTACHMENT1, _GL_COLOR_ATTACHMENT2 }); - for (int i = 0; i < 2; ++i) { + for(int i = 0; i < 2; ++i) { realisticWaterSSRFramebuffer[i] = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[i]); GlStateManager.bindTexture(realisticWaterControlReflectionTexture[i]); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterControlReflectionTexture[i]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterControlReflectionTexture[i]), 0); GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[i]); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterControlHitVectorTexture[i]), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterControlHitVectorTexture[i]), 0); _wglDrawBuffers(new int[] { _GL_COLOR_ATTACHMENT0, _GL_COLOR_ATTACHMENT1 }); } realisticWaterDisplacementMapFramebuffer = _wglCreateFramebuffer(); @@ -910,8 +874,7 @@ public class EaglerDeferredPipeline { realisticWaterDisplacementMapTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterDisplacementMapTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterDisplacementMapTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterDisplacementMapTexture), 0); EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 256, 256, GL_RED, true); realisticWaterNormalMapFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterNormalMapFramebuffer); @@ -921,9 +884,8 @@ public class EaglerDeferredPipeline { _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(realisticWaterNormalMapTexture), 0); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_RG8, 256, 256, 0, _GL_RG, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(realisticWaterNormalMapTexture), 0); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_RG8, 256, 256, 0, _GL_RG, GL_UNSIGNED_BYTE, (ByteBuffer)null); realisticWaterNoiseMap = GlStateManager.generateTexture(); GlStateManager.bindTexture(realisticWaterNoiseMap); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); @@ -931,12 +893,12 @@ public class EaglerDeferredPipeline { _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); String realistic_water_noise_filename = "assets/eagler/glsl/deferred/realistic_water_noise.bmp"; - byte[] bitmapBytes = EagRuntime.getResourceBytes(realistic_water_noise_filename); + byte[] bitmapBytes = EagRuntime.getRequiredResourceBytes(realistic_water_noise_filename); try { - if (bitmapBytes.length != 32768) { + if(bitmapBytes.length != 32768) { throw new IOException("File is length " + bitmapBytes.length + ", expected " + 32768); } - } catch (Throwable t) { + }catch(Throwable t) { throw new RuntimeException("File \"" + realistic_water_noise_filename + "\" could not be loaded!", t); } ByteBuffer buf = EagRuntime.allocateByteBuffer(32768); @@ -951,7 +913,7 @@ public class EaglerDeferredPipeline { shader_realistic_water_normals = PipelineShaderRealisticWaterNormalMap.compile(); shader_realistic_water_normals.loadUniforms(); _wglUniform2f(shader_realistic_water_normals.uniforms.u_sampleOffset2f, 0.00390625f, 0.00390625f); - if (!config.is_rendering_raytracing) { + if(!config.is_rendering_raytracing) { shader_reproject_ssr = PipelineShaderReprojSSR.compile(); shader_reproject_ssr.loadUniforms(); } @@ -959,19 +921,18 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("Post: rebuild pipeline: realistic water"); } - if (config.is_rendering_fxaa) { + if(config.is_rendering_fxaa) { tonemapOutputFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, tonemapOutputFramebuffer); tonemapOutputTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(tonemapOutputTexture); setNearest(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(tonemapOutputTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(tonemapOutputTexture), 0); DeferredStateManager.checkGLError("Post: rebuild pipeline: fxaa"); } - if (config.is_rendering_lensFlares) { + if(config.is_rendering_lensFlares) { LensFlareMeshRenderer.initialize(); DeferredStateManager.checkGLError("Post: rebuild pipeline: lensFlares"); } @@ -1004,22 +965,20 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("Post: rebuild pipeline: skybox shaders"); - if (config.is_rendering_lightShafts) { + if(config.is_rendering_lightShafts) { lightShaftsFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, lightShaftsFramebuffer); lightShaftsTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(lightShaftsTexture); setLinear(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(lightShaftsTexture), 0); - shader_light_shafts_sample = PipelineShaderLightShaftsSample - .compile(config.is_rendering_shadowsSun_clamped); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(lightShaftsTexture), 0); + shader_light_shafts_sample = PipelineShaderLightShaftsSample.compile(config.is_rendering_shadowsSun_clamped); shader_light_shafts_sample.loadUniforms(); DeferredStateManager.checkGLError("Post: rebuild pipeline: light shafts"); } - if (config.is_rendering_bloom) { + if(config.is_rendering_bloom) { bloomBrightPassFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomBrightPassFramebuffer); bloomBrightPassTexture = GlStateManager.generateTexture(); @@ -1027,22 +986,19 @@ public class EaglerDeferredPipeline { setNearest(); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(bloomBrightPassTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(bloomBrightPassTexture), 0); bloomDownscaleAFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomDownscaleAFramebuffer); bloomDownscaleATexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(bloomDownscaleATexture); setLinear(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(bloomDownscaleATexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(bloomDownscaleATexture), 0); bloomDownscaleBFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomDownscaleBFramebuffer); bloomDownscaleBTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(bloomDownscaleBTexture); setLinear(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(bloomDownscaleBTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(bloomDownscaleBTexture), 0); bloomHBlurFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomHBlurFramebuffer); bloomHBlurTexture = GlStateManager.generateTexture(); @@ -1050,15 +1006,13 @@ public class EaglerDeferredPipeline { setNearest(); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(bloomHBlurTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(bloomHBlurTexture), 0); bloomVBlurFramebuffer = _wglCreateFramebuffer(); _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomVBlurFramebuffer); bloomVBlurTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(bloomVBlurTexture); setLinear(); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - EaglercraftGPU.getNativeTexture(bloomVBlurTexture), 0); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(bloomVBlurTexture), 0); shader_post_bloom_bright = PipelineShaderBloomBrightPass.compile(); shader_post_bloom_bright.loadUniforms(); shader_post_bloom_blur = PipelineShaderBloomBlurPass.compile(); @@ -1070,19 +1024,26 @@ public class EaglerDeferredPipeline { gbufferEffectRenderer.initialize(); forwardEffectRenderer.initialize(config.is_rendering_dynamicLights, config.is_rendering_shadowsSun_clamped); - if (config.is_rendering_dynamicLights) { + if(config.is_rendering_dynamicLights) { shader_lighting_point = PipelineShaderLightingPoint.compile(false); shader_lighting_point.loadUniforms(); - buffer_chunkLightingData = _wglGenBuffers(); - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - int lightingDataLength = 8 * MAX_LIGHTS_PER_CHUNK + 4; - chunkLightingDataCopyBuffer = EagRuntime.allocateByteBuffer(lightingDataLength << 2); - for (int i = 0; i < lightingDataLength; ++i) { + lightingBufferSliceLength = uboAlign(LIGHTING_BUFFER_LENGTH); + + chunkLightingDataCopyBuffer = EagRuntime.allocateByteBuffer(LIGHTING_BUFFER_LENGTH); + for(int i = 0; i < LIGHTING_BUFFER_LENGTH; i += 4) { chunkLightingDataCopyBuffer.putInt(0); } chunkLightingDataCopyBuffer.flip(); - _wglBufferData(_GL_UNIFORM_BUFFER, chunkLightingDataCopyBuffer, GL_DYNAMIC_DRAW); + + buffer_chunkLightingData = _wglGenBuffers(); + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); + int cnt = lightSourceBucketsWidth * lightSourceBucketsHeight * lightSourceBucketsWidth; + _wglBufferData(_GL_UNIFORM_BUFFER, cnt * lightingBufferSliceLength, GL_DYNAMIC_DRAW); + + buffer_chunkLightingDataZero = _wglGenBuffers(); + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingDataZero); + _wglBufferData(_GL_UNIFORM_BUFFER, chunkLightingDataCopyBuffer, GL_STATIC_DRAW); DeferredStateManager.checkGLError("Post: rebuild pipeline: dynamic lights"); } @@ -1090,7 +1051,7 @@ public class EaglerDeferredPipeline { buffer_worldLightingData = _wglGenBuffers(); EaglercraftGPU.bindGLUniformBuffer(buffer_worldLightingData); worldLightingDataCopyBuffer = EagRuntime.allocateByteBuffer(304); - for (int i = 0; i < 76; ++i) { + for(int i = 0; i < 76; ++i) { worldLightingDataCopyBuffer.putInt(0); } worldLightingDataCopyBuffer.flip(); @@ -1100,20 +1061,29 @@ public class EaglerDeferredPipeline { FixedFunctionPipeline.loadExtensionPipeline(deferredExtPipeline); - if (!EaglercraftGPU.checkHDRFramebufferSupport(16)) { - logger.warn( - "16-bit HDR (floating point) framebuffers are not supported on this device, 32-bit framebuffers will be used instead which may slow the game down"); + if(!EaglercraftGPU.checkHDRFramebufferSupport(16)) { + logger.warn("16-bit HDR (floating point) framebuffers are not supported on this device, 32-bit framebuffers will be used instead which may slow the game down"); } _wglBindFramebuffer(_GL_FRAMEBUFFER, null); DeferredStateManager.checkGLError("Post: rebuild pipeline"); } + public void setRenderPosGlobal(double renderPosX, double renderPosY, double renderPosZ) { + if (renderPosX != currentRenderX || renderPosY != currentRenderY || renderPosZ != currentRenderZ + || currentRenderPosSerial == 0) { + currentRenderX = renderPosX; + currentRenderY = renderPosY; + currentRenderZ = renderPosZ; + ++currentRenderPosSerial; + } + } + public void updateReprojectionCoordinates(double worldX, double worldY, double worldZ) { double distX = worldX - reprojectionOriginCoordinateX; double distY = worldY - reprojectionOriginCoordinateY; double distZ = worldZ - reprojectionOriginCoordinateZ; - if (distX * distX + distY * distY + distZ * distZ > 48.0 * 48.0) { + if(distX * distX + distY * distY + distZ * distZ > 48.0 * 48.0) { reprojectionOriginCoordinateX = worldX; reprojectionOriginCoordinateY = worldY; reprojectionOriginCoordinateZ = worldZ; @@ -1121,19 +1091,19 @@ public class EaglerDeferredPipeline { reprojectionViewerOffsetY = 0.0f; reprojectionViewerOffsetZ = 0.0f; reprojectionStartup = 0; - } else { + }else { reprojectionViewerOffsetX = (float) distX; reprojectionViewerOffsetY = (float) distY; reprojectionViewerOffsetZ = (float) distZ; } distX = worldX - cloudRenderOriginCoordinateX; distZ = worldZ - cloudRenderOriginCoordinateZ; - if (distX * distX + distZ * distZ > 256.0 * 256.0) { + if(distX * distX + distZ * distZ > 256.0 * 256.0) { cloudRenderOriginCoordinateX = worldX; cloudRenderOriginCoordinateZ = worldZ; cloudRenderViewerOffsetX = 0.0f; cloudRenderViewerOffsetZ = 0.0f; - } else { + }else { cloudRenderViewerOffsetX = (float) distX; cloudRenderViewerOffsetZ = (float) distZ; } @@ -1148,31 +1118,30 @@ public class EaglerDeferredPipeline { } public void resize(int w, int h) { - if (w == currentWidth && h == currentHeight) { + if(w == currentWidth && h == currentHeight) { return; } DeferredStateManager.checkGLError("Pre: resize pipeline to " + w + " x " + h); GlStateManager.bindTexture(gBufferDiffuseTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); GlStateManager.bindTexture(gBufferNormalsTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); GlStateManager.bindTexture(gBufferMaterialTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); GlStateManager.bindTexture(gBufferDepthTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, - (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: gbuffer"); - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { GlStateManager.bindTexture(sunLightingShadowTexture); - if (config.is_rendering_shadowsColored) { - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); - } else { - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, (ByteBuffer) null); + if(config.is_rendering_shadowsColored) { + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + }else { + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, (ByteBuffer)null); } DeferredStateManager.checkGLError("Post: resize pipeline: sunLightingShadowTexture"); } @@ -1182,128 +1151,107 @@ public class EaglerDeferredPipeline { reprojectionTexHeight = h >> 1; shader_deferred_combine.useProgram(); - _wglUniform2f(shader_deferred_combine.uniforms.u_halfResolutionPixelAlignment2f, - (float) w / (reprojectionTexWidth << 1), (float) h / (reprojectionTexHeight << 1)); + _wglUniform2f(shader_deferred_combine.uniforms.u_halfResolutionPixelAlignment2f, (float)w / (reprojectionTexWidth << 1), (float)h / (reprojectionTexHeight << 1)); - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { GlStateManager.bindTexture(ssaoGenerateTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, reprojectionTexWidth, reprojectionTexHeight, 0, GL_RED, - GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, reprojectionTexWidth, reprojectionTexHeight, 0, GL_RED, GL_UNSIGNED_BYTE, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: ssao"); } - if (reprojectionEngineEnable || config.is_rendering_realisticWater) { + if(reprojectionEngineEnable || config.is_rendering_realisticWater) { GlStateManager.bindTexture(lastFrameColorTexture); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, - GL_RGBA, true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, GL_RGBA, true); GlStateManager.bindTexture(lastFrameDepthTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, reprojectionTexWidth, reprojectionTexHeight, 0, - _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, reprojectionTexWidth, reprojectionTexHeight, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); GlStateManager.bindTexture(lastFrameGBufferDepthTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, - (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: lastFrame"); } - if (config.is_rendering_raytracing || config.is_rendering_realisticWater) { + if(config.is_rendering_raytracing || config.is_rendering_realisticWater) { shader_reproject_ssr.useProgram(); - _wglUniform4f(shader_reproject_ssr.uniforms.u_pixelAlignment4f, reprojectionTexWidth, reprojectionTexHeight, - w, h); + _wglUniform4f(shader_reproject_ssr.uniforms.u_pixelAlignment4f, reprojectionTexWidth, reprojectionTexHeight, w, h); } - if (reprojectionEngineEnable) { + if(reprojectionEngineEnable) { GlStateManager.bindTexture(gBufferQuarterDepthTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, reprojectionTexWidth, reprojectionTexHeight, 0, - _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, reprojectionTexWidth, reprojectionTexHeight, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); - for (int i = 0; i < 2; ++i) { - if (config.is_rendering_ssao) { + for(int i = 0; i < 2; ++i) { + if(config.is_rendering_ssao) { GlStateManager.bindTexture(reprojectionControlSSAOTexture[i]); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, reprojectionTexWidth, reprojectionTexHeight, 0, GL_RGBA, - GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, reprojectionTexWidth, reprojectionTexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); } - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { GlStateManager.bindTexture(reprojectionSSRTexture[i]); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, - reprojectionTexHeight, GL_RGBA, true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, GL_RGBA, true); GlStateManager.bindTexture(reprojectionSSRHitVector[i]); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, - reprojectionTexHeight, GL_RGBA, true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, GL_RGBA, true); } } shader_reproject_control.useProgram(); - _wglUniform4f(shader_reproject_control.uniforms.u_pixelAlignment4f, reprojectionTexWidth, - reprojectionTexHeight, w, h); + _wglUniform4f(shader_reproject_control.uniforms.u_pixelAlignment4f, reprojectionTexWidth, reprojectionTexHeight, w, h); DeferredStateManager.checkGLError("Post: resize pipeline: reprojectionEngineEnable"); } - if (config.is_rendering_realisticWater) { + if(config.is_rendering_realisticWater) { GlStateManager.bindTexture(realisticWaterMaskTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); GlStateManager.bindTexture(realisticWaterDepthBuffer); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, - (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); GlStateManager.bindTexture(realisticWaterCombinedNormalsTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); GlStateManager.bindTexture(realisticWaterRefractionTexture); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, - GL_RGBA, true); - for (int i = 0; i < 2; ++i) { + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, GL_RGBA, true); + for(int i = 0; i < 2; ++i) { GlStateManager.bindTexture(realisticWaterControlReflectionTexture[i]); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, - reprojectionTexHeight, GL_RGBA, true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, GL_RGBA, true); GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[i]); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, - reprojectionTexHeight, GL_RGBA, true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, reprojectionTexWidth, reprojectionTexHeight, GL_RGBA, true); } shader_realistic_water_control.useProgram(); - _wglUniform4f(shader_realistic_water_control.uniforms.u_pixelAlignment4f, reprojectionTexWidth, - reprojectionTexHeight, w, h); + _wglUniform4f(shader_realistic_water_control.uniforms.u_pixelAlignment4f, reprojectionTexWidth, reprojectionTexHeight, w, h); DeferredStateManager.checkGLError("Post: resize pipeline: realisticWater"); } - if (config.is_rendering_lightShafts) { + if(config.is_rendering_lightShafts) { GlStateManager.bindTexture(lightShaftsTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, reprojectionTexWidth, reprojectionTexHeight, 0, GL_RED, - GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_R8, reprojectionTexWidth, reprojectionTexHeight, 0, GL_RED, GL_UNSIGNED_BYTE, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: lightShafts"); } GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, w, h, GL_RGBA, true); // USE RGBA! WebGL won't - // render to RGB16F + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, w, h, GL_RGBA, true); // USE RGBA! WebGL won't render to RGB16F GlStateManager.bindTexture(lightingHDRFramebufferDepthTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, - (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); GlStateManager.bindTexture(handRenderFramebufferDepthTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, - (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: lightingHDRFramebuffer"); GlStateManager.bindTexture(fogDepthCopyTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, - (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, _GL_DEPTH_COMPONENT32F, w, h, 0, _GL_DEPTH_COMPONENT, GL_FLOAT, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: fogDepthCopyTexture"); - if (config.is_rendering_lensDistortion) { + if(config.is_rendering_lensDistortion) { GlStateManager.bindTexture(lensDistortTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: lensDistortion"); } - if (config.is_rendering_fxaa) { + if(config.is_rendering_fxaa) { GlStateManager.bindTexture(tonemapOutputTexture); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); DeferredStateManager.checkGLError("Post: resize pipeline: fxaa"); } - if (config.is_rendering_bloom) { + if(config.is_rendering_bloom) { int bloomStageW = w; int bloomStageH = h; GlStateManager.bindTexture(bloomBrightPassTexture); @@ -1312,29 +1260,27 @@ public class EaglerDeferredPipeline { bloomBrightPassTextureH = bloomStageH; bloomDownscaleATextureW = bloomDownscaleATextureH = 0; bloomDownscaleBTextureW = bloomDownscaleBTextureH = 0; - if (bloomStageW > 150 && bloomStageH > 85) { + if(bloomStageW > 150 && bloomStageH > 85) { setLinear(); bloomStageW >>= 1; bloomStageH >>= 1; - if (bloomStageW > 150 && bloomStageH > 85) { + if(bloomStageW > 150 && bloomStageH > 85) { GlStateManager.bindTexture(bloomDownscaleATexture); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, bloomStageW, bloomStageH, GL_RGBA, - true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, bloomStageW, bloomStageH, GL_RGBA, true); bloomDownscaleATextureW = bloomStageW; bloomDownscaleATextureH = bloomStageH; bloomStageW >>= 1; bloomStageH >>= 1; - if (bloomStageW > 150 && bloomStageH > 85) { + if(bloomStageW > 150 && bloomStageH > 85) { GlStateManager.bindTexture(bloomDownscaleBTexture); - EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, bloomStageW, bloomStageH, - GL_RGBA, true); + EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, bloomStageW, bloomStageH, GL_RGBA, true); bloomDownscaleBTextureW = bloomStageW; bloomDownscaleBTextureH = bloomStageH; bloomStageW >>= 1; bloomStageH >>= 1; } } - } else { + }else { setNearest(); } GlStateManager.bindTexture(bloomHBlurTexture); @@ -1346,21 +1292,21 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("Post: resize pipeline: bloom"); } - if (lumaAvgDownscaleFramebuffers != null) { - for (int i = 0; i < lumaAvgDownscaleFramebuffers.length; ++i) { + if(lumaAvgDownscaleFramebuffers != null) { + for(int i = 0; i < lumaAvgDownscaleFramebuffers.length; ++i) { _wglDeleteFramebuffer(lumaAvgDownscaleFramebuffers[i]); } } - if (lumaAvgDownscaleTexture != null) { - for (int i = 0; i < lumaAvgDownscaleTexture.length; ++i) { + if(lumaAvgDownscaleTexture != null) { + for(int i = 0; i < lumaAvgDownscaleTexture.length; ++i) { GlStateManager.deleteTexture(lumaAvgDownscaleTexture[i]); } } int j = 0; int k = h > w ? w : h; - while (k > 8) { + while(k > 8) { ++j; k >>= 2; } @@ -1371,7 +1317,7 @@ public class EaglerDeferredPipeline { int kw = w; int kh = h; int kw2, kh2; - for (int i = 0; i < j; ++i) { + for(int i = 0; i < j; ++i) { kw2 = kw >> 2; kh2 = kh >> 2; lumaAvgDownscaleFramebuffers[i] = _wglCreateFramebuffer(); @@ -1452,14 +1398,14 @@ public class EaglerDeferredPipeline { public void beginDrawMainShadowMap() { DeferredStateManager.checkGLError("Pre: beginDrawMainShadowMap()"); - if (config.is_rendering_shadowsColored) { + if(config.is_rendering_shadowsColored) { _wglBindFramebuffer(_GL_FRAMEBUFFER, sunShadowColorFramebuffer); _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); GlStateManager.clearColor(1.0f, 1.0f, 1.0f, 1.0f); GlStateManager.clearDepth(1.0f); GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _wglBindFramebuffer(_GL_FRAMEBUFFER, sunShadowFramebuffer); - } else { + }else { _wglBindFramebuffer(_GL_FRAMEBUFFER, sunShadowFramebuffer); _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); GlStateManager.clearDepth(1.0f); @@ -1543,16 +1489,13 @@ public class EaglerDeferredPipeline { Matrix4f.invert(tmpMatrixViewProj, tmpMatrixInverseViewProj); Entity renderViewEntity = mc.getRenderViewEntity(); - if (renderViewEntity == null) { + if(renderViewEntity == null) { renderViewEntity = mc.thePlayer; } - double entityPosX = renderViewEntity.prevPosX - + (renderViewEntity.posX - renderViewEntity.prevPosX) * partialTicks; - double entityPosY = renderViewEntity.prevPosY - + (renderViewEntity.posY - renderViewEntity.prevPosY) * partialTicks; - double entityPosZ = renderViewEntity.prevPosZ - + (renderViewEntity.posZ - renderViewEntity.prevPosZ) * partialTicks; + double entityPosX = renderViewEntity.prevPosX + (renderViewEntity.posX - renderViewEntity.prevPosX) * partialTicks; + double entityPosY = renderViewEntity.prevPosY + (renderViewEntity.posY - renderViewEntity.prevPosY) * partialTicks; + double entityPosZ = renderViewEntity.prevPosZ + (renderViewEntity.posZ - renderViewEntity.prevPosZ) * partialTicks; int entityChunkOriginX = MathHelper.floor_double(entityPosX / 16.0) << 4; int entityChunkOriginY = MathHelper.floor_double(entityPosY / 16.0) << 4; int entityChunkOriginZ = MathHelper.floor_double(entityPosZ / 16.0) << 4; @@ -1561,20 +1504,20 @@ public class EaglerDeferredPipeline { float sunKelvin = 1500.0f + (2500.0f * Math.max(-currentSunAngle.y, 0.0f)); float fff = mc.theWorld.getRainStrength(partialTicks); float ff2 = mc.theWorld.getThunderStrength(partialTicks); - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); int dim = Minecraft.getMinecraft().theWorld.provider.getDimensionId(); // ==================== UPDATE CLOUD RENDERER ===================== // - if (dim == 0) { - CloudRenderWorker.setPosition(cloudRenderViewerOffsetX, (float) entityPosY, cloudRenderViewerOffsetZ); + if(dim == 0) { + CloudRenderWorker.setPosition(cloudRenderViewerOffsetX, (float)entityPosY, cloudRenderViewerOffsetZ); CloudRenderWorker.update(); DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): UPDATE CLOUD RENDERER"); } - if (millis - recalcAtmosphereTimer > 100l) { + if(millis - recalcAtmosphereTimer > 100l) { - if (dim == 0) { + if(dim == 0) { // =============== CALCULATE ATMOSPHERE COLORS ================ // @@ -1584,10 +1527,8 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(skybox.getNormalsLUT()); GlStateManager.viewport(0, 0, skybox.getAtmosLUTWidth(), skybox.getAtmosLUTHeight()); - _wglUniform4f(shader_skybox_atmosphere.uniforms.u_sunDirectionIntensity4f, -currentSunAngle.x, - -currentSunAngle.y, -currentSunAngle.z, 5.0f); - _wglUniform1f(shader_skybox_atmosphere.uniforms.u_altitude1f, - Math.max((float) (entityPosY - 85.0), -20.0f)); + _wglUniform4f(shader_skybox_atmosphere.uniforms.u_sunDirectionIntensity4f, -currentSunAngle.x, -currentSunAngle.y, -currentSunAngle.z, 5.0f); + _wglUniform1f(shader_skybox_atmosphere.uniforms.u_altitude1f, Math.max((float)(entityPosY - 85.0), -20.0f)); Vector3f sunColorTmp = tmpVector3; sunColorTmp.set(DeferredStateManager.currentSunLightColor); float luma = sunColorTmp.x * 0.299f + sunColorTmp.y * 0.587f + sunColorTmp.z * 0.114f; @@ -1596,8 +1537,7 @@ public class EaglerDeferredPipeline { sunColorTmp.y = (sunColorTmp.y - luma) * sat + luma; sunColorTmp.z = (sunColorTmp.z - luma) * sat + luma; sunColorTmp.scale(0.3f - ff2 * 0.175f); - _wglUniform4f(shader_skybox_atmosphere.uniforms.u_blendColor4f, sunColorTmp.x * 0.05f, - sunColorTmp.y * 0.05f, sunColorTmp.z * 0.05f, fff); + _wglUniform4f(shader_skybox_atmosphere.uniforms.u_blendColor4f, sunColorTmp.x * 0.05f, sunColorTmp.y * 0.05f, sunColorTmp.z * 0.05f, fff); DrawUtils.drawStandardQuad2D(); @@ -1612,52 +1552,45 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(atmosphereHDRFramebufferColorTexture); shader_skybox_render_paraboloid.useProgram(); - uniformMatrixHelper(shader_skybox_render_paraboloid.uniforms.u_viewMatrix4f, - DeferredStateManager.paraboloidTopViewMatrix); + uniformMatrixHelper(shader_skybox_render_paraboloid.uniforms.u_viewMatrix4f, DeferredStateManager.paraboloidTopViewMatrix); _wglUniform1f(shader_skybox_render_paraboloid.uniforms.u_farPlane1f, 2.0f); if (mc.theWorld.getLastLightningBolt() > 0) { float f = 0.3f + fff; - _wglUniform4f(shader_skybox_render_paraboloid.uniforms.u_lightningColor4f, 0.02f * f, 0.02f * f, - 0.02f * f, 1.0f - f * 0.25f); - } else { + _wglUniform4f(shader_skybox_render_paraboloid.uniforms.u_lightningColor4f, 0.02f * f, 0.02f * f, 0.02f * f, 1.0f - f * 0.25f); + }else { _wglUniform4f(shader_skybox_render_paraboloid.uniforms.u_lightningColor4f, 0.0f, 0.0f, 0.0f, 1.0f); } skybox.drawTop(); GlStateManager.viewport(0, 128, 128, 128); - uniformMatrixHelper(shader_skybox_render_paraboloid.uniforms.u_viewMatrix4f, - DeferredStateManager.paraboloidBottomViewMatrix); + uniformMatrixHelper(shader_skybox_render_paraboloid.uniforms.u_viewMatrix4f, DeferredStateManager.paraboloidBottomViewMatrix); skybox.drawBottom(); DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): GENERATE SKY REFLECTION MAP"); - if (irradiancePhase++ % 10 == 0) { + + if(irradiancePhase++ % 10 == 0) { // =============== GENERATE ATMOSPHERE REFLECTION MAP ================ // _wglBindFramebuffer(_GL_FRAMEBUFFER, envMapAtmosphereFramebuffer); GlStateManager.viewport(0, 0, 128, 128); shader_skybox_render_paraboloid_noclouds.useProgram(); - uniformMatrixHelper(shader_skybox_render_paraboloid_noclouds.uniforms.u_viewMatrix4f, - DeferredStateManager.paraboloidTopViewMatrix); + uniformMatrixHelper(shader_skybox_render_paraboloid_noclouds.uniforms.u_viewMatrix4f, DeferredStateManager.paraboloidTopViewMatrix); _wglUniform1f(shader_skybox_render_paraboloid_noclouds.uniforms.u_farPlane1f, 2.0f); if (mc.theWorld.getLastLightningBolt() > 0) { float f = 0.3f + fff; - _wglUniform4f(shader_skybox_render_paraboloid_noclouds.uniforms.u_lightningColor4f, 0.02f * f, - 0.02f * f, 0.02f * f, 1.0f - f * 0.25f); - } else { - _wglUniform4f(shader_skybox_render_paraboloid_noclouds.uniforms.u_lightningColor4f, 0.0f, 0.0f, - 0.0f, 1.0f); + _wglUniform4f(shader_skybox_render_paraboloid_noclouds.uniforms.u_lightningColor4f, 0.02f * f, 0.02f * f, 0.02f * f, 1.0f - f * 0.25f); + }else { + _wglUniform4f(shader_skybox_render_paraboloid_noclouds.uniforms.u_lightningColor4f, 0.0f, 0.0f, 0.0f, 1.0f); } skybox.drawTop(); GlStateManager.viewport(0, 128, 128, 128); - uniformMatrixHelper(shader_skybox_render_paraboloid_noclouds.uniforms.u_viewMatrix4f, - DeferredStateManager.paraboloidBottomViewMatrix); + uniformMatrixHelper(shader_skybox_render_paraboloid_noclouds.uniforms.u_viewMatrix4f, DeferredStateManager.paraboloidBottomViewMatrix); skybox.drawBottom(); - DeferredStateManager - .checkGLError("combineGBuffersAndIlluminate(): GENERATE ATMOSPHERE REFLECTION MAP"); + DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): GENERATE ATMOSPHERE REFLECTION MAP"); // =============== GENERATE ATMOSPHERE IRRADIANCE MAP ================ // @@ -1679,10 +1612,9 @@ public class EaglerDeferredPipeline { GlStateManager.disableBlend(); - DeferredStateManager - .checkGLError("combineGBuffersAndIlluminate(): GENERATE ATMOSPHERE IRRADIANCE MAP"); + DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): GENERATE ATMOSPHERE IRRADIANCE MAP"); - } else { + }else { // =============== GENERATE SKY IRRADIANCE MAP ================ // @@ -1706,7 +1638,7 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): GENERATE SKY IRRADIANCE MAP"); } - } else if (dim == -1) { + }else if(dim == -1) { // =============== NETHER SKY REFLECTION MAP ================ // @@ -1724,7 +1656,7 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): NETHER SKY IRRADIANCE MAP"); - } else if (dim == 1) { + }else if(dim == 1) { // =============== END SKY REFLECTION MAP ================ // @@ -1746,7 +1678,7 @@ public class EaglerDeferredPipeline { } - if (reprojectionEngineEnable) { + if(reprojectionEngineEnable) { // ============ DOWNSCALE DEPTH BUFFER, FOR PERFORMANCE =========== // @@ -1758,17 +1690,15 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(gBufferDepthTexture); _wglDrawBuffers(GL_NONE); GlStateManager.viewport(0, 0, reprojectionTexWidth, reprojectionTexHeight); - TextureCopyUtil.alignPixelsTopLeft(reprojectionTexWidth << 1, reprojectionTexHeight << 1, - reprojectionTexWidth, reprojectionTexHeight); + TextureCopyUtil.alignPixelsTopLeft(reprojectionTexWidth << 1, reprojectionTexHeight << 1, reprojectionTexWidth, reprojectionTexHeight); TextureCopyUtil.blitTextureDepth(); GlStateManager.disableDepth(); GlStateManager.depthMask(false); GlStateManager.depthFunc(GL_LEQUAL); - DeferredStateManager - .checkGLError("combineGBuffersAndIlluminate(): DOWNSCALE DEPTH BUFFER, FOR PERFORMANCE"); + DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): DOWNSCALE DEPTH BUFFER, FOR PERFORMANCE"); - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { // ====================== RUN SSAO ALGORITHM ====================== // @@ -1781,19 +1711,13 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(gBufferQuarterDepthTexture); shader_ssao_generate.useProgram(); - uniformMatrixHelper(shader_ssao_generate.uniforms.u_projectionMatrix4f, - DeferredStateManager.projMatrix); - uniformMatrixHelper(shader_ssao_generate.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_ssao_generate.uniforms.u_projectionMatrix4f, DeferredStateManager.projMatrix); + uniformMatrixHelper(shader_ssao_generate.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); matrixCopyBuffer.clear(); - matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) - + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); - matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) - + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); - matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) - + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); - matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) - + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); + matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); + matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); + matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); + matrixCopyBuffer.put(((random.nextFloat() * 25.0f - 12.5f) + (random.nextBoolean() ? 1.0f : -1.0f) * (random.nextFloat() * 6.0f + 6.0f)) * 10.0f); matrixCopyBuffer.flip(); _wglUniformMatrix2fv(shader_ssao_generate.uniforms.u_randomizerDataMatrix2f, false, matrixCopyBuffer); @@ -1807,46 +1731,45 @@ public class EaglerDeferredPipeline { // ============== RUN REPROJECTION CONTROL SHADER ================ // GlStateManager.setActiveTexture(GL_TEXTURE8); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { GlStateManager.bindTexture(gBufferMaterialTexture); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE7); - GlStateManager.bindTexture(lastFrameGBufferDepthTexture); // may be full of garbage data, let's pretend it - // won't + GlStateManager.bindTexture(lastFrameGBufferDepthTexture); // may be full of garbage data, let's pretend it won't GlStateManager.setActiveTexture(GL_TEXTURE6); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { GlStateManager.bindTexture(lastFrameColorTexture); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE5); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { GlStateManager.bindTexture(reprojectionSSRHitVector[1]); // may be garbage data - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE4); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { GlStateManager.bindTexture(reprojectionSSRTexture[1]); // may be garbage data - } else { + }else { GlStateManager.bindTexture(-1); } - + GlStateManager.setActiveTexture(GL_TEXTURE3); GlStateManager.bindTexture(gBufferNormalsTexture); GlStateManager.setActiveTexture(GL_TEXTURE2); - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { GlStateManager.bindTexture(reprojectionControlSSAOTexture[1 - reprojectionPhase]); // may be garbage - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE1); - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { GlStateManager.bindTexture(ssaoGenerateTexture); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE0); @@ -1860,16 +1783,12 @@ public class EaglerDeferredPipeline { Matrix4f.mul(DeferredStateManager.viewMatrix, tmpMatrix1, tmpMatrixViewReproject); Matrix4f.mul(tmpMatrixViewProj, tmpMatrix1, tmpMatrixViewProjReproject); Matrix4f.invert(tmpMatrixViewProjReproject, tmpMatrixInverseViewProjReproject); - - uniformMatrixHelper(shader_reproject_control.uniforms.u_inverseViewProjMatrix4f, - tmpMatrixInverseViewProjReproject); - uniformMatrixHelper(shader_reproject_control.uniforms.u_reprojectionMatrix4f, - tmpMatrixLastFrameViewProjReproject); - if (config.is_rendering_raytracing) { - uniformMatrixHelper(shader_reproject_control.uniforms.u_projectionMatrix4f, - DeferredStateManager.projMatrix); - uniformMatrixHelper(shader_reproject_control.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + + uniformMatrixHelper(shader_reproject_control.uniforms.u_inverseViewProjMatrix4f, tmpMatrixInverseViewProjReproject); + uniformMatrixHelper(shader_reproject_control.uniforms.u_reprojectionMatrix4f, tmpMatrixLastFrameViewProjReproject); + if(config.is_rendering_raytracing) { + uniformMatrixHelper(shader_reproject_control.uniforms.u_projectionMatrix4f, DeferredStateManager.projMatrix); + uniformMatrixHelper(shader_reproject_control.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); Matrix4f.invert(tmpMatrixLastFrameProj, tmpMatrix1); uniformMatrixHelper(shader_reproject_control.uniforms.u_lastInverseProjMatrix4f, tmpMatrix1); Matrix4f.invert(tmpMatrixLastFrameViewReproject, tmpMatrix1); @@ -1880,15 +1799,14 @@ public class EaglerDeferredPipeline { uniformMatrixHelper(shader_reproject_control.uniforms.u_viewToPreviousProjMatrix4f, tmpMatrix1); } _wglUniform4f(shader_reproject_control.uniforms.u_nearFarPlane4f, DeferredStateManager.gbufferNearPlane, - DeferredStateManager.gbufferFarPlane, - DeferredStateManager.gbufferNearPlane * DeferredStateManager.gbufferFarPlane * 2.0f, + DeferredStateManager.gbufferFarPlane, DeferredStateManager.gbufferNearPlane * DeferredStateManager.gbufferFarPlane * 2.0f, DeferredStateManager.gbufferFarPlane - DeferredStateManager.gbufferNearPlane); DrawUtils.drawStandardQuad2D(); DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RUN REPROJECTION CONTROL SHADER"); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { // =========== RUN SCREENSPACE REFLECTIONS ALGORITHM ============= // @@ -1908,8 +1826,7 @@ public class EaglerDeferredPipeline { _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[1]); shader_reproject_ssr.useProgram(); - uniformMatrixHelper(shader_reproject_ssr.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_reproject_ssr.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); Matrix4f.mul(tmpMatrixLastFrameViewProjReproject, tmpMatrixInverseViewProjReproject, tmpMatrix1); Matrix4f.mul(tmpMatrix1, DeferredStateManager.projMatrix, tmpMatrix1); uniformMatrixHelper(shader_reproject_ssr.uniforms.u_lastProjectionMatrix4f, tmpMatrix1); @@ -1924,8 +1841,7 @@ public class EaglerDeferredPipeline { matrixCopyBuffer.put(tmpMatrix1.m32); matrixCopyBuffer.put(tmpMatrix1.m33); matrixCopyBuffer.flip(); - _wglUniformMatrix4x2fv(shader_reproject_ssr.uniforms.u_lastInverseProjMatrix4x2f, false, - matrixCopyBuffer); + _wglUniformMatrix4x2fv(shader_reproject_ssr.uniforms.u_lastInverseProjMatrix4x2f, false, matrixCopyBuffer); _wglUniform1f(shader_reproject_ssr.uniforms.u_sampleStep1f, 0.125f); DrawUtils.drawStandardQuad2D(); // sample 1 @@ -1962,12 +1878,11 @@ public class EaglerDeferredPipeline { DrawUtils.drawStandardQuad2D(); // sample 5 - DeferredStateManager - .checkGLError("combineGBuffersAndIlluminate(): RUN SCREENSPACE REFLECTIONS ALGORITHM"); + DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RUN SCREENSPACE REFLECTIONS ALGORITHM"); } } - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { // ==================== RENDER SUNLIGHT SHADOWS ===================== // @@ -1975,17 +1890,16 @@ public class EaglerDeferredPipeline { GlStateManager.viewport(0, 0, currentWidth, currentHeight); shader_shadows_sun.useProgram(); - uniformMatrixHelper(shader_shadows_sun.uniforms.u_inverseViewMatrix4f, - DeferredStateManager.inverseViewMatrix); + uniformMatrixHelper(shader_shadows_sun.uniforms.u_inverseViewMatrix4f, DeferredStateManager.inverseViewMatrix); uniformMatrixHelper(shader_shadows_sun.uniforms.u_inverseViewProjMatrix4f, tmpMatrixInverseViewProj); - if (config.is_rendering_shadowsColored) { + if(config.is_rendering_shadowsColored) { GlStateManager.setActiveTexture(GL_TEXTURE3); GlStateManager.bindTexture(sunShadowColorBuffer); } GlStateManager.setActiveTexture(GL_TEXTURE2); GlStateManager.bindTexture(sunShadowDepthBuffer); - if (config.is_rendering_shadowsSmoothed) { + if(config.is_rendering_shadowsSmoothed) { setLinear(); } GlStateManager.setActiveTexture(GL_TEXTURE1); @@ -1994,24 +1908,20 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(gBufferNormalsTexture); Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix0, tmpShadowLOD0MatrixTexSpace); uniformMatrixHelper(shader_shadows_sun.uniforms.u_sunShadowMatrixLOD04f, tmpShadowLOD0MatrixTexSpace); - if (config.is_rendering_shadowsSun_clamped > 1) { - Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix1, - tmpShadowLOD1MatrixTexSpace); + if(config.is_rendering_shadowsSun_clamped > 1) { + Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix1, tmpShadowLOD1MatrixTexSpace); uniformMatrixHelper(shader_shadows_sun.uniforms.u_sunShadowMatrixLOD14f, tmpShadowLOD1MatrixTexSpace); - if (config.is_rendering_shadowsSun_clamped > 2) { - Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix2, - tmpShadowLOD2MatrixTexSpace); - uniformMatrixHelper(shader_shadows_sun.uniforms.u_sunShadowMatrixLOD24f, - tmpShadowLOD2MatrixTexSpace); + if(config.is_rendering_shadowsSun_clamped > 2) { + Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix2, tmpShadowLOD2MatrixTexSpace); + uniformMatrixHelper(shader_shadows_sun.uniforms.u_sunShadowMatrixLOD24f, tmpShadowLOD2MatrixTexSpace); } } Vector3f currentSunShadowAngle = DeferredStateManager.currentSunLightAngle; - _wglUniform3f(shader_shadows_sun.uniforms.u_sunDirection3f, -currentSunShadowAngle.x, - -currentSunShadowAngle.y, -currentSunShadowAngle.z); + _wglUniform3f(shader_shadows_sun.uniforms.u_sunDirection3f, -currentSunShadowAngle.x, -currentSunShadowAngle.y, -currentSunShadowAngle.z); DrawUtils.drawStandardQuad2D(); - if (config.is_rendering_shadowsSmoothed) { + if(config.is_rendering_shadowsSmoothed) { GlStateManager.setActiveTexture(GL_TEXTURE2); setNearest(); GlStateManager.setActiveTexture(GL_TEXTURE0); @@ -2025,14 +1935,13 @@ public class EaglerDeferredPipeline { GlStateManager.viewport(0, 0, currentWidth, currentHeight); _wglBindFramebuffer(_GL_READ_FRAMEBUFFER, gBufferFramebuffer); _wglBindFramebuffer(_GL_DRAW_FRAMEBUFFER, lightingHDRFramebuffer); - _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, - GL_NEAREST); + _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); _wglBindFramebuffer(_GL_FRAMEBUFFER, lightingHDRFramebuffer); - if (dim == -1) { + if(dim == -1) { float f = 0.13f; GlStateManager.clearColor(0.57f * 0.57f * f, 0.38f * 0.38f * f, 0.20f * 0.20f * f, 0.0f); - } else { + }else { GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 0.0f); } GlStateManager.clear(GL_COLOR_BUFFER_BIT); @@ -2048,21 +1957,21 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE7); GlStateManager.bindTexture(skyIrradianceTexture); GlStateManager.setActiveTexture(GL_TEXTURE6); - if (config.is_rendering_useEnvMap) { + if(config.is_rendering_useEnvMap) { GlStateManager.bindTexture(envMapColorTexture); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE5); - if (config.is_rendering_raytracing) { + if(config.is_rendering_raytracing) { GlStateManager.bindTexture(reprojectionSSRTexture[1]); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE4); - if (config.is_rendering_ssao) { + if(config.is_rendering_ssao) { GlStateManager.bindTexture(reprojectionControlSSAOTexture[reprojectionPhase]); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE3); @@ -2075,16 +1984,12 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(gBufferDiffuseTexture); shader_deferred_combine.useProgram(); - uniformMatrixHelper(shader_deferred_combine.uniforms.u_inverseViewMatrix4f, - DeferredStateManager.inverseViewMatrix); - uniformMatrixHelper(shader_deferred_combine.uniforms.u_inverseProjMatrix4f, - DeferredStateManager.inverseProjMatrix); - _wglUniform3f(shader_deferred_combine.uniforms.u_sunDirection3f, DeferredStateManager.currentSunAngle.x, - DeferredStateManager.currentSunAngle.y, DeferredStateManager.currentSunAngle.z); + uniformMatrixHelper(shader_deferred_combine.uniforms.u_inverseViewMatrix4f, DeferredStateManager.inverseViewMatrix); + uniformMatrixHelper(shader_deferred_combine.uniforms.u_inverseProjMatrix4f, DeferredStateManager.inverseProjMatrix); + _wglUniform3f(shader_deferred_combine.uniforms.u_sunDirection3f, DeferredStateManager.currentSunAngle.x, DeferredStateManager.currentSunAngle.y, DeferredStateManager.currentSunAngle.z); float lightningBoost = mc.theWorld.getLastLightningBolt() > 0 ? 1.0f : 0.0f; lightningBoost *= 0.3f + fff; - _wglUniform1f(shader_deferred_combine.uniforms.u_skyLightFactor1f, - getSkyBrightnessTimeParam() + lightningBoost); + _wglUniform1f(shader_deferred_combine.uniforms.u_skyLightFactor1f, getSkyBrightnessTimeParam() + lightningBoost); DrawUtils.drawStandardQuad2D(); DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RENDER AMBIENT LIGHTING"); @@ -2096,86 +2001,77 @@ public class EaglerDeferredPipeline { // ==================== RENDER SUNLIGHT ===================== // - if (dim == 0) { + if(dim == 0) { shader_lighting_sun.useProgram(); - uniformMatrixHelper(shader_lighting_sun.uniforms.u_inverseViewMatrix4f, - DeferredStateManager.inverseViewMatrix); - uniformMatrixHelper(shader_lighting_sun.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_lighting_sun.uniforms.u_inverseViewMatrix4f, DeferredStateManager.inverseViewMatrix); + uniformMatrixHelper(shader_lighting_sun.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); GlStateManager.setActiveTexture(GL_TEXTURE5); GlStateManager.bindTexture(MetalsLUT.getGLTexture()); GlStateManager.setActiveTexture(GL_TEXTURE4); - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { GlStateManager.bindTexture(sunLightingShadowTexture); - } else { + }else { GlStateManager.bindTexture(-1); } GlStateManager.setActiveTexture(GL_TEXTURE0); - + float ffff = getSkyBrightnessParam(); float[] sunRGB; - if (currentSunAngle.y < 0.05f) { - sunRGB = TemperaturesLUT.getColorTemperature((int) sunKelvin); + if(currentSunAngle.y < 0.05f) { + sunRGB = TemperaturesLUT.getColorTemperature((int)sunKelvin); Vector3f currentSunLightColor3f = DeferredStateManager.currentSunLightColor; // reuse variable currentSunLightColor3f.x = sunRGB[0] * 8.0f * (0.1f + ffff * 0.9f); currentSunLightColor3f.y = sunRGB[1] * 8.0f * (0.1f + ffff * 0.9f); currentSunLightColor3f.z = sunRGB[2] * 8.0f * (0.1f + ffff * 0.9f); - _wglUniform3f(shader_lighting_sun.uniforms.u_sunColor3f, sunRGB[0] * 4.0f * ffff, - sunRGB[1] * 4.0f * ffff, sunRGB[2] * 4.0f * ffff); - } else { - sunRGB = TemperaturesLUT.getColorTemperature((int) (9000.0f + 2500.0f * currentSunAngle.y)); + _wglUniform3f(shader_lighting_sun.uniforms.u_sunColor3f, sunRGB[0] * 4.0f * ffff, sunRGB[1] * 4.0f * ffff, sunRGB[2] * 4.0f * ffff); + }else { + sunRGB = TemperaturesLUT.getColorTemperature((int)(9000.0f + 2500.0f * currentSunAngle.y)); Vector3f currentSunLightColor3f = DeferredStateManager.currentSunLightColor; // reuse variable currentSunLightColor3f.x = sunRGB[0] * 0.3f * (0.2f + ffff * 0.8f); currentSunLightColor3f.y = sunRGB[1] * 0.3f * (0.2f + ffff * 0.8f); currentSunLightColor3f.z = sunRGB[2] * 0.3f * (0.2f + ffff * 0.8f); - _wglUniform3f(shader_lighting_sun.uniforms.u_sunColor3f, sunRGB[0] * 0.1f * (0.5f + ffff * 0.5f), - sunRGB[1] * 0.1f * (0.5f + ffff * 0.5f), sunRGB[2] * 0.1f * (0.5f + ffff * 0.5f)); + _wglUniform3f(shader_lighting_sun.uniforms.u_sunColor3f, sunRGB[0] * 0.1f * (0.5f + ffff * 0.5f), sunRGB[1] * 0.1f * (0.5f + ffff * 0.5f), sunRGB[2] * 0.1f * (0.5f + ffff * 0.5f)); } - - _wglUniform3f(shader_lighting_sun.uniforms.u_sunDirection3f, -DeferredStateManager.currentSunLightAngle.x, - -DeferredStateManager.currentSunLightAngle.y, -DeferredStateManager.currentSunLightAngle.z); - + + _wglUniform3f(shader_lighting_sun.uniforms.u_sunDirection3f, -DeferredStateManager.currentSunLightAngle.x, -DeferredStateManager.currentSunLightAngle.y, -DeferredStateManager.currentSunLightAngle.z); + DrawUtils.drawStandardQuad2D(); - + DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RENDER SUNLIGHT"); - } else { + }else { DeferredStateManager.currentSunLightColor.set(0.0f, 0.0f, 0.0f); } // ================== RENDER DYNAMIC LIGHTS =================== // - if (config.is_rendering_dynamicLights) { + if(config.is_rendering_dynamicLights) { shader_lighting_point.useProgram(); - uniformMatrixHelper(shader_lighting_point.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); - uniformMatrixHelper(shader_lighting_point.uniforms.u_inverseViewMatrix4f, - DeferredStateManager.inverseViewMatrix); + uniformMatrixHelper(shader_lighting_point.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_lighting_point.uniforms.u_inverseViewMatrix4f, DeferredStateManager.inverseViewMatrix); _wglUniform2f(shader_lighting_point.uniforms.u_viewportSize2f, 1.0f / currentWidth, 1.0f / currentHeight); Iterator itr = DynamicLightManager.lightRenderList.iterator(); AxisAlignedBB aabb = renderViewEntity.getEntityBoundingBox(); double eyeHeight = renderViewEntity.getEyeHeight(); - while (itr.hasNext()) { + while(itr.hasNext()) { DynamicLightInstance dl = itr.next(); - float lightPosX = (float) (dl.posX - entityPosX); - float lightPosY = (float) (dl.posY - entityPosY); - float lightPosZ = (float) (dl.posZ - entityPosZ); - float lightChunkPosX = (float) (dl.posX - entityChunkOriginX); - float lightChunkPosY = (float) (dl.posY - entityChunkOriginY); - float lightChunkPosZ = (float) (dl.posZ - entityChunkOriginZ); + float lightPosX = (float)(dl.posX - entityPosX); + float lightPosY = (float)(dl.posY - entityPosY); + float lightPosZ = (float)(dl.posZ - entityPosZ); + float lightChunkPosX = (float)(dl.posX - entityChunkOriginX); + float lightChunkPosY = (float)(dl.posY - entityChunkOriginY); + float lightChunkPosZ = (float)(dl.posZ - entityChunkOriginZ); bucketLightSource(lightChunkPosX, lightChunkPosY, lightChunkPosZ, dl); - if (dl.posX > aabb.minX - 0.25 && dl.posY > aabb.minY + eyeHeight - 0.25 && dl.posZ > aabb.minZ - 0.25 - && - dl.posX < aabb.maxX + 0.25 && dl.posY < aabb.minY + eyeHeight + 0.25 - && dl.posZ < aabb.maxZ + 0.25) { + if(dl.posX > aabb.minX - 0.25 && dl.posY > aabb.minY + eyeHeight - 0.25 && dl.posZ > aabb.minZ - 0.25 && + dl.posX < aabb.maxX + 0.25 && dl.posY < aabb.minY + eyeHeight + 0.25 && dl.posZ < aabb.maxZ + 0.25) { tmpMatrix1.setIdentity(); uniformMatrixHelper(shader_lighting_point.uniforms.u_modelViewProjMatrix4f, tmpMatrix1); _wglUniform3f(shader_lighting_point.uniforms.u_lightColor3f, dl.red, dl.green, dl.blue); _wglUniform3f(shader_lighting_point.uniforms.u_lightPosition3f, lightPosX, lightPosY, lightPosZ); DrawUtils.drawStandardQuad3D(); - } else { + }else { float radius = dl.radius; tmpVector1.set(lightPosX, lightPosY, lightPosZ); - if (DeferredStateManager.currentGBufferFrustum.testSphere(tmpVector1, radius)) { + if(DeferredStateManager.currentGBufferFrustum.testSphere(tmpVector1, radius)) { tmpMatrix1.setIdentity(); Matrix4f.translate(tmpVector1, tmpMatrix1, tmpMatrix1); tmpVector1.set(radius, radius, radius); @@ -2183,8 +2079,7 @@ public class EaglerDeferredPipeline { Matrix4f.mul(tmpMatrixViewProj, tmpMatrix1, tmpMatrix1); uniformMatrixHelper(shader_lighting_point.uniforms.u_modelViewProjMatrix4f, tmpMatrix1); _wglUniform3f(shader_lighting_point.uniforms.u_lightColor3f, dl.red, dl.green, dl.blue); - _wglUniform3f(shader_lighting_point.uniforms.u_lightPosition3f, lightPosX, lightPosY, - lightPosZ); + _wglUniform3f(shader_lighting_point.uniforms.u_lightPosition3f, lightPosX, lightPosY, lightPosZ); pointLightMesh.drawMeshVAO(); } } @@ -2201,14 +2096,13 @@ public class EaglerDeferredPipeline { GlStateManager.disableBlend(); - if (reprojectionEngineEnable || config.is_rendering_realisticWater) { + if(reprojectionEngineEnable || config.is_rendering_realisticWater) { // =========== SAVE REPROJECTION DATA FOR NEXT FRAME ============= // _wglBindFramebuffer(_GL_READ_FRAMEBUFFER, lightingHDRFramebuffer); _wglBindFramebuffer(_GL_DRAW_FRAMEBUFFER, lastFrameGBufferFramebuffer); - _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); + _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): SAVE REPROJECTION DATA FOR NEXT FRAME"); } @@ -2217,7 +2111,7 @@ public class EaglerDeferredPipeline { // =================== RENDER SKYBOX MESH =================== // - if (dim == 0) { + if(dim == 0) { GlStateManager.enableDepth(); GlStateManager.setActiveTexture(GL_TEXTURE2); GlStateManager.bindTexture(CloudRenderWorker.cloudOcclusionTexture); @@ -2228,29 +2122,26 @@ public class EaglerDeferredPipeline { shader_skybox_render.useProgram(); uniformMatrixHelper(shader_skybox_render.uniforms.u_viewMatrix4f, DeferredStateManager.viewMatrix); uniformMatrixHelper(shader_skybox_render.uniforms.u_projMatrix4f, DeferredStateManager.projMatrix); - _wglUniform3f(shader_skybox_render.uniforms.u_sunDirection3f, -currentSunAngle.x, -currentSunAngle.y, - -currentSunAngle.z); + _wglUniform3f(shader_skybox_render.uniforms.u_sunDirection3f, -currentSunAngle.x, -currentSunAngle.y, -currentSunAngle.z); float mag = 25.0f; - float[] sunRGB2 = TemperaturesLUT.getColorTemperature((int) sunKelvin - 1000); - _wglUniform3f(shader_skybox_render.uniforms.u_sunColor3f, sunRGB2[0] * mag, sunRGB2[1] * mag, - sunRGB2[2] * mag); + float[] sunRGB2 = TemperaturesLUT.getColorTemperature((int)sunKelvin - 1000); + _wglUniform3f(shader_skybox_render.uniforms.u_sunColor3f, sunRGB2[0] * mag, sunRGB2[1] * mag, sunRGB2[2] * mag); if (mc.theWorld.getLastLightningBolt() > 0) { float f = 0.3f + fff; - _wglUniform4f(shader_skybox_render.uniforms.u_lightningColor4f, 0.02f * f, 0.02f * f, 0.02f * f, - 1.0f - f * 0.25f); - } else { + _wglUniform4f(shader_skybox_render.uniforms.u_lightningColor4f, 0.02f * f, 0.02f * f, 0.02f * f, 1.0f - f * 0.25f); + }else { _wglUniform4f(shader_skybox_render.uniforms.u_lightningColor4f, 0.0f, 0.0f, 0.0f, 1.0f); } skybox.drawFull(); - + DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RENDER SKYBOX MESH"); - } else if (dim == 1) { + }else if(dim == 1) { GlStateManager.enableDepth(); GlStateManager.setActiveTexture(GL_TEXTURE0); mc.getTextureManager().bindTexture(locationEndSkyPng); - if (shader_skybox_render_end == null) { + if(shader_skybox_render_end == null) { shader_skybox_render_end = PipelineShaderSkyboxRenderEnd.compile(); shader_skybox_render_end.loadUniforms(); } @@ -2265,7 +2156,7 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RENDER SKYBOX MESH"); } - if (dim == 0 && fff < 1.0f) { + if(dim == 0 && fff < 1.0f) { // ===================== RENDER MOON ====================== // @@ -2292,22 +2183,22 @@ public class EaglerDeferredPipeline { moonMatrix.m21 = tmpVector4.y; moonMatrix.m22 = tmpVector4.z; Matrix4f.mul(moonMatrix, tmpMatrix2, moonMatrix); - + GlStateManager.bindTexture(moonTextures); shader_moon_render.useProgram(); - + uniformMatrixHelper(shader_moon_render.uniforms.u_modelMatrix4f, moonMatrix); uniformMatrixHelper(shader_moon_render.uniforms.u_viewMatrix4f, DeferredStateManager.viewMatrix); uniformMatrixHelper(shader_moon_render.uniforms.u_projMatrix4f, DeferredStateManager.projMatrix); float fffff = 0.1f + MathHelper.clamp_float((-currentSunAngle.y + 0.1f) * 8.0f, 0.0f, 0.5f); _wglUniform3f(shader_moon_render.uniforms.u_moonColor3f, 1.4f * fffff, 1.2f * fffff, 1.0f * fffff); - - float f = (float) (Minecraft.getMinecraft().theWorld.getWorldTime() - 18000f) / 24000f / 4f * 3.14159f; + + float f = (float)(Minecraft.getMinecraft().theWorld.getWorldTime() - 18000f) / 24000f / 4f * 3.14159f; _wglUniform3f(shader_moon_render.uniforms.u_lightDir3f, MathHelper.sin(f), 0.0f, MathHelper.cos(f)); - + GlStateManager.enableBlend(); GlStateManager.tryBlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ZERO); - + DrawUtils.drawStandardQuad2D(); DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RENDER MOON"); @@ -2318,20 +2209,59 @@ public class EaglerDeferredPipeline { GlStateManager.disableBlend(); } - public void loadLightSourceBucket(int relativeBlockX, int relativeBlockY, int relativeBlockZ) { + public void bindLightSourceBucket(int relativeBlockX, int relativeBlockY, int relativeBlockZ, int uboIndex) { int hw = lightSourceBucketsWidth / 2; int hh = lightSourceBucketsHeight / 2; int bucketX = (relativeBlockX >> 4) + hw; int bucketY = (relativeBlockY >> 4) + hh; int bucketZ = (relativeBlockZ >> 4) + hw; - if (bucketX >= 0 && bucketY >= 0 && bucketZ >= 0 && bucketX < lightSourceBucketsWidth + if(bucketX >= 0 && bucketY >= 0 && bucketZ >= 0 && bucketX < lightSourceBucketsWidth && bucketY < lightSourceBucketsHeight && bucketZ < lightSourceBucketsWidth) { - currentLightSourceBucket = lightSourceBuckets[bucketY * lightSourceBucketsWidth * lightSourceBucketsWidth - + bucketZ * lightSourceBucketsWidth + bucketX]; - } else { + currentLightSourceBucketId = bucketY * lightSourceBucketsWidth * lightSourceBucketsWidth + + bucketZ * lightSourceBucketsWidth + bucketX; + currentLightSourceBucket = lightSourceBuckets[currentLightSourceBucketId]; + int ser = currentLightSourceBucket.getEaglerSerial(); + int max = currentLightSourceBucket.size(); + if(max > 0) { + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); + int offset = currentLightSourceBucketId * lightingBufferSliceLength; + if (lightSourceBucketSerials[currentLightSourceBucketId] != ser + || lightSourceRenderPosSerials[currentLightSourceBucketId] != currentRenderPosSerial) { + lightSourceBucketSerials[currentLightSourceBucketId] = ser; + lightSourceRenderPosSerials[currentLightSourceBucketId] = currentRenderPosSerial; + if(max > MAX_LIGHTS_PER_CHUNK) { + max = MAX_LIGHTS_PER_CHUNK; + } + chunkLightingDataCopyBuffer.clear(); + chunkLightingDataCopyBuffer.putInt(max); + chunkLightingDataCopyBuffer.putInt(0); //padding + chunkLightingDataCopyBuffer.putInt(0); //padding + chunkLightingDataCopyBuffer.putInt(0); //padding + for(int i = 0; i < max; ++i) { + DynamicLightInstance dl = currentLightSourceBucket.get(i); + chunkLightingDataCopyBuffer.putFloat((float)(dl.posX - currentRenderX)); + chunkLightingDataCopyBuffer.putFloat((float)(dl.posY - currentRenderY)); + chunkLightingDataCopyBuffer.putFloat((float)(dl.posZ - currentRenderZ)); + chunkLightingDataCopyBuffer.putInt(0); //padding + chunkLightingDataCopyBuffer.putFloat(dl.red); + chunkLightingDataCopyBuffer.putFloat(dl.green); + chunkLightingDataCopyBuffer.putFloat(dl.blue); + chunkLightingDataCopyBuffer.putInt(0); //padding + } + chunkLightingDataCopyBuffer.flip(); + _wglBufferSubData(_GL_UNIFORM_BUFFER, offset, chunkLightingDataCopyBuffer); + } + EaglercraftGPU.bindUniformBufferRange(uboIndex, buffer_chunkLightingData, offset, LIGHTING_BUFFER_LENGTH); + }else { + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingDataZero); + EaglercraftGPU.bindUniformBufferRange(uboIndex, buffer_chunkLightingDataZero, 0, LIGHTING_BUFFER_LENGTH); + } + }else { + currentLightSourceBucketId = -1; currentLightSourceBucket = null; + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingDataZero); + EaglercraftGPU.bindUniformBufferRange(uboIndex, buffer_chunkLightingDataZero, 0, LIGHTING_BUFFER_LENGTH); } - updateLightSourceUBO(); } public ListSerial getLightSourceBucketRelativeChunkCoords(int cx, int cy, int cz) { @@ -2340,10 +2270,10 @@ public class EaglerDeferredPipeline { cx += hw; cy += hh; cz += hw; - if (cx < 0 || cx >= lightSourceBucketsWidth || cy < 0 || cy >= lightSourceBucketsHeight || cz < 0 + if(cx < 0 || cx >= lightSourceBucketsWidth || cy < 0 || cy >= lightSourceBucketsHeight || cz < 0 || cz >= lightSourceBucketsWidth) { return null; - } else { + }else { return lightSourceBuckets[cy * lightSourceBucketsWidth * lightSourceBucketsWidth + cz * lightSourceBucketsWidth + cx]; } @@ -2351,7 +2281,7 @@ public class EaglerDeferredPipeline { public void addLightSourceToBucket(int cx, int cy, int cz, DynamicLightInstance dl) { ListSerial lst = getLightSourceBucketRelativeChunkCoords(cx, cy, cz); - if (lst != null) { + if(lst != null) { lst.add(dl); } } @@ -2369,53 +2299,52 @@ public class EaglerDeferredPipeline { float lightLocalZ = z - (bucketZ << 4); float radius = dl.radius; boolean outOfBounds = false; - if (lightLocalX - radius < 0.0f) { + if(lightLocalX - radius < 0.0f) { minX -= 1; outOfBounds = true; addLightSourceToBucket(bucketX - 1, bucketY, bucketZ, dl); } - if (lightLocalY - radius < 0.0f) { + if(lightLocalY - radius < 0.0f) { minY -= 1; outOfBounds = true; addLightSourceToBucket(bucketX, bucketY - 1, bucketZ, dl); } - if (lightLocalZ - radius < 0.0f) { + if(lightLocalZ - radius < 0.0f) { minZ -= 1; outOfBounds = true; addLightSourceToBucket(bucketX, bucketY, bucketZ - 1, dl); } - if (lightLocalX + radius >= 16.0f) { + if(lightLocalX + radius >= 16.0f) { maxX += 1; outOfBounds = true; addLightSourceToBucket(bucketX + 1, bucketY, bucketZ, dl); } - if (lightLocalY + radius >= 16.0f) { + if(lightLocalY + radius >= 16.0f) { maxY += 1; outOfBounds = true; addLightSourceToBucket(bucketX, bucketY + 1, bucketZ, dl); } - if (lightLocalZ + radius >= 16.0f) { + if(lightLocalZ + radius >= 16.0f) { maxZ += 1; outOfBounds = true; addLightSourceToBucket(bucketX, bucketY, bucketZ + 1, dl); } - if (!outOfBounds) { + if(!outOfBounds) { return; } radius *= radius; - for (int yy = minY; yy <= maxY; ++yy) { - for (int zz = minZ; zz <= maxZ; ++zz) { - for (int xx = minX; xx <= maxX; ++xx) { - if ((xx == bucketX ? 1 : 0) + (yy == bucketY ? 1 : 0) + (zz == bucketZ ? 1 : 0) > 1) { + for(int yy = minY; yy <= maxY; ++yy) { + for(int zz = minZ; zz <= maxZ; ++zz) { + for(int xx = minX; xx <= maxX; ++xx) { + if((xx == bucketX ? 1 : 0) + (yy == bucketY ? 1 : 0) + (zz == bucketZ ? 1 : 0) > 1) { continue; } List lst = getLightSourceBucketRelativeChunkCoords(xx, yy, zz); - if (lst != null) { + if(lst != null) { int bucketBoundsX = xx << 4; int bucketBoundsY = yy << 4; int bucketBoundsZ = zz << 4; - if (testAabSphere(bucketBoundsX, bucketBoundsY, bucketBoundsZ, bucketBoundsX + 16, - bucketBoundsY + 16, bucketBoundsZ + 16, x, y, z, radius)) { + if(testAabSphere(bucketBoundsX, bucketBoundsY, bucketBoundsZ, bucketBoundsX + 16, bucketBoundsY + 16, bucketBoundsZ + 16, x, y, z, radius)) { lst.add(dl); } } @@ -2425,8 +2354,7 @@ public class EaglerDeferredPipeline { } /** - * source: - * https://github.com/JOML-CI/JOML/blob/main/src/main/java/org/joml/Intersectionf.java + * source: https://github.com/JOML-CI/JOML/blob/main/src/main/java/org/joml/Intersectionf.java */ public static boolean testAabSphere(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, float centerX, float centerY, float centerZ, float radius2) { @@ -2455,77 +2383,22 @@ public class EaglerDeferredPipeline { } private void truncateOverflowingLightBuffers() { - for (int i = 0; i < this.lightSourceBuckets.length; ++i) { + for(int i = 0; i < this.lightSourceBuckets.length; ++i) { List lst = this.lightSourceBuckets[i]; int k = lst.size(); - if (k > MAX_LIGHTS_PER_CHUNK) { + if(k > MAX_LIGHTS_PER_CHUNK) { lst.sort(comparatorLightRadius); - for (int l = MAX_LIGHTS_PER_CHUNK - 1; l >= MAX_LIGHTS_PER_CHUNK; --l) { + for(int l = MAX_LIGHTS_PER_CHUNK - 1; l >= MAX_LIGHTS_PER_CHUNK; --l) { lst.remove(l); } } } } - public void updateLightSourceUBO() { - if (currentLightSourceBucket == null) { - currentBoundLightSourceBucket = null; - if (isChunkLightingEnabled) { - isChunkLightingEnabled = false; - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - chunkLightingDataCopyBuffer.clear(); - chunkLightingDataCopyBuffer.putInt(0); - chunkLightingDataCopyBuffer.flip(); - _wglBufferSubData(_GL_UNIFORM_BUFFER, 0, chunkLightingDataCopyBuffer); - } - } else { - boolean isNew; - if (!isChunkLightingEnabled) { - isChunkLightingEnabled = true; - isNew = true; - } else { - isNew = currentLightSourceBucket != currentBoundLightSourceBucket; - } - currentBoundLightSourceBucket = currentLightSourceBucket; - if (isNew || currentBoundLightSourceBucket.eaglerCheck()) { - populateLightSourceUBOFromBucket(currentBoundLightSourceBucket); - currentBoundLightSourceBucket.eaglerResetCheck(); - } - } - } - private static final Comparator comparatorLightRadius = (l1, l2) -> { return l1.radius < l2.radius ? 1 : -1; }; - private void populateLightSourceUBOFromBucket(List lights) { - int max = lights.size(); - if (max > MAX_LIGHTS_PER_CHUNK) { - max = MAX_LIGHTS_PER_CHUNK; - } - chunkLightingDataCopyBuffer.clear(); - chunkLightingDataCopyBuffer.putInt(max); - if (max > 0) { - chunkLightingDataCopyBuffer.putInt(0); // padding - chunkLightingDataCopyBuffer.putInt(0); // padding - chunkLightingDataCopyBuffer.putInt(0); // padding - for (int i = 0; i < max; ++i) { - DynamicLightInstance dl = lights.get(i); - chunkLightingDataCopyBuffer.putFloat((float) (dl.posX - currentRenderX)); - chunkLightingDataCopyBuffer.putFloat((float) (dl.posY - currentRenderY)); - chunkLightingDataCopyBuffer.putFloat((float) (dl.posZ - currentRenderZ)); - chunkLightingDataCopyBuffer.putInt(0); // padding - chunkLightingDataCopyBuffer.putFloat(dl.red); - chunkLightingDataCopyBuffer.putFloat(dl.green); - chunkLightingDataCopyBuffer.putFloat(dl.blue); - chunkLightingDataCopyBuffer.putInt(0); // padding - } - } - chunkLightingDataCopyBuffer.flip(); - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - _wglBufferSubData(_GL_UNIFORM_BUFFER, 0, chunkLightingDataCopyBuffer); - } - public void beginDrawEnvMap() { DeferredStateManager.checkGLError("Pre: beginDrawEnvMap()"); GlStateManager.enableDepth(); @@ -2537,10 +2410,9 @@ public class EaglerDeferredPipeline { updateForwardRenderWorldLightingData(); EaglercraftGPU.bindGLUniformBuffer(buffer_worldLightingData); EaglercraftGPU.bindUniformBufferRange(0, buffer_worldLightingData, 0, worldLightingDataCopyBuffer.remaining()); - if (config.is_rendering_dynamicLights) { + if(config.is_rendering_dynamicLights) { EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - EaglercraftGPU.bindUniformBufferRange(1, buffer_chunkLightingData, 0, - chunkLightingDataCopyBuffer.capacity()); + EaglercraftGPU.bindUniformBufferRange(1, buffer_chunkLightingData, 0, chunkLightingDataCopyBuffer.capacity()); } GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.pushMatrix(); @@ -2592,9 +2464,9 @@ public class EaglerDeferredPipeline { private void bindEnvMapBlockTexture() { DeferredStateManager.checkGLError("Pre: bindEnvMapBlockTexture()"); GlStateManager.setActiveTexture(GL_TEXTURE4); - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { GlStateManager.bindTexture(sunShadowDepthBuffer); - } else { + }else { GlStateManager.bindTexture(-1); } TextureManager mgr = mc.getTextureManager(); @@ -2645,11 +2517,11 @@ public class EaglerDeferredPipeline { worldLightingDataCopyBuffer.putFloat(-DeferredStateManager.currentSunLightAngle.z); worldLightingDataCopyBuffer.putFloat(-DeferredStateManager.currentSunAngle.y); float f = getSkyBrightnessParam(); - if (DeferredStateManager.currentSunAngle.y > 0.05f) { // moon: + if(DeferredStateManager.currentSunAngle.y > 0.05f) { // moon: worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.x * 0.025f * f); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.y * 0.025f * f); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.z * 0.025f * f); - } else { + }else { worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.x * f); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.y * f); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.z * f); @@ -2657,7 +2529,7 @@ public class EaglerDeferredPipeline { float lightningBoost = mc.theWorld.getLastLightningBolt() > 0 ? 1.0f : 0.0f; lightningBoost *= 0.3f + mc.theWorld.getRainStrength(partialTicks); worldLightingDataCopyBuffer.putFloat(getSkyBrightnessTimeParam() + lightningBoost); - worldLightingDataCopyBuffer.putFloat((float) DeferredStateManager.fogLinearExp); + worldLightingDataCopyBuffer.putFloat((float)DeferredStateManager.fogLinearExp); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.fogDensity); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.fogNear); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.fogFar); @@ -2669,8 +2541,7 @@ public class EaglerDeferredPipeline { worldLightingDataCopyBuffer.putFloat(DeferredStateManager.fogColorLightG); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.fogColorLightB); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.fogColorLightA); - float mul = 0.05f * MathHelper.clamp_float(-1.0f - DeferredStateManager.getSunHeight() * 20.0f, 0.0f, 1.0f) - + 0.01f; + float mul = 0.05f * MathHelper.clamp_float(-1.0f - DeferredStateManager.getSunHeight() * 20.0f, 0.0f, 1.0f) + 0.01f; worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.x * mul); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.y * mul); worldLightingDataCopyBuffer.putFloat(DeferredStateManager.currentSunLightColor.z * mul); @@ -2679,11 +2550,11 @@ public class EaglerDeferredPipeline { worldLightingDataCopyBuffer.putFloat(1.0f); worldLightingDataCopyBuffer.putFloat(1.0f); worldLightingDataCopyBuffer.putFloat(1.0f); - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { tmpShadowLOD0MatrixTexSpace.store(worldLightingDataCopyBuffer); - if (config.is_rendering_shadowsSun_clamped > 1) { + if(config.is_rendering_shadowsSun_clamped > 1) { tmpShadowLOD1MatrixTexSpace.store(worldLightingDataCopyBuffer); - if (config.is_rendering_shadowsSun_clamped > 2) { + if(config.is_rendering_shadowsSun_clamped > 2) { tmpShadowLOD2MatrixTexSpace.store(worldLightingDataCopyBuffer); } } @@ -2711,16 +2582,14 @@ public class EaglerDeferredPipeline { } private float getSkyBrightnessTimeParam() { - return (2.0f + MathHelper.clamp_float(-DeferredStateManager.currentSunAngle.y * 8.0f, 0.0f, 1.5f)) - * getSkyBrightnessParam(); + return (2.0f + MathHelper.clamp_float(-DeferredStateManager.currentSunAngle.y * 8.0f, 0.0f, 1.5f)) * getSkyBrightnessParam(); } public void beginDrawRealisticWaterMask() { DeferredStateManager.checkGLError("Pre: beginDrawRealisticWaterMask()"); _wglBindFramebuffer(_GL_READ_FRAMEBUFFER, gBufferFramebuffer); _wglBindFramebuffer(_GL_DRAW_FRAMEBUFFER, realisticWaterMaskFramebuffer); - _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, - GL_NEAREST); + _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterMaskFramebuffer); GlStateManager.viewport(0, 0, currentWidth, currentHeight); GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -2740,7 +2609,7 @@ public class EaglerDeferredPipeline { DeferredStateManager.disableDrawRealisticWaterMask(); GlStateManager.disableExtensionPipeline(); - if (config.is_rendering_lightShafts) { + if(config.is_rendering_lightShafts) { // ================== RENDER LIGHT SHAFTS =================== // @@ -2753,26 +2622,18 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(realisticWaterDepthBuffer); shader_light_shafts_sample.useProgram(); - _wglUniform2f(shader_light_shafts_sample.uniforms.u_ditherScale2f, reprojectionTexWidth * 0.125f, - reprojectionTexHeight * 0.125f); - uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_inverseViewProjMatrix4f, - tmpMatrixInverseViewProj); - _wglUniform3f(shader_light_shafts_sample.uniforms.u_eyePosition3f, - DeferredStateManager.inverseViewMatrix.m30, + _wglUniform2f(shader_light_shafts_sample.uniforms.u_ditherScale2f, reprojectionTexWidth * 0.125f, reprojectionTexHeight * 0.125f); + uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_inverseViewProjMatrix4f, tmpMatrixInverseViewProj); + _wglUniform3f(shader_light_shafts_sample.uniforms.u_eyePosition3f, DeferredStateManager.inverseViewMatrix.m30, DeferredStateManager.inverseViewMatrix.m31, DeferredStateManager.inverseViewMatrix.m32); Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix0, tmpShadowLOD0MatrixTexSpace); - uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_sunShadowMatrixLOD04f, - tmpShadowLOD0MatrixTexSpace); - if (config.is_rendering_shadowsSun_clamped > 1) { - Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix1, - tmpShadowLOD1MatrixTexSpace); - uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_sunShadowMatrixLOD14f, - tmpShadowLOD1MatrixTexSpace); - if (config.is_rendering_shadowsSun_clamped > 2) { - Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix2, - tmpShadowLOD2MatrixTexSpace); - uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_sunShadowMatrixLOD24f, - tmpShadowLOD2MatrixTexSpace); + uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_sunShadowMatrixLOD04f, tmpShadowLOD0MatrixTexSpace); + if(config.is_rendering_shadowsSun_clamped > 1) { + Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix1, tmpShadowLOD1MatrixTexSpace); + uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_sunShadowMatrixLOD14f, tmpShadowLOD1MatrixTexSpace); + if(config.is_rendering_shadowsSun_clamped > 2) { + Matrix4f.mul(tmpClipToTexSpaceMatLeft, DeferredStateManager.sunShadowMatrix2, tmpShadowLOD2MatrixTexSpace); + uniformMatrixHelper(shader_light_shafts_sample.uniforms.u_sunShadowMatrixLOD24f, tmpShadowLOD2MatrixTexSpace); } } @@ -2836,7 +2697,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); shader_realistic_water_control.useProgram(); - if (!reprojectionEngineEnable) { + if(!reprojectionEngineEnable) { tmpVector1.set(-reprojectionViewerOffsetX, -reprojectionViewerOffsetY, -reprojectionViewerOffsetZ); tmpMatrix1.setIdentity(); Matrix4f.translate(tmpVector1, tmpMatrix1, tmpMatrix1); @@ -2845,15 +2706,11 @@ public class EaglerDeferredPipeline { Matrix4f.invert(tmpMatrixViewProjReproject, tmpMatrixInverseViewProjReproject); } - uniformMatrixHelper(shader_realistic_water_control.uniforms.u_inverseViewProjMatrix4f, - tmpMatrixInverseViewProjReproject); - uniformMatrixHelper(shader_realistic_water_control.uniforms.u_reprojectionMatrix4f, - tmpMatrixLastFrameViewProjReproject); + uniformMatrixHelper(shader_realistic_water_control.uniforms.u_inverseViewProjMatrix4f, tmpMatrixInverseViewProjReproject); + uniformMatrixHelper(shader_realistic_water_control.uniforms.u_reprojectionMatrix4f, tmpMatrixLastFrameViewProjReproject); - uniformMatrixHelper(shader_realistic_water_control.uniforms.u_projectionMatrix4f, - DeferredStateManager.projMatrix); - uniformMatrixHelper(shader_realistic_water_control.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_realistic_water_control.uniforms.u_projectionMatrix4f, DeferredStateManager.projMatrix); + uniformMatrixHelper(shader_realistic_water_control.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); Matrix4f.invert(tmpMatrixLastFrameProj, tmpMatrix1); uniformMatrixHelper(shader_realistic_water_control.uniforms.u_lastInverseProjMatrix4f, tmpMatrix1); Matrix4f.invert(tmpMatrixLastFrameViewReproject, tmpMatrix1); @@ -2874,8 +2731,7 @@ public class EaglerDeferredPipeline { float fac = MathHelper.clamp_float(DeferredStateManager.currentSunAngle.y * -4.0f, 0.1f, 1.0f); _wglUniform4f(shader_realistic_water_control.uniforms.u_refractFogColor4f, fr * ff, fg * ff, fb * ff, fac); - uniformMatrixHelper(shader_realistic_water_control.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_realistic_water_control.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); DrawUtils.drawStandardQuad2D(); @@ -2899,8 +2755,7 @@ public class EaglerDeferredPipeline { _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[1]); shader_reproject_ssr.useProgram(); - uniformMatrixHelper(shader_reproject_ssr.uniforms.u_inverseProjectionMatrix4f, - DeferredStateManager.inverseProjMatrix); + uniformMatrixHelper(shader_reproject_ssr.uniforms.u_inverseProjectionMatrix4f, DeferredStateManager.inverseProjMatrix); Matrix4f.mul(tmpMatrixLastFrameViewProjReproject, tmpMatrixInverseViewProjReproject, tmpMatrix1); Matrix4f.mul(tmpMatrix1, DeferredStateManager.projMatrix, tmpMatrix1); uniformMatrixHelper(shader_reproject_ssr.uniforms.u_lastProjectionMatrix4f, tmpMatrix1); @@ -2963,7 +2818,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(realisticWaterNoiseMap); shader_realistic_water_noise.useProgram(); - float waveTimer = (float) ((System.currentTimeMillis() % 600000l) * 0.001); + float waveTimer = (float)((EagRuntime.steadyTimeMillis() % 600000l) * 0.001); _wglUniform4f(shader_realistic_water_noise.uniforms.u_waveTimer4f, waveTimer, 0.0f, 0.0f, 0.0f); DrawUtils.drawStandardQuad2D(); @@ -2981,16 +2836,15 @@ public class EaglerDeferredPipeline { public void applyGBufferFog() { DeferredStateManager.checkGLError("Pre: applyGBufferFog()"); - if (DeferredStateManager.fogLinearExp == 0) { + if(DeferredStateManager.fogLinearExp == 0) { _wglBindFramebuffer(_GL_FRAMEBUFFER, lightingHDRFramebuffer); return; } _wglBindFramebuffer(_GL_READ_FRAMEBUFFER, lightingHDRFramebuffer); _wglBindFramebuffer(_GL_DRAW_FRAMEBUFFER, fogDepthCopyBuffer); - _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, - GL_NEAREST); + _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); _wglBindFramebuffer(_GL_FRAMEBUFFER, lightingHDRFramebuffer); - if (config.is_rendering_lightShafts) { + if(config.is_rendering_lightShafts) { GlStateManager.setActiveTexture(GL_TEXTURE4); GlStateManager.bindTexture(lightShaftsTexture); } @@ -3006,31 +2860,26 @@ public class EaglerDeferredPipeline { tmpMatrix1.m30 = tmpMatrix1.m31 = tmpMatrix1.m32 = 0.0f; Matrix4f.mul(tmpMatrix1, DeferredStateManager.inverseProjMatrix, tmpMatrix1); PipelineShaderGBufferFog fogShader; - switch (DeferredStateManager.fogLinearExp) { - case 1: - fogShader = shader_colored_fog_linear; - fogShader.useProgram(); - _wglUniform2f(fogShader.uniforms.u_linearFogParam2f, DeferredStateManager.fogNear, - DeferredStateManager.fogFar); - break; - case 2: - fogShader = shader_colored_fog_exp; - fogShader.useProgram(); - _wglUniform1f(fogShader.uniforms.u_expFogDensity1f, DeferredStateManager.fogDensity); - break; - case 6: - fogShader = shader_atmosphere_fog; - fogShader.useProgram(); - _wglUniform1f(fogShader.uniforms.u_expFogDensity1f, DeferredStateManager.fogDensity); - float mul = 0.05f - * MathHelper.clamp_float(-1.0f - DeferredStateManager.getSunHeight() * 20.0f, 0.0f, 1.0f) - + 0.01f; - _wglUniform3f(fogShader.uniforms.u_sunColorAdd3f, DeferredStateManager.currentSunLightColor.x * mul, - DeferredStateManager.currentSunLightColor.y * mul, - DeferredStateManager.currentSunLightColor.z * mul); - break; - default: - throw new RuntimeException("Invalid fog type: " + DeferredStateManager.fogLinearExp); + switch(DeferredStateManager.fogLinearExp) { + case 1: + fogShader = shader_colored_fog_linear; + fogShader.useProgram(); + _wglUniform2f(fogShader.uniforms.u_linearFogParam2f, DeferredStateManager.fogNear, DeferredStateManager.fogFar); + break; + case 2: + fogShader = shader_colored_fog_exp; + fogShader.useProgram(); + _wglUniform1f(fogShader.uniforms.u_expFogDensity1f, DeferredStateManager.fogDensity); + break; + case 6: + fogShader = shader_atmosphere_fog; + fogShader.useProgram(); + _wglUniform1f(fogShader.uniforms.u_expFogDensity1f, DeferredStateManager.fogDensity); + float mul = 0.05f * MathHelper.clamp_float(-1.0f - DeferredStateManager.getSunHeight() * 20.0f, 0.0f, 1.0f) + 0.01f; + _wglUniform3f(fogShader.uniforms.u_sunColorAdd3f, DeferredStateManager.currentSunLightColor.x * mul, DeferredStateManager.currentSunLightColor.y * mul, DeferredStateManager.currentSunLightColor.z * mul); + break; + default: + throw new RuntimeException("Invalid fog type: " + DeferredStateManager.fogLinearExp); } uniformMatrixHelper(fogShader.uniforms.u_inverseViewProjMatrix4f, tmpMatrix1); _wglUniform4f(fogShader.uniforms.u_fogColorLight4f, DeferredStateManager.fogColorLightR, @@ -3062,21 +2911,20 @@ public class EaglerDeferredPipeline { updateForwardRenderWorldLightingData(); EaglercraftGPU.bindGLUniformBuffer(buffer_worldLightingData); EaglercraftGPU.bindUniformBufferRange(0, buffer_worldLightingData, 0, worldLightingDataCopyBuffer.remaining()); - if (config.is_rendering_dynamicLights) { + if(config.is_rendering_dynamicLights) { EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - EaglercraftGPU.bindUniformBufferRange(1, buffer_chunkLightingData, 0, - chunkLightingDataCopyBuffer.capacity()); + EaglercraftGPU.bindUniformBufferRange(1, buffer_chunkLightingData, 0, chunkLightingDataCopyBuffer.capacity()); } _wglBindFramebuffer(_GL_FRAMEBUFFER, lightingHDRFramebuffer); GlStateManager.viewport(0, 0, currentWidth, currentHeight); DeferredStateManager.setPassMatrixToGBuffer(); GlStateManager.setActiveTexture(GL_TEXTURE10); GlStateManager.bindTexture(skyIrradianceTexture); - if (config.is_rendering_lightShafts) { + if(config.is_rendering_lightShafts) { GlStateManager.setActiveTexture(GL_TEXTURE11); GlStateManager.bindTexture(lightShaftsTexture); } - if (config.is_rendering_useEnvMap) { + if(config.is_rendering_useEnvMap) { GlStateManager.setActiveTexture(GL_TEXTURE5); GlStateManager.bindTexture(envMapColorTexture); } @@ -3098,12 +2946,12 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE5); GlStateManager.bindTexture(envMapSkyTexture); GlStateManager.setActiveTexture(GL_TEXTURE4); - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { GlStateManager.bindTexture(sunShadowDepthBuffer); - if (config.is_rendering_shadowsSmoothed) { + if(config.is_rendering_shadowsSmoothed) { setLinear(); } - } else { + }else { GlStateManager.bindTexture(-1); } TextureManager mgr = mc.getTextureManager(); @@ -3118,7 +2966,7 @@ public class EaglerDeferredPipeline { public void endDrawRealisticWaterSurface() { DeferredStateManager.checkGLError("Pre: endDrawRealisticWaterSurface()"); DeferredStateManager.disableDrawRealisticWaterRender(); - if (config.is_rendering_useEnvMap) { + if(config.is_rendering_useEnvMap) { GlStateManager.setActiveTexture(GL_TEXTURE5); GlStateManager.bindTexture(envMapColorTexture); GlStateManager.setActiveTexture(GL_TEXTURE0); @@ -3152,12 +3000,12 @@ public class EaglerDeferredPipeline { public void beginDrawTranslucentEntities() { DeferredStateManager.checkGLError("Pre: beginDrawTranslucentEntities()"); GlStateManager.setActiveTexture(GL_TEXTURE4); - if (config.is_rendering_shadowsSun_clamped > 0) { + if(config.is_rendering_shadowsSun_clamped > 0) { GlStateManager.bindTexture(sunShadowDepthBuffer); - if (config.is_rendering_shadowsSmoothed) { + if(config.is_rendering_shadowsSmoothed) { setLinear(); } - } else { + }else { GlStateManager.bindTexture(-1); } TextureManager mgr = mc.getTextureManager(); @@ -3171,7 +3019,7 @@ public class EaglerDeferredPipeline { public void saveReprojData() { DeferredStateManager.checkGLError("Pre: saveReprojData()"); - if (reprojectionEngineEnable || config.is_rendering_realisticWater) { + if(reprojectionEngineEnable || config.is_rendering_realisticWater) { // =========== SAVE REPROJECTION DATA FOR NEXT FRAME ============= // @@ -3185,16 +3033,14 @@ public class EaglerDeferredPipeline { GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); - TextureCopyUtil.alignPixelsTopLeft(reprojectionTexWidth << 1, reprojectionTexHeight << 1, - reprojectionTexWidth, reprojectionTexHeight); + TextureCopyUtil.alignPixelsTopLeft(reprojectionTexWidth << 1, reprojectionTexHeight << 1, reprojectionTexWidth, reprojectionTexHeight); TextureCopyUtil.blitTexture(); GlStateManager.bindTexture(lightingHDRFramebufferDepthTexture); GlStateManager.enableDepth(); GlStateManager.depthFunc(GL_ALWAYS); GlStateManager.depthMask(true); _wglDrawBuffers(GL_NONE); - TextureCopyUtil.alignPixelsTopLeft(reprojectionTexWidth << 1, reprojectionTexHeight << 1, - reprojectionTexWidth, reprojectionTexHeight); + TextureCopyUtil.alignPixelsTopLeft(reprojectionTexWidth << 1, reprojectionTexHeight << 1, reprojectionTexWidth, reprojectionTexHeight); TextureCopyUtil.blitTextureDepth(); GlStateManager.disableDepth(); GlStateManager.depthMask(false); @@ -3245,7 +3091,7 @@ public class EaglerDeferredPipeline { GlStateManager.disableAlpha(); GlStateManager.disableBlend(); GlStateManager.disableExtensionPipeline(); - if (config.is_rendering_shadowsSun_clamped > 0 && config.is_rendering_shadowsSmoothed) { + if(config.is_rendering_shadowsSun_clamped > 0 && config.is_rendering_shadowsSmoothed) { GlStateManager.bindTexture(sunShadowDepthBuffer); setNearest(); } @@ -3255,7 +3101,7 @@ public class EaglerDeferredPipeline { public void endDrawDeferred() { DeferredStateManager.checkGLError("Pre: endDrawDeferred()"); - if (config.is_rendering_lensFlares && mc.theWorld.provider.getDimensionId() == 0 && + if(config.is_rendering_lensFlares && mc.theWorld.provider.getDimensionId() == 0 && DeferredStateManager.currentSunAngle.y < 0.2f && mc.theWorld.getRainStrength(partialTicks) < 1.0f) { // =============== CALCULATE SUN COORDINATES ================ // @@ -3264,9 +3110,9 @@ public class EaglerDeferredPipeline { tmpVector2.y = DeferredStateManager.currentSunAngle.y * 10.0f; tmpVector2.z = DeferredStateManager.currentSunAngle.z * 10.0f; tmpVector2.w = 1.0f; - + Matrix4f.transform(tmpMatrixViewProj, tmpVector2, tmpVector2); - + tmpVector2.z /= tmpVector2.w; float margin = 0.2f; if (tmpVector2.z <= -1.0f) { @@ -3281,7 +3127,7 @@ public class EaglerDeferredPipeline { && tmpVector2.y > -1.0f - margin) { // ============ CALCULATE DEPTH SUN OCCLUSION ============ // - + _wglBindFramebuffer(_GL_FRAMEBUFFER, sunOcclusionValueFramebuffer); GlStateManager.viewport(0, 0, 1, 1); @@ -3292,14 +3138,14 @@ public class EaglerDeferredPipeline { float fov = 90.0f / mc.entityRenderer.getFOVModifier(partialTicks, true); float radius = 0.05f * fov; - float aspectRatio = (float) currentHeight / (float) currentWidth; - + float aspectRatio = (float)currentHeight / (float)currentWidth; + tmpMatrix3.setIdentity(); tmpMatrix3.m00 = aspectRatio * radius; tmpMatrix3.m11 = radius; tmpMatrix3.m20 = tmpVector2.x * 0.5f + 0.5f; tmpMatrix3.m21 = tmpVector2.y * 0.5f + 0.5f; - + shader_lens_sun_occlusion.useProgram(); uniformMatrixHelper(shader_lens_sun_occlusion.uniforms.u_sampleMatrix3f, tmpMatrix3); @@ -3322,37 +3168,35 @@ public class EaglerDeferredPipeline { // ================ DOWNSCALE AND AVERAGE LUMA =============== // - long millis = System.currentTimeMillis(); - if (millis - lastExposureUpdate > 33l) { - if (lumaAvgDownscaleFramebuffers.length == 0) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastExposureUpdate > 33l) { + if(lumaAvgDownscaleFramebuffers.length == 0) { _wglBindFramebuffer(_GL_FRAMEBUFFER, exposureBlendFramebuffer); GlStateManager.clearColor(1.0f, 1.0f, 1.0f, 1.0f); GlStateManager.clear(GL_COLOR_BUFFER_BIT); - } else { + }else { GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); setLinear(); - + int iw = currentWidth; int ih = currentHeight; int iw2 = 0, ih2 = 0, iw3 = 0, ih3 = 0; - for (int i = 0; i < lumaAvgDownscaleFramebuffers.length; ++i) { + for(int i = 0; i < lumaAvgDownscaleFramebuffers.length; ++i) { iw2 = iw >> 2; ih2 = ih >> 2; // cheap way to round up: iw3 = ((iw & 3) != 0) ? (iw2 + 1) : iw2; ih3 = ((ih & 3) != 0) ? (ih2 + 1) : ih2; _wglBindFramebuffer(_GL_FRAMEBUFFER, lumaAvgDownscaleFramebuffers[i]); - - if (i == 0) { + + if(i == 0) { shader_post_exposure_avg_luma.useProgram(); - _wglUniform4f(shader_post_exposure_avg_luma.uniforms.u_sampleOffset4f, 1.0f / iw3, 1.0f / ih3, - 4.0f / iw, 4.0f / ih); - } else { + _wglUniform4f(shader_post_exposure_avg_luma.uniforms.u_sampleOffset4f, 1.0f / iw3, 1.0f / ih3, 4.0f / iw, 4.0f / ih); + }else { shader_post_exposure_avg.useProgram(); GlStateManager.bindTexture(lumaAvgDownscaleTexture[i - 1]); - _wglUniform4f(shader_post_exposure_avg.uniforms.u_sampleOffset4f, 1.0f / iw3, 1.0f / ih3, - 4.0f / iw, 4.0f / ih); + _wglUniform4f(shader_post_exposure_avg.uniforms.u_sampleOffset4f, 1.0f / iw3, 1.0f / ih3, 4.0f / iw, 4.0f / ih); } GlStateManager.viewport(0, 0, iw3, ih3); @@ -3372,8 +3216,7 @@ public class EaglerDeferredPipeline { GlStateManager.enableBlend(); GlStateManager.blendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); - GlStateManager.setBlendConstants(0.0f, 0.0f, 0.0f, - Math.min((float) ((millis - lastExposureUpdate) * 0.001), 1.0f)); + GlStateManager.setBlendConstants(0.0f, 0.0f, 0.0f, Math.min((float)((millis - lastExposureUpdate) * 0.001), 1.0f)); GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(lumaAvgDownscaleTexture[lumaAvgDownscaleTexture.length - 1]); @@ -3391,7 +3234,7 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("endDrawDeferred(): DOWNSCALE AND AVERAGE LUMA"); } - if (config.is_rendering_bloom) { + if(config.is_rendering_bloom) { // ==================== BLOOM: BRIGHT PASS ==================== // @@ -3402,21 +3245,20 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(lightingHDRFramebufferDepthTexture); GlStateManager.setActiveTexture(GL_TEXTURE2); GlStateManager.bindTexture(gBufferMaterialTexture); - if (flag) { + if(flag) { setLinear(); } GlStateManager.setActiveTexture(GL_TEXTURE1); GlStateManager.bindTexture(exposureBlendTexture); GlStateManager.setActiveTexture(GL_TEXTURE0); GlStateManager.bindTexture(lightingHDRFramebufferColorTexture); - if (flag) { + if(flag) { setLinear(); } shader_post_bloom_bright.useProgram(); - _wglUniform4f(shader_post_bloom_bright.uniforms.u_outputSize4f, bloomBrightPassTextureW, - bloomBrightPassTextureH, (flag ? 2.0f : 1.0f) / currentWidth, (flag ? 2.0f : 1.0f) / currentHeight); + _wglUniform4f(shader_post_bloom_bright.uniforms.u_outputSize4f, bloomBrightPassTextureW, bloomBrightPassTextureH, (flag ? 2.0f : 1.0f) / currentWidth, (flag ? 2.0f : 1.0f) / currentHeight); DrawUtils.drawStandardQuad2D(); - if (flag) { + if(flag) { setNearest(); GlStateManager.setActiveTexture(GL_TEXTURE2); setNearest(); @@ -3430,7 +3272,7 @@ public class EaglerDeferredPipeline { int bloomStageW = bloomBrightPassTextureW; int bloomStageH = bloomBrightPassTextureH; int texx = bloomBrightPassTexture; - if (bloomStageW > 300 && bloomStageH > 170) { + if(bloomStageW > 300 && bloomStageH > 170) { bloomStageW >>= 1; bloomStageH >>= 1; _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomDownscaleAFramebuffer); @@ -3442,10 +3284,10 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("endDrawDeferred(): BLOOM: DOWNSCALE A"); - if (bloomStageW > 300 && bloomStageH > 170) { - + if(bloomStageW > 300 && bloomStageH > 170) { + // ==================== BLOOM: DOWNSCALE B ==================== // - + bloomStageW >>= 1; bloomStageH >>= 1; _wglBindFramebuffer(_GL_FRAMEBUFFER, bloomDownscaleBFramebuffer); @@ -3467,8 +3309,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(texx); shader_post_bloom_blur.useProgram(); _wglUniform2f(shader_post_bloom_blur.uniforms.u_sampleOffset2f, (flag ? 2.0f : 1.0f) / bloomStageW, 0.0f); - _wglUniform4f(shader_post_bloom_blur.uniforms.u_outputSize4f, bloomBlurTextureW, bloomBlurTextureH, - (flag ? 2.0f : 1.0f) / bloomStageW, (flag ? 2.0f : 1.0f) / bloomStageH); + _wglUniform4f(shader_post_bloom_blur.uniforms.u_outputSize4f, bloomBlurTextureW, bloomBlurTextureH, (flag ? 2.0f : 1.0f) / bloomStageW, (flag ? 2.0f : 1.0f) / bloomStageH); DrawUtils.drawStandardQuad2D(); DeferredStateManager.checkGLError("endDrawDeferred(): BLOOM: HORZ BLUR"); @@ -3479,8 +3320,7 @@ public class EaglerDeferredPipeline { GlStateManager.bindTexture(bloomHBlurTexture); shader_post_bloom_blur.useProgram(); _wglUniform2f(shader_post_bloom_blur.uniforms.u_sampleOffset2f, 0.0f, 1.0f / bloomBlurTextureH); - _wglUniform4f(shader_post_bloom_blur.uniforms.u_outputSize4f, bloomBlurTextureW, bloomBlurTextureH, - 1.0f / bloomBlurTextureW, 1.0f / bloomBlurTextureH); + _wglUniform4f(shader_post_bloom_blur.uniforms.u_outputSize4f, bloomBlurTextureW, bloomBlurTextureH, 1.0f / bloomBlurTextureW, 1.0f / bloomBlurTextureH); DrawUtils.drawStandardQuad2D(); DeferredStateManager.checkGLError("endDrawDeferred(): BLOOM: VERT BLUR"); @@ -3503,12 +3343,12 @@ public class EaglerDeferredPipeline { float exposure = 1.0f; - if (config.is_rendering_fxaa) { + if(config.is_rendering_fxaa) { _wglBindFramebuffer(_GL_FRAMEBUFFER, tonemapOutputFramebuffer); - } else { - if (config.is_rendering_lensDistortion) { + }else { + if(config.is_rendering_lensDistortion) { _wglBindFramebuffer(_GL_FRAMEBUFFER, lensDistortFramebuffer); - } else { + }else { _wglBindFramebuffer(_GL_FRAMEBUFFER, null); } } @@ -3530,13 +3370,13 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("endDrawDeferred(): APPLY TONEMAPPING"); - if (config.is_rendering_fxaa) { - + if(config.is_rendering_fxaa) { + // ======================= APPLY FXAA ======================== // - if (config.is_rendering_lensDistortion) { + if(config.is_rendering_lensDistortion) { _wglBindFramebuffer(_GL_FRAMEBUFFER, lensDistortFramebuffer); - } else { + }else { _wglBindFramebuffer(_GL_FRAMEBUFFER, null); } shader_post_fxaa.useProgram(); @@ -3546,9 +3386,9 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("endDrawDeferred(): APPLY FXAA"); } - - if (config.is_rendering_lensDistortion) { - + + if(config.is_rendering_lensDistortion) { + // ================= APPLY LENS DISTORTION ================== // _wglBindFramebuffer(_GL_FRAMEBUFFER, null); @@ -3559,10 +3399,10 @@ public class EaglerDeferredPipeline { DeferredStateManager.checkGLError("endDrawDeferred(): APPLY LENS DISTORTION"); } - + // =========== BLIT WORLD DEPTH BUFFER TO OUTPUT ============= // - if (EagRuntime.getPlatformType() == EnumPlatformType.DESKTOP) { + if(EagRuntime.getPlatformType() == EnumPlatformType.DESKTOP) { _wglBindFramebuffer(_GL_FRAMEBUFFER, null); GlStateManager.enableDepth(); GlStateManager.depthFunc(GL_ALWAYS); @@ -3572,11 +3412,10 @@ public class EaglerDeferredPipeline { GlStateManager.disableDepth(); GlStateManager.depthFunc(GL_LEQUAL); GlStateManager.depthMask(false); - } else { + }else { _wglBindFramebuffer(_GL_READ_FRAMEBUFFER, lightingHDRFramebuffer); _wglBindFramebuffer(_GL_DRAW_FRAMEBUFFER, null); - _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); + _wglBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); } DeferredStateManager.checkGLError("endDrawDeferred(): BLIT WORLD DEPTH BUFFER TO OUTPUT"); @@ -3586,7 +3425,7 @@ public class EaglerDeferredPipeline { _wglBindFramebuffer(_GL_FRAMEBUFFER, null); drawDebugViewIfEnabled(); - for (int i = 0; i < lightSourceBuckets.length; ++i) { + for(int i = 0; i < lightSourceBuckets.length; ++i) { lightSourceBuckets[i].clear(); } @@ -3623,7 +3462,7 @@ public class EaglerDeferredPipeline { public PipelineShaderGBufferDebugView useDebugViewShader(int idx) { PipelineShaderGBufferDebugView dbgShader = shader_gbuffer_debug_view[idx]; - if (dbgShader == null) { + if(dbgShader == null) { shader_gbuffer_debug_view[idx] = dbgShader = PipelineShaderGBufferDebugView.compile(idx); dbgShader.loadUniforms(); } @@ -3632,501 +3471,508 @@ public class EaglerDeferredPipeline { } private void drawDebugViewIfEnabled() { - if (DebugFramebufferView.debugViewShown) { + if(DebugFramebufferView.debugViewShown) { DebugFramebufferView.renderDebugView(); } } public void destroy() { DeferredStateManager.checkGLError("Pre: destroy()"); - if (gBufferFramebuffer != null) { + if(gBufferFramebuffer != null) { _wglDeleteFramebuffer(gBufferFramebuffer); gBufferFramebuffer = null; } - if (gBufferDiffuseTexture != -1) { + if(gBufferDiffuseTexture != -1) { GlStateManager.bindTexture(gBufferDiffuseTexture); gBufferDiffuseTexture = -1; } - if (gBufferNormalsTexture != -1) { + if(gBufferNormalsTexture != -1) { GlStateManager.bindTexture(gBufferNormalsTexture); gBufferNormalsTexture = -1; } - if (gBufferMaterialTexture != -1) { + if(gBufferMaterialTexture != -1) { GlStateManager.bindTexture(gBufferMaterialTexture); gBufferMaterialTexture = -1; } - if (gBufferDepthTexture != -1) { + if(gBufferDepthTexture != -1) { GlStateManager.bindTexture(gBufferDepthTexture); gBufferDepthTexture = -1; } - if (sunShadowFramebuffer != null) { + if(sunShadowFramebuffer != null) { _wglDeleteFramebuffer(sunShadowFramebuffer); sunShadowFramebuffer = null; } - if (sunShadowDepthBuffer != -1) { + if(sunShadowDepthBuffer != -1) { GlStateManager.deleteTexture(sunShadowDepthBuffer); sunShadowDepthBuffer = -1; } - if (sunLightingShadowFramebuffer != null) { + if(sunLightingShadowFramebuffer != null) { _wglDeleteFramebuffer(sunLightingShadowFramebuffer); sunLightingShadowFramebuffer = null; } - if (sunLightingShadowTexture != -1) { + if(sunLightingShadowTexture != -1) { GlStateManager.deleteTexture(sunLightingShadowTexture); sunLightingShadowTexture = -1; } - if (ssaoGenerateFramebuffer != null) { + if(ssaoGenerateFramebuffer != null) { _wglDeleteFramebuffer(ssaoGenerateFramebuffer); ssaoGenerateFramebuffer = null; } - if (ssaoGenerateTexture != -1) { + if(ssaoGenerateTexture != -1) { GlStateManager.deleteTexture(ssaoGenerateTexture); ssaoGenerateTexture = -1; reprojectionTexWidth = -1; reprojectionTexHeight = -1; } - if (shader_ssao_generate != null) { + if(shader_ssao_generate != null) { shader_ssao_generate.destroy(); shader_ssao_generate = null; } - if (ssaoNoiseTexture != -1) { + if(ssaoNoiseTexture != -1) { GlStateManager.deleteTexture(ssaoNoiseTexture); ssaoNoiseTexture = -1; } - for (int i = 0; i < 2; ++i) { - if (reprojectionControlFramebuffer[i] != null) { + for(int i = 0; i < 2; ++i) { + if(reprojectionControlFramebuffer[i] != null) { _wglDeleteFramebuffer(reprojectionControlFramebuffer[i]); reprojectionControlFramebuffer[i] = null; } - if (reprojectionControlSSAOTexture[i] != -1) { + if(reprojectionControlSSAOTexture[i] != -1) { GlStateManager.deleteTexture(reprojectionControlSSAOTexture[i]); reprojectionControlSSAOTexture[i] = -1; } - if (reprojectionSSRFramebuffer[i] != null) { + if(reprojectionSSRFramebuffer[i] != null) { _wglDeleteFramebuffer(reprojectionSSRFramebuffer[i]); reprojectionSSRFramebuffer[i] = null; } - if (reprojectionSSRTexture[i] != -1) { + if(reprojectionSSRTexture[i] != -1) { GlStateManager.deleteTexture(reprojectionSSRTexture[i]); reprojectionSSRTexture[i] = -1; } - if (reprojectionSSRHitVector[i] != -1) { + if(reprojectionSSRHitVector[i] != -1) { GlStateManager.deleteTexture(reprojectionSSRHitVector[i]); reprojectionSSRHitVector[i] = -1; } } - if (lastFrameFramebuffer != null) { + if(lastFrameFramebuffer != null) { _wglDeleteFramebuffer(lastFrameFramebuffer); lastFrameFramebuffer = null; } - if (lastFrameColorTexture != -1) { + if(lastFrameColorTexture != -1) { GlStateManager.deleteTexture(lastFrameColorTexture); lastFrameColorTexture = -1; } - if (lastFrameDepthTexture != -1) { + if(lastFrameDepthTexture != -1) { GlStateManager.deleteTexture(lastFrameDepthTexture); lastFrameDepthTexture = -1; } - if (gBufferQuarterFramebuffer != null) { + if(gBufferQuarterFramebuffer != null) { _wglDeleteFramebuffer(gBufferQuarterFramebuffer); gBufferQuarterFramebuffer = null; } - if (gBufferQuarterDepthTexture != -1) { + if(gBufferQuarterDepthTexture != -1) { GlStateManager.deleteTexture(gBufferQuarterDepthTexture); gBufferQuarterDepthTexture = -1; } - if (lastFrameGBufferFramebuffer != null) { + if(lastFrameGBufferFramebuffer != null) { _wglDeleteFramebuffer(lastFrameGBufferFramebuffer); lastFrameGBufferFramebuffer = null; } - if (lastFrameGBufferDepthTexture != -1) { + if(lastFrameGBufferDepthTexture != -1) { GlStateManager.deleteTexture(lastFrameGBufferDepthTexture); lastFrameGBufferDepthTexture = -1; } - if (lightingHDRFramebuffer != null) { + if(lightingHDRFramebuffer != null) { _wglDeleteFramebuffer(lightingHDRFramebuffer); lightingHDRFramebuffer = null; } - if (lightingHDRFramebufferColorTexture != -1) { + if(lightingHDRFramebufferColorTexture != -1) { GlStateManager.deleteTexture(lightingHDRFramebufferColorTexture); lightingHDRFramebufferColorTexture = -1; } - if (lightingHDRFramebufferDepthTexture != -1) { + if(lightingHDRFramebufferDepthTexture != -1) { GlStateManager.deleteTexture(lightingHDRFramebufferDepthTexture); lightingHDRFramebufferDepthTexture = -1; } - if (handRenderFramebuffer != null) { + if(handRenderFramebuffer != null) { _wglDeleteFramebuffer(handRenderFramebuffer); handRenderFramebuffer = null; } - if (handRenderFramebufferDepthTexture != -1) { + if(handRenderFramebufferDepthTexture != -1) { GlStateManager.deleteTexture(handRenderFramebufferDepthTexture); handRenderFramebufferDepthTexture = -1; } - if (atmosphereHDRFramebuffer != null) { + if(atmosphereHDRFramebuffer != null) { _wglDeleteFramebuffer(atmosphereHDRFramebuffer); atmosphereHDRFramebuffer = null; } - if (atmosphereHDRFramebufferColorTexture != -1) { + if(atmosphereHDRFramebufferColorTexture != -1) { GlStateManager.deleteTexture(atmosphereHDRFramebufferColorTexture); atmosphereHDRFramebufferColorTexture = -1; } - if (envMapAtmosphereFramebuffer != null) { + if(envMapAtmosphereFramebuffer != null) { _wglDeleteFramebuffer(envMapAtmosphereFramebuffer); envMapAtmosphereFramebuffer = null; } - if (envMapAtmosphereTexture != -1) { + if(envMapAtmosphereTexture != -1) { GlStateManager.deleteTexture(envMapAtmosphereTexture); envMapAtmosphereTexture = -1; } - if (envMapSkyFramebuffer != null) { + if(envMapSkyFramebuffer != null) { _wglDeleteFramebuffer(envMapSkyFramebuffer); envMapSkyFramebuffer = null; } - if (envMapSkyTexture != -1) { + if(envMapSkyTexture != -1) { GlStateManager.deleteTexture(envMapSkyTexture); envMapSkyTexture = -1; } - if (moonTextures != -1) { + if(moonTextures != -1) { GlStateManager.deleteTexture(moonTextures); moonTextures = -1; } - if (envMapFramebuffer != null) { + if(envMapFramebuffer != null) { _wglDeleteFramebuffer(envMapFramebuffer); envMapFramebuffer = null; } - if (envMapColorTexture != -1) { + if(envMapColorTexture != -1) { GlStateManager.deleteTexture(envMapColorTexture); envMapColorTexture = -1; } - if (envMapDepthTexture != -1) { + if(envMapDepthTexture != -1) { GlStateManager.deleteTexture(envMapDepthTexture); envMapDepthTexture = -1; } - if (atmosphereIrradianceFramebuffer != null) { + if(atmosphereIrradianceFramebuffer != null) { _wglDeleteFramebuffer(atmosphereIrradianceFramebuffer); atmosphereIrradianceFramebuffer = null; } - if (atmosphereIrradianceTexture != -1) { + if(atmosphereIrradianceTexture != -1) { GlStateManager.deleteTexture(atmosphereIrradianceTexture); atmosphereIrradianceTexture = -1; } - if (skyIrradianceFramebuffer != null) { + if(skyIrradianceFramebuffer != null) { _wglDeleteFramebuffer(skyIrradianceFramebuffer); skyIrradianceFramebuffer = null; } - if (skyIrradianceTexture != -1) { + if(skyIrradianceTexture != -1) { GlStateManager.deleteTexture(skyIrradianceTexture); skyIrradianceTexture = -1; } - if (tonemapOutputFramebuffer != null) { + if(tonemapOutputFramebuffer != null) { _wglDeleteFramebuffer(tonemapOutputFramebuffer); tonemapOutputFramebuffer = null; } - if (tonemapOutputTexture != -1) { + if(tonemapOutputTexture != -1) { GlStateManager.deleteTexture(tonemapOutputTexture); tonemapOutputTexture = -1; } - if (lensDistortFramebuffer != null) { + if(lensDistortFramebuffer != null) { _wglDeleteFramebuffer(lensDistortFramebuffer); lensDistortFramebuffer = null; } - if (lensDistortTexture != -1) { + if(lensDistortTexture != -1) { GlStateManager.deleteTexture(lensDistortTexture); lensDistortTexture = -1; } - if (lumaAvgDownscaleFramebuffers != null) { - for (int i = 0; i < lumaAvgDownscaleFramebuffers.length; ++i) { + if(lumaAvgDownscaleFramebuffers != null) { + for(int i = 0; i < lumaAvgDownscaleFramebuffers.length; ++i) { _wglDeleteFramebuffer(lumaAvgDownscaleFramebuffers[i]); } lumaAvgDownscaleFramebuffers = null; } - if (lumaAvgDownscaleTexture != null) { - for (int i = 0; i < lumaAvgDownscaleTexture.length; ++i) { + if(lumaAvgDownscaleTexture != null) { + for(int i = 0; i < lumaAvgDownscaleTexture.length; ++i) { GlStateManager.deleteTexture(lumaAvgDownscaleTexture[i]); } lumaAvgDownscaleTexture = null; } - if (exposureBlendFramebuffer != null) { + if(exposureBlendFramebuffer != null) { _wglDeleteFramebuffer(exposureBlendFramebuffer); exposureBlendFramebuffer = null; } - if (exposureBlendTexture != -1) { + if(exposureBlendTexture != -1) { GlStateManager.deleteTexture(exposureBlendTexture); exposureBlendTexture = -1; } - if (bloomBrightPassFramebuffer != null) { + if(bloomBrightPassFramebuffer != null) { _wglDeleteFramebuffer(bloomBrightPassFramebuffer); bloomBrightPassFramebuffer = null; } - if (bloomBrightPassTexture != -1) { + if(bloomBrightPassTexture != -1) { GlStateManager.bindTexture(bloomBrightPassTexture); bloomBrightPassTexture = -1; } - if (bloomHBlurFramebuffer != null) { + if(bloomHBlurFramebuffer != null) { _wglDeleteFramebuffer(bloomHBlurFramebuffer); bloomHBlurFramebuffer = null; } - if (bloomHBlurTexture != -1) { + if(bloomHBlurTexture != -1) { GlStateManager.deleteTexture(bloomHBlurTexture); bloomHBlurTexture = -1; } - if (bloomVBlurFramebuffer != null) { + if(bloomVBlurFramebuffer != null) { _wglDeleteFramebuffer(bloomVBlurFramebuffer); bloomVBlurFramebuffer = null; } - if (bloomVBlurTexture != -1) { + if(bloomVBlurTexture != -1) { GlStateManager.deleteTexture(bloomVBlurTexture); bloomVBlurTexture = -1; } - if (bloomDownscaleAFramebuffer != null) { + if(bloomDownscaleAFramebuffer != null) { _wglDeleteFramebuffer(bloomDownscaleAFramebuffer); bloomDownscaleAFramebuffer = null; } - if (bloomDownscaleATexture != -1) { + if(bloomDownscaleATexture != -1) { GlStateManager.deleteTexture(bloomDownscaleATexture); bloomDownscaleATexture = -1; } - if (bloomDownscaleBFramebuffer != null) { + if(bloomDownscaleBFramebuffer != null) { _wglDeleteFramebuffer(bloomDownscaleBFramebuffer); bloomDownscaleBFramebuffer = null; } - if (bloomDownscaleBTexture != -1) { + if(bloomDownscaleBTexture != -1) { GlStateManager.deleteTexture(bloomDownscaleBTexture); bloomDownscaleBTexture = -1; } - if (sunOcclusionValueFramebuffer != null) { + if(sunOcclusionValueFramebuffer != null) { _wglDeleteFramebuffer(sunOcclusionValueFramebuffer); sunOcclusionValueFramebuffer = null; } - if (sunOcclusionValueTexture != -1) { + if(sunOcclusionValueTexture != -1) { GlStateManager.deleteTexture(sunOcclusionValueTexture); sunOcclusionValueTexture = -1; } - if (dither8x8Texture != -1) { + if(dither8x8Texture != -1) { GlStateManager.deleteTexture(dither8x8Texture); dither8x8Texture = -1; } - if (shader_deferred_combine != null) { + if(shader_deferred_combine != null) { shader_deferred_combine.destroy(); shader_deferred_combine = null; } - if (shader_hand_depth_mask != null) { + if(shader_hand_depth_mask != null) { shader_hand_depth_mask.destroy(); shader_hand_depth_mask = null; } - if (brdfTexture != -1) { + if(brdfTexture != -1) { GlStateManager.deleteTexture(brdfTexture); brdfTexture = -1; } - if (shader_lighting_sun != null) { + if(shader_lighting_sun != null) { shader_lighting_sun.destroy(); shader_lighting_sun = null; } - if (shader_shadows_sun != null) { + if(shader_shadows_sun != null) { shader_shadows_sun.destroy(); shader_shadows_sun = null; } - if (shader_light_shafts_sample != null) { + if(shader_light_shafts_sample != null) { shader_light_shafts_sample.destroy(); shader_light_shafts_sample = null; } - if (skybox != null) { + if(skybox != null) { skybox.destroy(); skybox = null; } - if (pointLightMesh != null) { + if(pointLightMesh != null) { pointLightMesh.destroy(); pointLightMesh = null; } - if (shader_skybox_atmosphere != null) { + if(shader_skybox_atmosphere != null) { shader_skybox_atmosphere.destroy(); shader_skybox_atmosphere = null; } - if (shader_skybox_render != null) { + if(shader_skybox_render != null) { shader_skybox_render.destroy(); shader_skybox_render = null; } - if (shader_lighting_point != null) { + if(shader_lighting_point != null) { shader_lighting_point.destroy(); shader_lighting_point = null; } - if (shader_post_lens_distort != null) { + if(shader_post_lens_distort != null) { shader_post_lens_distort.destroy(); shader_post_lens_distort = null; } - if (shader_post_tonemap != null) { + if(shader_post_tonemap != null) { shader_post_tonemap.destroy(); shader_post_tonemap = null; } - if (shader_post_exposure_avg != null) { + if(shader_post_exposure_avg != null) { shader_post_exposure_avg.destroy(); shader_post_exposure_avg = null; } - if (shader_post_exposure_avg_luma != null) { + if(shader_post_exposure_avg_luma != null) { shader_post_exposure_avg_luma.destroy(); shader_post_exposure_avg_luma = null; } - if (shader_post_exposure_final != null) { + if(shader_post_exposure_final != null) { shader_post_exposure_final.destroy(); shader_post_exposure_final = null; } - if (shader_post_bloom_bright != null) { + if(shader_post_bloom_bright != null) { shader_post_bloom_bright.destroy(); shader_post_bloom_bright = null; } - if (shader_post_bloom_blur != null) { + if(shader_post_bloom_blur != null) { shader_post_bloom_blur.destroy(); shader_post_bloom_blur = null; } - if (shader_lens_sun_occlusion != null) { + if(shader_lens_sun_occlusion != null) { shader_lens_sun_occlusion.destroy(); shader_lens_sun_occlusion = null; } - if (shader_realistic_water_control != null) { + if(shader_realistic_water_control != null) { shader_realistic_water_control.destroy(); shader_realistic_water_control = null; } - if (shader_realistic_water_noise != null) { + if(shader_realistic_water_noise != null) { shader_realistic_water_noise.destroy(); shader_realistic_water_noise = null; } - if (shader_realistic_water_normals != null) { + if(shader_realistic_water_normals != null) { shader_realistic_water_normals.destroy(); shader_realistic_water_normals = null; } - if (shader_post_fxaa != null) { + if(shader_post_fxaa != null) { shader_post_fxaa.destroy(); shader_post_fxaa = null; } - if (shader_skybox_render_paraboloid != null) { + if(shader_skybox_render_paraboloid != null) { shader_skybox_render_paraboloid.destroy(); shader_skybox_render_paraboloid = null; } - if (shader_skybox_render_paraboloid_noclouds != null) { + if(shader_skybox_render_paraboloid_noclouds != null) { shader_skybox_render_paraboloid_noclouds.destroy(); shader_skybox_render_paraboloid_noclouds = null; } - if (shader_skybox_render_end != null) { + if(shader_skybox_render_end != null) { shader_skybox_render_end.destroy(); shader_skybox_render_end = null; } - for (int i = 0; i < 3; ++i) { - if (shader_skybox_irradiance[i] != null) { + for(int i = 0; i < 3; ++i) { + if(shader_skybox_irradiance[i] != null) { shader_skybox_irradiance[i].destroy(); shader_skybox_irradiance[i] = null; } } - if (shader_colored_fog_linear != null) { + if(shader_colored_fog_linear != null) { shader_colored_fog_linear.destroy(); shader_colored_fog_linear = null; } - if (shader_colored_fog_exp != null) { + if(shader_colored_fog_exp != null) { shader_colored_fog_exp.destroy(); shader_colored_fog_exp = null; } - if (shader_atmosphere_fog != null) { + if(shader_atmosphere_fog != null) { shader_atmosphere_fog.destroy(); shader_atmosphere_fog = null; } - if (shader_moon_render != null) { + if(shader_moon_render != null) { shader_moon_render.destroy(); shader_moon_render = null; } - if (shader_reproject_control != null) { + if(shader_reproject_control != null) { shader_reproject_control.destroy(); shader_reproject_control = null; } - if (shader_reproject_ssr != null) { + if(shader_reproject_ssr != null) { shader_reproject_ssr.destroy(); shader_reproject_ssr = null; } - if (realisticWaterMaskFramebuffer != null) { + if(realisticWaterMaskFramebuffer != null) { _wglDeleteFramebuffer(realisticWaterMaskFramebuffer); realisticWaterMaskFramebuffer = null; } - if (realisticWaterMaskTexture != -1) { + if(realisticWaterMaskTexture != -1) { GlStateManager.deleteTexture(realisticWaterMaskTexture); realisticWaterMaskTexture = -1; } - if (realisticWaterDepthBuffer != -1) { + if(realisticWaterDepthBuffer != -1) { GlStateManager.deleteTexture(realisticWaterDepthBuffer); realisticWaterDepthBuffer = -1; } - if (realisticWaterCombinedNormalsFramebuffer != null) { + if(realisticWaterCombinedNormalsFramebuffer != null) { _wglDeleteFramebuffer(realisticWaterCombinedNormalsFramebuffer); realisticWaterCombinedNormalsFramebuffer = null; } - if (realisticWaterCombinedNormalsTexture != -1) { + if(realisticWaterCombinedNormalsTexture != -1) { GlStateManager.deleteTexture(realisticWaterCombinedNormalsTexture); realisticWaterCombinedNormalsTexture = -1; } - if (realisticWaterRefractionTexture != -1) { + if(realisticWaterRefractionTexture != -1) { GlStateManager.deleteTexture(realisticWaterRefractionTexture); realisticWaterRefractionTexture = -1; } - if (realisticWaterControlFramebuffer != null) { + if(realisticWaterControlFramebuffer != null) { _wglDeleteFramebuffer(realisticWaterControlFramebuffer); realisticWaterControlFramebuffer = null; } - for (int i = 0; i < 2; ++i) { - if (realisticWaterSSRFramebuffer[i] != null) { + for(int i = 0; i < 2; ++i) { + if(realisticWaterSSRFramebuffer[i] != null) { _wglDeleteFramebuffer(realisticWaterSSRFramebuffer[i]); realisticWaterSSRFramebuffer[i] = null; } - if (realisticWaterControlReflectionTexture[i] != -1) { + if(realisticWaterControlReflectionTexture[i] != -1) { GlStateManager.deleteTexture(realisticWaterControlReflectionTexture[i]); realisticWaterControlReflectionTexture[i] = -1; } - if (realisticWaterControlHitVectorTexture[i] != -1) { + if(realisticWaterControlHitVectorTexture[i] != -1) { GlStateManager.deleteTexture(realisticWaterControlHitVectorTexture[i]); realisticWaterControlHitVectorTexture[i] = -1; } } - if (realisticWaterNormalMapFramebuffer != null) { + if(realisticWaterNormalMapFramebuffer != null) { _wglDeleteFramebuffer(realisticWaterNormalMapFramebuffer); realisticWaterNormalMapFramebuffer = null; } - if (realisticWaterNormalMapTexture != -1) { + if(realisticWaterNormalMapTexture != -1) { GlStateManager.deleteTexture(realisticWaterNormalMapTexture); realisticWaterNormalMapTexture = -1; } - if (realisticWaterDisplacementMapFramebuffer != null) { + if(realisticWaterDisplacementMapFramebuffer != null) { _wglDeleteFramebuffer(realisticWaterDisplacementMapFramebuffer); realisticWaterDisplacementMapFramebuffer = null; } - if (realisticWaterDisplacementMapTexture != -1) { + if(realisticWaterDisplacementMapTexture != -1) { GlStateManager.deleteTexture(realisticWaterDisplacementMapTexture); realisticWaterDisplacementMapTexture = -1; } - if (realisticWaterNoiseMap != -1) { + if(realisticWaterNoiseMap != -1) { GlStateManager.deleteTexture(realisticWaterNoiseMap); realisticWaterNoiseMap = -1; } - if (buffer_chunkLightingData != null) { + if(buffer_chunkLightingData != null) { _wglDeleteBuffers(buffer_chunkLightingData); buffer_chunkLightingData = null; } - if (buffer_worldLightingData != null) { + if(buffer_chunkLightingDataZero != null) { + _wglDeleteBuffers(buffer_chunkLightingDataZero); + buffer_chunkLightingDataZero = null; + } + if(buffer_worldLightingData != null) { _wglDeleteBuffers(buffer_worldLightingData); buffer_worldLightingData = null; } - if (worldLightingDataCopyBuffer != null) { + if(worldLightingDataCopyBuffer != null) { EagRuntime.freeByteBuffer(worldLightingDataCopyBuffer); worldLightingDataCopyBuffer = null; } - if (chunkLightingDataCopyBuffer != null) { + if(chunkLightingDataCopyBuffer != null) { EagRuntime.freeByteBuffer(chunkLightingDataCopyBuffer); chunkLightingDataCopyBuffer = null; } - for (int i = 0; i < lightSourceBuckets.length; ++i) { + for(int i = 0; i < lightSourceBuckets.length; ++i) { lightSourceBuckets[i].clear(); + lightSourceBucketSerials[i] = -1; + lightSourceRenderPosSerials[i] = -1; } currentLightSourceBucket = null; + currentLightSourceBucketId = -1; currentBoundLightSourceBucket = null; isChunkLightingEnabled = false; - for (int i = 0; i < shader_gbuffer_debug_view.length; ++i) { - if (shader_gbuffer_debug_view[i] != null) { + for(int i = 0; i < shader_gbuffer_debug_view.length; ++i) { + if(shader_gbuffer_debug_view[i] != null) { shader_gbuffer_debug_view[i].destroy(); shader_gbuffer_debug_view[i] = null; } @@ -4167,7 +4013,7 @@ public class EaglerDeferredPipeline { GlStateManager.loadIdentity(); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.loadIdentity(); - if (config.is_rendering_shadowsSun_clamped > 0 && config.is_rendering_shadowsSmoothed) { + if(config.is_rendering_shadowsSun_clamped > 0 && config.is_rendering_shadowsSmoothed) { GlStateManager.bindTexture(sunShadowDepthBuffer); _wglTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_COMPARE_MODE, _GL_COMPARE_REF_TO_TEXTURE); setNearest(); @@ -4175,13 +4021,15 @@ public class EaglerDeferredPipeline { } public static final boolean isSupported() { - return EaglercraftGPU.checkHasHDRFramebufferSupportWithFilter(); + return EaglercraftGPU.checkOpenGLESVersion() >= 300 && EaglercraftGPU.checkHasHDRFramebufferSupportWithFilter(); } public static final String getReasonUnsupported() { - if (!EaglercraftGPU.checkHasHDRFramebufferSupportWithFilter()) { + if(EaglercraftGPU.checkOpenGLESVersion() < 300) { + return I18n.format("shaders.gui.unsupported.reason.oldOpenGLVersion"); + }else if(!EaglercraftGPU.checkHasHDRFramebufferSupportWithFilter()) { return I18n.format("shaders.gui.unsupported.reason.hdrFramebuffer"); - } else { + }else { return null; } } @@ -4197,7 +4045,7 @@ public class EaglerDeferredPipeline { GlStateManager.pushMatrix(); GlStateManager.matrixMode(GL_MODELVIEW); GlStateManager.pushMatrix(); - ScaledResolution scaledresolution = new ScaledResolution(mc); + ScaledResolution scaledresolution = mc.scaledResolution; int w = scaledresolution.getScaledWidth(); mc.entityRenderer.setupOverlayRendering(); GlStateManager.enableAlpha(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardRenderCallbackHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardRenderCallbackHandler.java index 699e747..7dd380c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardRenderCallbackHandler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardRenderCallbackHandler.java @@ -22,7 +22,7 @@ import java.util.List; */ public class ForwardRenderCallbackHandler { - public final List renderPassList = new ArrayList(1024); + public final List renderPassList = new ArrayList<>(1024); public void push(ShadersRenderPassFuture f) { renderPassList.add(f); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java index d258636..a49c1a8 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java @@ -8,7 +8,6 @@ import java.io.DataInputStream; import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; @@ -20,6 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f; import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; import net.minecraft.client.Minecraft; import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; /** * Copyright (c) 2023 lax1dude. All Rights Reserved. @@ -38,8 +38,8 @@ import net.minecraft.util.MathHelper; */ public class LensFlareMeshRenderer { - public static final String streaksTextureLocation ="assets/eagler/glsl/deferred/lens_streaks.bmp"; - public static final String ghostsTextureLocation = "assets/eagler/glsl/deferred/lens_ghosts.bmp"; + public static final ResourceLocation streaksTextureLocation = new ResourceLocation("eagler:glsl/deferred/lens_streaks.bmp"); + public static final ResourceLocation ghostsTextureLocation = new ResourceLocation("eagler:glsl/deferred/lens_ghosts.bmp"); public static final int ghostsSpriteCount = 4; static IBufferArrayGL streaksVertexArray = null; @@ -157,11 +157,8 @@ public class LensFlareMeshRenderer { streaksTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(streaksTexture); - byte[] flareTex = EagRuntime.getResourceBytes(streaksTextureLocation); - if(flareTex == null) { - throw new RuntimeException("Could not locate: " + streaksTextureLocation); - } - try(DataInputStream dis = new DataInputStream(new EaglerInputStream(flareTex))) { + try (DataInputStream dis = new DataInputStream( + Minecraft.getMinecraft().getResourceManager().getResource(streaksTextureLocation).getInputStream())) { loadFlareTexture(copyBuffer, dis); }catch(IOException ex) { EagRuntime.freeByteBuffer(copyBuffer); @@ -170,11 +167,8 @@ public class LensFlareMeshRenderer { ghostsTexture = GlStateManager.generateTexture(); GlStateManager.bindTexture(ghostsTexture); - flareTex = EagRuntime.getResourceBytes(ghostsTextureLocation); - if(flareTex == null) { - throw new RuntimeException("Could not locate: " + ghostsTextureLocation); - } - try(DataInputStream dis = new DataInputStream(new EaglerInputStream(flareTex))) { + try (DataInputStream dis = new DataInputStream( + Minecraft.getMinecraft().getResourceManager().getResource(ghostsTextureLocation).getInputStream())) { loadFlareTexture(copyBuffer, dis); }catch(IOException ex) { EagRuntime.freeByteBuffer(copyBuffer); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ShaderPackInfo.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ShaderPackInfo.java index df2e38d..4837b04 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ShaderPackInfo.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ShaderPackInfo.java @@ -52,7 +52,7 @@ public class ShaderPackInfo { vers = json.optString("vers", "Unknown"); author = json.optString("author", "Unknown"); apiVers = json.optInt("api_vers", -1); - supportedFeatures = new HashSet(); + supportedFeatures = new HashSet<>(); JSONArray features = json.getJSONArray("features"); if(features.length() == 0) { throw new JSONException("No supported features list has been defined for this shader pack!"); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfig.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfig.java index 7b29066..4d8b481 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfig.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfig.java @@ -1,135 +1,140 @@ -package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui; - -import java.io.IOException; -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiShaderConfig extends GuiScreen { - - private static final Logger logger = LogManager.getLogger(); - - boolean shaderStartState = false; - - private final GuiScreen parent; - private GuiShaderConfigList listView; - - private String title; - private GuiButton enableDisableButton; - - public GuiShaderConfig(GuiScreen parent) { - this.parent = parent; - this.shaderStartState = Minecraft.getMinecraft().gameSettings.shaders; - } - - public void initGui() { - this.title = I18n.format("shaders.gui.title"); - this.buttonList.clear(); - this.buttonList.add(enableDisableButton = new GuiButton(0, width / 2 - 155, height - 30, 150, 20, I18n.format("shaders.gui.enable") - + ": " + (mc.gameSettings.shaders ? I18n.format("gui.yes") : I18n.format("gui.no")))); - this.buttonList.add(new GuiButton(1, width / 2 + 5, height - 30, 150, 20, I18n.format("gui.done"))); - if(listView == null) { - this.listView = new GuiShaderConfigList(this, mc); - }else { - this.listView.resize(); - } - } - - protected void actionPerformed(GuiButton btn) { - if(btn.id == 0) { - mc.gameSettings.shaders = !mc.gameSettings.shaders; - listView.setAllDisabled(!mc.gameSettings.shaders); - enableDisableButton.displayString = I18n.format("shaders.gui.enable") + ": " - + (mc.gameSettings.shaders ? I18n.format("gui.yes") : I18n.format("gui.no")); - }else if(btn.id == 1) { - mc.displayGuiScreen(parent); - } - } - - public void onGuiClosed() { - if(shaderStartState != mc.gameSettings.shaders || listView.isDirty()) { - mc.gameSettings.saveOptions(); - if(shaderStartState != mc.gameSettings.shaders) { - mc.loadingScreen.eaglerShowRefreshResources(); - mc.refreshResources(); - }else { - logger.info("Reloading shaders..."); - try { - mc.gameSettings.deferredShaderConf.reloadShaderPackInfo(mc.getResourceManager()); - }catch(IOException ex) { - logger.info("Could not reload shader pack info!"); - logger.info(ex); - logger.info("Shaders have been disabled"); - mc.gameSettings.shaders = false; - mc.refreshResources(); - return; - } - - if(mc.gameSettings.shaders) { - ShaderSource.clearCache(); - } - - if (mc.renderGlobal != null) { - mc.renderGlobal.loadRenderers(); - } - } - } - } - - public void handleMouseInput() throws IOException { - super.handleMouseInput(); - listView.handleMouseInput(); - } - - protected void mouseClicked(int parInt1, int parInt2, int parInt3) { - super.mouseClicked(parInt1, parInt2, parInt3); - listView.mouseClicked(parInt1, parInt2, parInt3); - } - - protected void mouseReleased(int i, int j, int k) { - super.mouseReleased(i, j, k); - listView.mouseReleased(i, j, k); - } - - public void drawScreen(int i, int j, float f) { - this.drawBackground(0); - listView.drawScreen(i, j, f); - drawCenteredString(this.fontRendererObj, title, this.width / 2, 15, 16777215); - super.drawScreen(i, j, f); - listView.postRender(i, j, f); - } - - void renderTooltip(List txt, int x, int y) { - drawHoveringText(txt, x, y); - } - - FontRenderer getFontRenderer() { - return fontRendererObj; - } - - Minecraft getMinecraft() { - return mc; - } -} +package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui; + +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiShaderConfig extends GuiScreen { + + private static final Logger logger = LogManager.getLogger(); + + boolean shaderStartState = false; + + private final GuiScreen parent; + private GuiShaderConfigList listView; + + private String title; + private GuiButton enableDisableButton; + + public GuiShaderConfig(GuiScreen parent) { + this.parent = parent; + this.shaderStartState = Minecraft.getMinecraft().gameSettings.shaders; + } + + public void initGui() { + this.title = I18n.format("shaders.gui.title"); + this.buttonList.clear(); + this.buttonList.add(enableDisableButton = new GuiButton(0, width / 2 - 155, height - 30, 150, 20, I18n.format("shaders.gui.enable") + + ": " + (mc.gameSettings.shaders ? I18n.format("gui.yes") : I18n.format("gui.no")))); + this.buttonList.add(new GuiButton(1, width / 2 + 5, height - 30, 150, 20, I18n.format("gui.done"))); + if(listView == null) { + this.listView = new GuiShaderConfigList(this, mc); + }else { + this.listView.resize(); + } + } + + protected void actionPerformed(GuiButton btn) { + if(btn.id == 0) { + mc.gameSettings.shaders = !mc.gameSettings.shaders; + listView.setAllDisabled(!mc.gameSettings.shaders); + enableDisableButton.displayString = I18n.format("shaders.gui.enable") + ": " + + (mc.gameSettings.shaders ? I18n.format("gui.yes") : I18n.format("gui.no")); + }else if(btn.id == 1) { + mc.displayGuiScreen(parent); + } + } + + public void onGuiClosed() { + if(shaderStartState != mc.gameSettings.shaders || listView.isDirty()) { + mc.gameSettings.saveOptions(); + if(shaderStartState != mc.gameSettings.shaders) { + mc.loadingScreen.eaglerShowRefreshResources(); + mc.refreshResources(); + }else { + logger.info("Reloading shaders..."); + try { + mc.gameSettings.deferredShaderConf.reloadShaderPackInfo(mc.getResourceManager()); + }catch(IOException ex) { + logger.info("Could not reload shader pack info!"); + logger.info(ex); + logger.info("Shaders have been disabled"); + mc.gameSettings.shaders = false; + mc.refreshResources(); + return; + } + + if(mc.gameSettings.shaders) { + ShaderSource.clearCache(); + } + + if (mc.renderGlobal != null) { + mc.renderGlobal.loadRenderers(); + } + } + } + } + + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + listView.handleMouseInput(); + } + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + listView.handleTouchInput(); + } + + protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + super.mouseClicked(parInt1, parInt2, parInt3); + listView.mouseClicked(parInt1, parInt2, parInt3); + } + + protected void mouseReleased(int i, int j, int k) { + super.mouseReleased(i, j, k); + listView.mouseReleased(i, j, k); + } + + public void drawScreen(int i, int j, float f) { + this.drawBackground(0); + listView.drawScreen(i, j, f); + drawCenteredString(this.fontRendererObj, title, this.width / 2, 15, 16777215); + super.drawScreen(i, j, f); + listView.postRender(i, j, f); + } + + void renderTooltip(List txt, int x, int y) { + drawHoveringText(txt, x, y); + } + + FontRenderer getFontRenderer() { + return fontRendererObj; + } + + Minecraft getMinecraft() { + return mc; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfigList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfigList.java index 680297d..9d62b7f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfigList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/gui/GuiShaderConfigList.java @@ -35,7 +35,7 @@ public class GuiShaderConfigList extends GuiListExtended { private final GuiShaderConfig screen; - private final List list = new ArrayList(); + private final List list = new ArrayList<>(); private static abstract class ShaderOption { @@ -56,7 +56,7 @@ public class GuiShaderConfigList extends GuiListExtended { } private static List loadDescription(String key) { - List ret = new ArrayList(); + List ret = new ArrayList<>(); String msg; int i = 0; while(true) { @@ -112,7 +112,7 @@ public class GuiShaderConfigList extends GuiListExtended { this.list.add(new ListEntrySpacing()); this.list.add(new ListEntrySpacing()); this.list.add(new ListEntryHeader(I18n.format("shaders.gui.headerTier1"))); - List opts = new ArrayList(); + List opts = new ArrayList<>(); EaglerDeferredConfig conf = mcIn.gameSettings.deferredShaderConf; if(conf.shaderPackInfo.WAVING_BLOCKS) { opts.add(new ShaderOption(loadShaderLbl("WAVING_BLOCKS"), loadShaderDesc("WAVING_BLOCKS")) { @@ -550,6 +550,7 @@ public class GuiShaderConfigList extends GuiListExtended { @Override public boolean mousePressed(int var1, int var2, int var3, int var4, int var5, int var6) { + if(var4 != 0) return false; if(this.button1 != null) { if(this.button1.yPosition + 15 < bottom && this.button1.yPosition + 5 > top) { if(this.button1.mousePressed(mc, var2, var3)) { @@ -610,7 +611,7 @@ public class GuiShaderConfigList extends GuiListExtended { } private void renderTooltip(int x, int y, int width, List msg) { - ArrayList tooltipList = new ArrayList(msg.size() * 2); + List tooltipList = new ArrayList<>(msg.size() * 2); for(int i = 0, l = msg.size(); i < l; ++i) { String s = msg.get(i); if(s.length() > 0) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderAccelParticleForward.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderAccelParticleForward.java index bde8c29..5393352 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderAccelParticleForward.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderAccelParticleForward.java @@ -13,21 +13,14 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; /** * Copyright (c) 2023 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -39,13 +32,13 @@ public class PipelineShaderAccelParticleForward extends ShaderProgram lst = new ArrayList(2); - if (dynamicLights) { + List lst = new ArrayList<>(2); + if(dynamicLights) { lst.add("COMPILE_DYNAMIC_LIGHTS"); } - if (sunShadows > 0) { + if(sunShadows > 0) { int lods = sunShadows - 1; - if (lods > 2) { + if(lods > 2) { lods = 2; } lst.add("COMPILE_SUN_SHADOW_LOD" + lods); @@ -54,11 +47,11 @@ public class PipelineShaderAccelParticleForward extends ShaderProgram compileFlags = new ArrayList(2); + List compileFlags = new ArrayList<>(2); if(ssao) { compileFlags.add("COMPILE_GLOBAL_AMBIENT_OCCLUSION"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderGBufferFog.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderGBufferFog.java index a158c5d..0827606 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderGBufferFog.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderGBufferFog.java @@ -28,7 +28,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; public class PipelineShaderGBufferFog extends ShaderProgram { public static PipelineShaderGBufferFog compile(boolean linear, boolean atmosphere, boolean lightShafts) { - List macros = new ArrayList(3); + List macros = new ArrayList<>(3); if(linear) { macros.add("COMPILE_FOG_LINEAR"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingPoint.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingPoint.java index 73f5874..8fc6aa1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingPoint.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingPoint.java @@ -29,7 +29,7 @@ public class PipelineShaderLightingPoint extends ShaderProgram compileFlags = new ArrayList(2); + List compileFlags = new ArrayList<>(2); if(shadows) { compileFlags.add("COMPILE_PARABOLOID_SHADOW"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingSun.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingSun.java index 0932c5a..9e72537 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingSun.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderLightingSun.java @@ -29,7 +29,7 @@ public class PipelineShaderLightingSun extends ShaderProgram compileFlags = new ArrayList(1); + List compileFlags = new ArrayList<>(1); if(shadowsSun > 0) { compileFlags.add("COMPILE_SUN_SHADOW"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderPostExposureAvg.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderPostExposureAvg.java index e4a092c..6a267c9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderPostExposureAvg.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderPostExposureAvg.java @@ -28,7 +28,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; public class PipelineShaderPostExposureAvg extends ShaderProgram { public static PipelineShaderPostExposureAvg compile(boolean luma) throws ShaderException { - List compileFlags = new ArrayList(1); + List compileFlags = new ArrayList<>(1); if(luma) { compileFlags.add("CALCULATE_LUMINANCE"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojControl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojControl.java index ac9f6c9..36be41e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojControl.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojControl.java @@ -28,7 +28,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL; public class PipelineShaderReprojControl extends ShaderProgram { public static PipelineShaderReprojControl compile(boolean ssao, boolean ssr) throws ShaderException { - List compileFlags = new ArrayList(2); + List compileFlags = new ArrayList<>(2); if(ssao) { compileFlags.add("COMPILE_REPROJECT_SSAO"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderShadowsSun.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderShadowsSun.java index bd318ab..2164b71 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderShadowsSun.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderShadowsSun.java @@ -30,7 +30,7 @@ public class PipelineShaderShadowsSun extends ShaderProgram compileFlags = new ArrayList(2); + List compileFlags = new ArrayList<>(2); if(shadowsSun == 0) { throw new IllegalStateException("Enable shadows to compile this shader"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderSkyboxRender.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderSkyboxRender.java index df798ac..14e4f45 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderSkyboxRender.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderSkyboxRender.java @@ -28,7 +28,7 @@ import java.util.List; public class PipelineShaderSkyboxRender extends ShaderProgram { public static PipelineShaderSkyboxRender compile(boolean paraboloid, boolean clouds) throws ShaderException { - List compileFlags = new ArrayList(); + List compileFlags = new ArrayList<>(); if(paraboloid) { compileFlags.add("COMPILE_PARABOLOID_SKY"); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java index ae43195..83b3f57 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java @@ -10,7 +10,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader; +import net.lax1dude.eaglercraft.v1_8.opengl.GLSLHeader; import net.minecraft.util.ResourceLocation; /** @@ -47,7 +47,7 @@ public class ShaderCompiler { public static IShaderGL compileShader(String name, int stage, String filename, String source, List compileFlags) throws ShaderCompileException { logger.info("Compiling Shader: " + filename); StringBuilder srcCat = new StringBuilder(); - srcCat.append(FixedFunctionShader.FixedFunctionConstants.VERSION).append('\n'); + srcCat.append(GLSLHeader.getHeader()).append('\n'); if(compileFlags != null && compileFlags.size() > 0) { for(int i = 0, l = compileFlags.size(); i < l; ++i) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java index 4e9295e..17b2129 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java @@ -18,21 +18,14 @@ import net.minecraft.util.ResourceLocation; /** * Copyright (c) 2023 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -41,127 +34,70 @@ public class ShaderSource { private static final Logger logger = LogManager.getLogger("ShaderSource"); - public static final ResourceLocation accel_particle_vsh = new ResourceLocation( - "eagler:glsl/deferred/accel_particle.vsh"); - public static final ResourceLocation accel_particle_gbuffer_fsh = new ResourceLocation( - "eagler:glsl/deferred/accel_particle_gbuffer.fsh"); - public static final ResourceLocation accel_particle_forward_fsh = new ResourceLocation( - "eagler:glsl/deferred/accel_particle_forward.fsh"); - public static final ResourceLocation deferred_core_vsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_core.vsh"); - public static final ResourceLocation deferred_core_gbuffer_fsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_core_gbuffer.fsh"); - public static final ResourceLocation deferred_shadow_vsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_shadow.vsh"); - public static final ResourceLocation deferred_shadow_fsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_shadow.fsh"); - public static final ResourceLocation deferred_local_vsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_local.vsh"); - public static final ResourceLocation deferred_combine_fsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_combine.fsh"); - public static final ResourceLocation deferred_fog_fsh = new ResourceLocation( - "eagler:glsl/deferred/deferred_fog.fsh"); - public static final ResourceLocation forward_core_vsh = new ResourceLocation( - "eagler:glsl/deferred/forward_core.vsh"); - public static final ResourceLocation forward_core_fsh = new ResourceLocation( - "eagler:glsl/deferred/forward_core.fsh"); - public static final ResourceLocation forward_glass_highlights_vsh = new ResourceLocation( - "eagler:glsl/deferred/forward_glass_highlights.vsh"); - public static final ResourceLocation forward_glass_highlights_fsh = new ResourceLocation( - "eagler:glsl/deferred/forward_glass_highlights.fsh"); - public static final ResourceLocation realistic_water_mask_vsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_mask.vsh"); - public static final ResourceLocation realistic_water_mask_fsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_mask.fsh"); - public static final ResourceLocation realistic_water_render_vsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_render.vsh"); - public static final ResourceLocation realistic_water_render_fsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_render.fsh"); - public static final ResourceLocation realistic_water_control_fsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_control.fsh"); - public static final ResourceLocation realistic_water_normals_fsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_normals.fsh"); - public static final ResourceLocation realistic_water_noise_fsh = new ResourceLocation( - "eagler:glsl/deferred/realistic_water_noise.fsh"); - public static final ResourceLocation gbuffer_debug_view_fsh = new ResourceLocation( - "eagler:glsl/deferred/gbuffer_debug_view.fsh"); - public static final ResourceLocation ssao_generate_fsh = new ResourceLocation( - "eagler:glsl/deferred/ssao_generate.fsh"); - public static final ResourceLocation lighting_sun_fsh = new ResourceLocation( - "eagler:glsl/deferred/lighting_sun.fsh"); + public static final ResourceLocation accel_particle_vsh = new ResourceLocation("eagler:glsl/deferred/accel_particle.vsh"); + public static final ResourceLocation accel_particle_gbuffer_fsh = new ResourceLocation("eagler:glsl/deferred/accel_particle_gbuffer.fsh"); + public static final ResourceLocation accel_particle_forward_fsh = new ResourceLocation("eagler:glsl/deferred/accel_particle_forward.fsh"); + public static final ResourceLocation deferred_core_vsh = new ResourceLocation("eagler:glsl/deferred/deferred_core.vsh"); + public static final ResourceLocation deferred_core_gbuffer_fsh = new ResourceLocation("eagler:glsl/deferred/deferred_core_gbuffer.fsh"); + public static final ResourceLocation deferred_shadow_vsh = new ResourceLocation("eagler:glsl/deferred/deferred_shadow.vsh"); + public static final ResourceLocation deferred_shadow_fsh = new ResourceLocation("eagler:glsl/deferred/deferred_shadow.fsh"); + public static final ResourceLocation deferred_local_vsh = new ResourceLocation("eagler:glsl/deferred/deferred_local.vsh"); + public static final ResourceLocation deferred_combine_fsh = new ResourceLocation("eagler:glsl/deferred/deferred_combine.fsh"); + public static final ResourceLocation deferred_fog_fsh = new ResourceLocation("eagler:glsl/deferred/deferred_fog.fsh"); + public static final ResourceLocation forward_core_vsh = new ResourceLocation("eagler:glsl/deferred/forward_core.vsh"); + public static final ResourceLocation forward_core_fsh = new ResourceLocation("eagler:glsl/deferred/forward_core.fsh"); + public static final ResourceLocation forward_glass_highlights_vsh = new ResourceLocation("eagler:glsl/deferred/forward_glass_highlights.vsh"); + public static final ResourceLocation forward_glass_highlights_fsh = new ResourceLocation("eagler:glsl/deferred/forward_glass_highlights.fsh"); + public static final ResourceLocation realistic_water_mask_vsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_mask.vsh"); + public static final ResourceLocation realistic_water_mask_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_mask.fsh"); + public static final ResourceLocation realistic_water_render_vsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_render.vsh"); + public static final ResourceLocation realistic_water_render_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_render.fsh"); + public static final ResourceLocation realistic_water_control_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_control.fsh"); + public static final ResourceLocation realistic_water_normals_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_normals.fsh"); + public static final ResourceLocation realistic_water_noise_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_noise.fsh"); + public static final ResourceLocation gbuffer_debug_view_fsh = new ResourceLocation("eagler:glsl/deferred/gbuffer_debug_view.fsh"); + public static final ResourceLocation ssao_generate_fsh = new ResourceLocation("eagler:glsl/deferred/ssao_generate.fsh"); + public static final ResourceLocation lighting_sun_fsh = new ResourceLocation("eagler:glsl/deferred/lighting_sun.fsh"); public static final ResourceLocation shadows_sun_fsh = new ResourceLocation("eagler:glsl/deferred/shadows_sun.fsh"); - public static final ResourceLocation light_shafts_sample_fsh = new ResourceLocation( - "eagler:glsl/deferred/light_shafts_sample.fsh"); - public static final ResourceLocation post_tonemap_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_tonemap.fsh"); - public static final ResourceLocation post_bloom_bright_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_bloom_bright.fsh"); - public static final ResourceLocation post_bloom_blur_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_bloom_blur.fsh"); - public static final ResourceLocation post_lens_distort_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_lens_distort.fsh"); - public static final ResourceLocation post_exposure_avg_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_exposure_avg.fsh"); - public static final ResourceLocation post_exposure_final_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_exposure_final.fsh"); - public static final ResourceLocation post_lens_streaks_vsh = new ResourceLocation( - "eagler:glsl/deferred/post_lens_streaks.vsh"); - public static final ResourceLocation post_lens_streaks_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_lens_streaks.fsh"); - public static final ResourceLocation post_lens_ghosts_vsh = new ResourceLocation( - "eagler:glsl/deferred/post_lens_ghosts.vsh"); - public static final ResourceLocation post_lens_ghosts_fsh = new ResourceLocation( - "eagler:glsl/deferred/post_lens_ghosts.fsh"); - public static final ResourceLocation lens_sun_occlusion_fsh = new ResourceLocation( - "eagler:glsl/deferred/lens_sun_occlusion.fsh"); - public static final ResourceLocation skybox_atmosphere_fsh = new ResourceLocation( - "eagler:glsl/deferred/skybox_atmosphere.fsh"); - public static final ResourceLocation skybox_irradiance_fsh = new ResourceLocation( - "eagler:glsl/deferred/skybox_irradiance.fsh"); - public static final ResourceLocation skybox_render_vsh = new ResourceLocation( - "eagler:glsl/deferred/skybox_render.vsh"); - public static final ResourceLocation skybox_render_fsh = new ResourceLocation( - "eagler:glsl/deferred/skybox_render.fsh"); - public static final ResourceLocation skybox_render_end_vsh = new ResourceLocation( - "eagler:glsl/deferred/skybox_render_end.vsh"); - public static final ResourceLocation skybox_render_end_fsh = new ResourceLocation( - "eagler:glsl/deferred/skybox_render_end.fsh"); + public static final ResourceLocation light_shafts_sample_fsh = new ResourceLocation("eagler:glsl/deferred/light_shafts_sample.fsh"); + public static final ResourceLocation post_tonemap_fsh = new ResourceLocation("eagler:glsl/deferred/post_tonemap.fsh"); + public static final ResourceLocation post_bloom_bright_fsh = new ResourceLocation("eagler:glsl/deferred/post_bloom_bright.fsh"); + public static final ResourceLocation post_bloom_blur_fsh = new ResourceLocation("eagler:glsl/deferred/post_bloom_blur.fsh"); + public static final ResourceLocation post_lens_distort_fsh = new ResourceLocation("eagler:glsl/deferred/post_lens_distort.fsh"); + public static final ResourceLocation post_exposure_avg_fsh = new ResourceLocation("eagler:glsl/deferred/post_exposure_avg.fsh"); + public static final ResourceLocation post_exposure_final_fsh = new ResourceLocation("eagler:glsl/deferred/post_exposure_final.fsh"); + public static final ResourceLocation post_lens_streaks_vsh = new ResourceLocation("eagler:glsl/deferred/post_lens_streaks.vsh"); + public static final ResourceLocation post_lens_streaks_fsh = new ResourceLocation("eagler:glsl/deferred/post_lens_streaks.fsh"); + public static final ResourceLocation post_lens_ghosts_vsh = new ResourceLocation("eagler:glsl/deferred/post_lens_ghosts.vsh"); + public static final ResourceLocation post_lens_ghosts_fsh = new ResourceLocation("eagler:glsl/deferred/post_lens_ghosts.fsh"); + public static final ResourceLocation lens_sun_occlusion_fsh = new ResourceLocation("eagler:glsl/deferred/lens_sun_occlusion.fsh"); + public static final ResourceLocation skybox_atmosphere_fsh = new ResourceLocation("eagler:glsl/deferred/skybox_atmosphere.fsh"); + public static final ResourceLocation skybox_irradiance_fsh = new ResourceLocation("eagler:glsl/deferred/skybox_irradiance.fsh"); + public static final ResourceLocation skybox_render_vsh = new ResourceLocation("eagler:glsl/deferred/skybox_render.vsh"); + public static final ResourceLocation skybox_render_fsh = new ResourceLocation("eagler:glsl/deferred/skybox_render.fsh"); + public static final ResourceLocation skybox_render_end_vsh = new ResourceLocation("eagler:glsl/deferred/skybox_render_end.vsh"); + public static final ResourceLocation skybox_render_end_fsh = new ResourceLocation("eagler:glsl/deferred/skybox_render_end.fsh"); public static final ResourceLocation moon_render_vsh = new ResourceLocation("eagler:glsl/deferred/moon_render.vsh"); public static final ResourceLocation moon_render_fsh = new ResourceLocation("eagler:glsl/deferred/moon_render.fsh"); - public static final ResourceLocation clouds_noise3d_fsh = new ResourceLocation( - "eagler:glsl/deferred/clouds_noise3d.fsh"); - public static final ResourceLocation clouds_shapes_fsh = new ResourceLocation( - "eagler:glsl/deferred/clouds_shapes.fsh"); - public static final ResourceLocation clouds_shapes_vsh = new ResourceLocation( - "eagler:glsl/deferred/clouds_shapes.vsh"); - public static final ResourceLocation clouds_sample_fsh = new ResourceLocation( - "eagler:glsl/deferred/clouds_sample.fsh"); - public static final ResourceLocation clouds_sun_occlusion_fsh = new ResourceLocation( - "eagler:glsl/deferred/clouds_sun_occlusion.fsh"); - public static final ResourceLocation clouds_color_fsh = new ResourceLocation( - "eagler:glsl/deferred/clouds_color.fsh"); - public static final ResourceLocation lighting_mesh_vsh = new ResourceLocation( - "eagler:glsl/deferred/lighting_mesh.vsh"); - public static final ResourceLocation lighting_point_fsh = new ResourceLocation( - "eagler:glsl/deferred/lighting_point.fsh"); - public static final ResourceLocation reproject_control_fsh = new ResourceLocation( - "eagler:glsl/deferred/reproject_control.fsh"); - public static final ResourceLocation reproject_ssr_fsh = new ResourceLocation( - "eagler:glsl/deferred/reproject_ssr.fsh"); + public static final ResourceLocation clouds_noise3d_fsh = new ResourceLocation("eagler:glsl/deferred/clouds_noise3d.fsh"); + public static final ResourceLocation clouds_shapes_fsh = new ResourceLocation("eagler:glsl/deferred/clouds_shapes.fsh"); + public static final ResourceLocation clouds_shapes_vsh = new ResourceLocation("eagler:glsl/deferred/clouds_shapes.vsh"); + public static final ResourceLocation clouds_sample_fsh = new ResourceLocation("eagler:glsl/deferred/clouds_sample.fsh"); + public static final ResourceLocation clouds_sun_occlusion_fsh = new ResourceLocation("eagler:glsl/deferred/clouds_sun_occlusion.fsh"); + public static final ResourceLocation clouds_color_fsh = new ResourceLocation("eagler:glsl/deferred/clouds_color.fsh"); + public static final ResourceLocation lighting_mesh_vsh = new ResourceLocation("eagler:glsl/deferred/lighting_mesh.vsh"); + public static final ResourceLocation lighting_point_fsh = new ResourceLocation("eagler:glsl/deferred/lighting_point.fsh"); + public static final ResourceLocation reproject_control_fsh = new ResourceLocation("eagler:glsl/deferred/reproject_control.fsh"); + public static final ResourceLocation reproject_ssr_fsh = new ResourceLocation("eagler:glsl/deferred/reproject_ssr.fsh"); public static final ResourceLocation post_fxaa_fsh = new ResourceLocation("eagler:glsl/deferred/post_fxaa.fsh"); - public static final ResourceLocation hand_depth_mask_fsh = new ResourceLocation( - "eagler:glsl/deferred/hand_depth_mask.fsh"); + public static final ResourceLocation hand_depth_mask_fsh = new ResourceLocation("eagler:glsl/deferred/hand_depth_mask.fsh"); - public static final ResourceLocation core_dynamiclights_vsh = new ResourceLocation( - "eagler:glsl/dynamiclights/core_dynamiclights.vsh"); - public static final ResourceLocation core_dynamiclights_fsh = new ResourceLocation( - "eagler:glsl/dynamiclights/core_dynamiclights.fsh"); - public static final ResourceLocation accel_particle_dynamiclights_vsh = new ResourceLocation( - "eagler:glsl/dynamiclights/accel_particle_dynamiclights.vsh"); - public static final ResourceLocation accel_particle_dynamiclights_fsh = new ResourceLocation( - "eagler:glsl/dynamiclights/accel_particle_dynamiclights.fsh"); + public static final ResourceLocation core_dynamiclights_vsh = new ResourceLocation("eagler:glsl/dynamiclights/core_dynamiclights.vsh"); + public static final ResourceLocation core_dynamiclights_fsh = new ResourceLocation("eagler:glsl/dynamiclights/core_dynamiclights.fsh"); + public static final ResourceLocation accel_particle_dynamiclights_vsh = new ResourceLocation("eagler:glsl/dynamiclights/accel_particle_dynamiclights.vsh"); + public static final ResourceLocation accel_particle_dynamiclights_fsh = new ResourceLocation("eagler:glsl/dynamiclights/accel_particle_dynamiclights.fsh"); - private static final Map sourceCache = new HashMap(); + private static final Map sourceCache = new HashMap<>(); private static boolean isHighP = false; @@ -171,10 +107,10 @@ public class ShaderSource { private static String getSourceFor(ResourceLocation path, int lineNumberOffset) { String str = sourceCache.get(path); - if (str == null) { + if(str == null) { try { str = loadSource(path, lineNumberOffset); - } catch (IOException ex) { + }catch(IOException ex) { str = ""; logger.error("Could not load shader source \"{}\"! {}", deferred_core_vsh, ex.toString()); logger.error("This may cause a NullPointerException when enabling certain features"); @@ -187,52 +123,44 @@ public class ShaderSource { private static String loadSource(ResourceLocation resourceLocation, int fileID) throws IOException { StringBuilder ret = new StringBuilder(); - try (InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation) - .getInputStream()) { + try(InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation).getInputStream()) { int lineCounter = 1; BufferedReader lineReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); String line; - while ((line = lineReader.readLine()) != null) { - if (line.startsWith("#line ")) { + while((line = lineReader.readLine()) != null) { + if(line.startsWith("#line ")) { String[] split = line.split("\\s+", 3); try { lineCounter = Integer.parseInt(split[1]); - } catch (NumberFormatException ex) { + }catch(NumberFormatException ex) { throw new IOException("Invalid preprocessor directive: " + line, ex); } ret.append("#line ").append(lineCounter).append(' ').append(fileID).append('\n'); - } else if (line.startsWith("#EAGLER ")) { + }else if(line.startsWith("#EAGLER ")) { StrTokenizer tokenizer = new StrTokenizer(line.substring(8)); tokenizer.setDelimiterChar(' '); tokenizer.setIgnoreEmptyTokens(true); tokenizer.setQuoteChar('\"'); - if (tokenizer.hasNext()) { + if(tokenizer.hasNext()) { String p1 = tokenizer.next(); - if (p1.equals("INCLUDE") && tokenizer.hasNext()) { + if(p1.equals("INCLUDE") && tokenizer.hasNext()) { String fileIDStr = tokenizer.next(); - if (tokenizer.hasNext() && fileIDStr.charAt(0) == '(' - && fileIDStr.charAt(fileIDStr.length() - 1) == ')') { + if(tokenizer.hasNext() && fileIDStr.charAt(0) == '(' && fileIDStr.charAt(fileIDStr.length() - 1) == ')') { String includePath = tokenizer.next(); - if (!tokenizer.hasNext()) { // ignore if there are extra arguments + if(!tokenizer.hasNext()) { // ignore if there are extra arguments int newFileId = -1; try { newFileId = Integer.parseInt(fileIDStr.substring(1, fileIDStr.length() - 1)); - } catch (NumberFormatException ex) { + }catch(NumberFormatException ex) { } - if (newFileId != -1) { + if(newFileId != -1) { newFileId += fileID * 100; - ret.append('\n').append( - "////////////////////////////////////////////////////////////////////") - .append('\n'); + ret.append('\n').append("////////////////////////////////////////////////////////////////////").append('\n'); ret.append("//" + line).append('\n'); ret.append("#line 1 ").append(newFileId).append('\n'); - ret.append(getSourceFor(new ResourceLocation(includePath), newFileId)) - .append('\n'); - ret.append( - "////////////////////////////////////////////////////////////////////") - .append('\n'); - ret.append("#line ").append(lineCounter - 1).append(' ').append(fileID) - .append('\n').append('\n'); + ret.append(getSourceFor(new ResourceLocation(includePath), newFileId)).append('\n'); + ret.append("////////////////////////////////////////////////////////////////////").append('\n'); + ret.append("#line ").append(lineCounter - 1).append(' ').append(fileID).append('\n').append('\n'); ++lineCounter; continue; } @@ -242,9 +170,9 @@ public class ShaderSource { } logger.error("Skipping invalid preprocessor directive: " + line); ret.append("// [INVALID]: " + line).append('\n'); - } else if (isHighP && line.startsWith("precision")) { + }else if(isHighP && line.startsWith("precision")) { ret.append(line.replace("lowp", "highp").replace("mediump", "highp")).append('\n'); - } else { + }else { ret.append(line).append('\n'); } ++lineCounter; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java index b095929..a2074e2 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java @@ -1,7 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture; import java.io.IOException; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; @@ -121,7 +120,7 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite { this.animationMetadata = meta; } else { - ArrayList arraylist = Lists.newArrayList(); + List arraylist = Lists.newArrayList(); for (int l1 = 0; l1 < j1; ++l1) { this.frameTextureDataPBR[0].add(getFrameTextureData(aint[0], k1, l, l1)); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EmissiveItems.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EmissiveItems.java index ef2fdb4..8e9eb1e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EmissiveItems.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EmissiveItems.java @@ -34,7 +34,7 @@ public class EmissiveItems implements IResourceManagerReloadListener { private static final Logger logger = LogManager.getLogger("EmissiveItemsCSV"); - private static final Map entries = new HashMap(); + private static final Map entries = new HashMap<>(); public static float[] getItemEmission(ItemStack itemStack) { return getItemEmission(itemStack.getItem(), itemStack.getItemDamage()); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRMaterialConstants.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRMaterialConstants.java index 4bd425c..88fc72e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRMaterialConstants.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRMaterialConstants.java @@ -34,7 +34,7 @@ public class PBRMaterialConstants implements IResourceManagerReloadListener { public static final Logger logger = LogManager.getLogger("PBRMaterialConstants"); public final ResourceLocation resourceLocation; - public final Map spriteNameToMaterialConstants = new HashMap(); + public final Map spriteNameToMaterialConstants = new HashMap<>(); public int defaultMaterial = 0x00000A77; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightBucketLoader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightBucketLoader.java index 6b56bc3..5a17d1f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightBucketLoader.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightBucketLoader.java @@ -19,276 +19,273 @@ import net.minecraft.util.MathHelper; /** * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class DynamicLightBucketLoader { - public IBufferGL buffer_chunkLightingData; - private ByteBuffer chunkLightingDataCopyBuffer; - private boolean isChunkLightingEnabled = false; - public ListSerial currentBoundLightSourceBucket; + public IBufferGL buffer_chunkLightingData; + public IBufferGL buffer_chunkLightingDataZero; + private ByteBuffer chunkLightingDataCopyBuffer; + public ListSerial currentBoundLightSourceBucket; - public final ListSerial[] lightSourceBuckets; - public ListSerial currentLightSourceBucket; + public final ListSerial[] lightSourceBuckets; + private final int[] lightSourceBucketsSerials; + private final int[] lightSourceRenderPosSerials; + public ListSerial currentLightSourceBucket; + private int currentLightSourceBucketId = -1; + private int lightingBufferSliceLength = -1; - public static final int MAX_LIGHTS_PER_CHUNK = 12; + public static final int MAX_LIGHTS_PER_CHUNK = 12; + public static final int LIGHTING_BUFFER_LENGTH = 16 * MAX_LIGHTS_PER_CHUNK + 16; - private final int lightSourceBucketsWidth; - private final int lightSourceBucketsHeight; + private final int lightSourceBucketsWidth; + private final int lightSourceBucketsHeight; - private double currentRenderX = 0.0; - private double currentRenderY = 0.0; - private double currentRenderZ = 0.0; + private double currentRenderX = 0.0; + private double currentRenderY = 0.0; + private double currentRenderZ = 0.0; + private int currentRenderPosSerial = 0; - public DynamicLightBucketLoader() { - this.lightSourceBucketsWidth = 5; - this.lightSourceBucketsHeight = 3; - int cnt = 5 * 3 * 5; - this.lightSourceBuckets = new ListSerial[cnt]; - } + public DynamicLightBucketLoader() { + this.lightSourceBucketsWidth = 5; + this.lightSourceBucketsHeight = 3; + int cnt = 5 * 3 * 5; + this.lightSourceBuckets = new ListSerial[cnt]; + this.lightSourceBucketsSerials = new int[cnt]; + this.lightSourceRenderPosSerials = new int[cnt]; + } - public void initialize() { - destroy(); + public void initialize() { + destroy(); + + int alignment = EaglercraftGPU.getUniformBufferOffsetAlignment(); + lightingBufferSliceLength = MathHelper.ceiling_float_int((float)LIGHTING_BUFFER_LENGTH / (float)alignment) * alignment; + + chunkLightingDataCopyBuffer = EagRuntime.allocateByteBuffer(LIGHTING_BUFFER_LENGTH); + for(int i = 0; i < LIGHTING_BUFFER_LENGTH; i += 4) { + chunkLightingDataCopyBuffer.putInt(0); + } + chunkLightingDataCopyBuffer.flip(); + + buffer_chunkLightingData = _wglGenBuffers(); + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); + int cnt = lightSourceBucketsWidth * lightSourceBucketsHeight * lightSourceBucketsWidth; + _wglBufferData(_GL_UNIFORM_BUFFER, cnt * lightingBufferSliceLength, GL_DYNAMIC_DRAW); + + buffer_chunkLightingDataZero = _wglGenBuffers(); + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingDataZero); + _wglBufferData(_GL_UNIFORM_BUFFER, chunkLightingDataCopyBuffer, GL_STATIC_DRAW); + + for(int i = 0; i < this.lightSourceBuckets.length; ++i) { + this.lightSourceBuckets[i] = new ArrayListSerial<>(16); + this.lightSourceBucketsSerials[i] = -1; + this.lightSourceRenderPosSerials[i] = -1; + } + } - buffer_chunkLightingData = _wglGenBuffers(); - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - int lightingDataLength = 4 * MAX_LIGHTS_PER_CHUNK + 4; - chunkLightingDataCopyBuffer = EagRuntime.allocateByteBuffer(lightingDataLength << 2); - for (int i = 0; i < lightingDataLength; ++i) { - chunkLightingDataCopyBuffer.putInt(0); - } - chunkLightingDataCopyBuffer.flip(); - _wglBufferData(_GL_UNIFORM_BUFFER, chunkLightingDataCopyBuffer, GL_DYNAMIC_DRAW); + public void clearBuckets() { + for(int i = 0; i < this.lightSourceBuckets.length; ++i) { + this.lightSourceBuckets[i].clear(); + } + } - for (int i = 0; i < this.lightSourceBuckets.length; ++i) { - this.lightSourceBuckets[i] = new ArrayListSerial(16); - } - } + public void bindLightSourceBucket(int relativeBlockX, int relativeBlockY, int relativeBlockZ, int uboIndex) { + int hw = lightSourceBucketsWidth / 2; + int hh = lightSourceBucketsHeight / 2; + int bucketX = (relativeBlockX >> 4) + hw; + int bucketY = (relativeBlockY >> 4) + hh; + int bucketZ = (relativeBlockZ >> 4) + hw; + if(bucketX >= 0 && bucketY >= 0 && bucketZ >= 0 && bucketX < lightSourceBucketsWidth + && bucketY < lightSourceBucketsHeight && bucketZ < lightSourceBucketsWidth) { + currentLightSourceBucketId = bucketY * lightSourceBucketsWidth * lightSourceBucketsWidth + + bucketZ * lightSourceBucketsWidth + bucketX; + currentLightSourceBucket = lightSourceBuckets[currentLightSourceBucketId]; + int ser = currentLightSourceBucket.getEaglerSerial(); + int max = currentLightSourceBucket.size(); + if(max > 0) { + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); + int offset = currentLightSourceBucketId * lightingBufferSliceLength; + if (lightSourceBucketsSerials[currentLightSourceBucketId] != ser + || lightSourceRenderPosSerials[currentLightSourceBucketId] != currentRenderPosSerial) { + lightSourceBucketsSerials[currentLightSourceBucketId] = ser; + lightSourceRenderPosSerials[currentLightSourceBucketId] = currentRenderPosSerial; + if(max > MAX_LIGHTS_PER_CHUNK) { + max = MAX_LIGHTS_PER_CHUNK; + } + chunkLightingDataCopyBuffer.clear(); + chunkLightingDataCopyBuffer.putInt(max); + chunkLightingDataCopyBuffer.putInt(0); //padding + chunkLightingDataCopyBuffer.putInt(0); //padding + chunkLightingDataCopyBuffer.putInt(0); //padding + for(int i = 0; i < max; ++i) { + DynamicLightInstance dl = currentLightSourceBucket.get(i); + chunkLightingDataCopyBuffer.putFloat((float)(dl.posX - currentRenderX)); + chunkLightingDataCopyBuffer.putFloat((float)(dl.posY - currentRenderY)); + chunkLightingDataCopyBuffer.putFloat((float)(dl.posZ - currentRenderZ)); + chunkLightingDataCopyBuffer.putFloat(dl.radius); + } + chunkLightingDataCopyBuffer.flip(); + _wglBufferSubData(_GL_UNIFORM_BUFFER, offset, chunkLightingDataCopyBuffer); + } + EaglercraftGPU.bindUniformBufferRange(uboIndex, buffer_chunkLightingData, offset, LIGHTING_BUFFER_LENGTH); + }else { + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingDataZero); + EaglercraftGPU.bindUniformBufferRange(uboIndex, buffer_chunkLightingDataZero, 0, LIGHTING_BUFFER_LENGTH); + } + }else { + currentLightSourceBucketId = -1; + currentLightSourceBucket = null; + EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingDataZero); + EaglercraftGPU.bindUniformBufferRange(uboIndex, buffer_chunkLightingDataZero, 0, LIGHTING_BUFFER_LENGTH); + } + } - public void clearBuckets() { - for (int i = 0; i < this.lightSourceBuckets.length; ++i) { - this.lightSourceBuckets[i].clear(); - } - } + public ListSerial getLightSourceBucketRelativeChunkCoords(int cx, int cy, int cz) { + int hw = lightSourceBucketsWidth / 2; + int hh = lightSourceBucketsHeight / 2; + cx += hw; + cy += hh; + cz += hw; + if(cx < 0 || cx >= lightSourceBucketsWidth || cy < 0 || cy >= lightSourceBucketsHeight || cz < 0 + || cz >= lightSourceBucketsWidth) { + return null; + }else { + return lightSourceBuckets[cy * lightSourceBucketsWidth * lightSourceBucketsWidth + + cz * lightSourceBucketsWidth + cx]; + } + } - public void loadLightSourceBucket(int relativeBlockX, int relativeBlockY, int relativeBlockZ) { - int hw = lightSourceBucketsWidth / 2; - int hh = lightSourceBucketsHeight / 2; - int bucketX = (relativeBlockX >> 4) + hw; - int bucketY = (relativeBlockY >> 4) + hh; - int bucketZ = (relativeBlockZ >> 4) + hw; - if (bucketX >= 0 && bucketY >= 0 && bucketZ >= 0 && bucketX < lightSourceBucketsWidth - && bucketY < lightSourceBucketsHeight && bucketZ < lightSourceBucketsWidth) { - currentLightSourceBucket = lightSourceBuckets[bucketY * lightSourceBucketsWidth * lightSourceBucketsWidth - + bucketZ * lightSourceBucketsWidth + bucketX]; - } else { - currentLightSourceBucket = null; - } - updateLightSourceUBO(); - } + public void addLightSourceToBucket(int cx, int cy, int cz, DynamicLightInstance dl) { + ListSerial lst = getLightSourceBucketRelativeChunkCoords(cx, cy, cz); + if(lst != null) { + lst.add(dl); + } + } - public ListSerial getLightSourceBucketRelativeChunkCoords(int cx, int cy, int cz) { - int hw = lightSourceBucketsWidth / 2; - int hh = lightSourceBucketsHeight / 2; - cx += hw; - cy += hh; - cz += hw; - if (cx < 0 || cx >= lightSourceBucketsWidth || cy < 0 || cy >= lightSourceBucketsHeight || cz < 0 - || cz >= lightSourceBucketsWidth) { - return null; - } else { - return lightSourceBuckets[cy * lightSourceBucketsWidth * lightSourceBucketsWidth - + cz * lightSourceBucketsWidth + cx]; - } - } + public void bucketLightSource(float x, float y, float z, DynamicLightInstance dl) { + int bucketX = MathHelper.floor_float(x / 16.0f); + int bucketY = MathHelper.floor_float(y / 16.0f); + int bucketZ = MathHelper.floor_float(z / 16.0f); + addLightSourceToBucket(bucketX, bucketY, bucketZ, dl); + int minX = bucketX, maxX = bucketX; + int minY = bucketY, maxY = bucketY; + int minZ = bucketZ, maxZ = bucketZ; + float lightLocalX = x - (bucketX << 4); + float lightLocalY = y - (bucketY << 4); + float lightLocalZ = z - (bucketZ << 4); + float radius = dl.radius; + boolean outOfBounds = false; + if(lightLocalX - radius < 0.0f) { + minX -= 1; + outOfBounds = true; + addLightSourceToBucket(bucketX - 1, bucketY, bucketZ, dl); + } + if(lightLocalY - radius < 0.0f) { + minY -= 1; + outOfBounds = true; + addLightSourceToBucket(bucketX, bucketY - 1, bucketZ, dl); + } + if(lightLocalZ - radius < 0.0f) { + minZ -= 1; + outOfBounds = true; + addLightSourceToBucket(bucketX, bucketY, bucketZ - 1, dl); + } + if(lightLocalX + radius >= 16.0f) { + maxX += 1; + outOfBounds = true; + addLightSourceToBucket(bucketX + 1, bucketY, bucketZ, dl); + } + if(lightLocalY + radius >= 16.0f) { + maxY += 1; + outOfBounds = true; + addLightSourceToBucket(bucketX, bucketY + 1, bucketZ, dl); + } + if(lightLocalZ + radius >= 16.0f) { + maxZ += 1; + outOfBounds = true; + addLightSourceToBucket(bucketX, bucketY, bucketZ + 1, dl); + } + if(!outOfBounds) { + return; + } + radius *= radius; + for(int yy = minY; yy <= maxY; ++yy) { + for(int zz = minZ; zz <= maxZ; ++zz) { + for(int xx = minX; xx <= maxX; ++xx) { + if((xx == bucketX ? 1 : 0) + (yy == bucketY ? 1 : 0) + (zz == bucketZ ? 1 : 0) > 1) { + continue; + } + List lst = getLightSourceBucketRelativeChunkCoords(xx, yy, zz); + if(lst != null) { + int bucketBoundsX = xx << 4; + int bucketBoundsY = yy << 4; + int bucketBoundsZ = zz << 4; + if (EaglerDeferredPipeline.testAabSphere(bucketBoundsX, bucketBoundsY, bucketBoundsZ, + bucketBoundsX + 16, bucketBoundsY + 16, bucketBoundsZ + 16, x, y, z, radius)) { + lst.add(dl); + } + } + } + } + } + } - public void addLightSourceToBucket(int cx, int cy, int cz, DynamicLightInstance dl) { - ListSerial lst = getLightSourceBucketRelativeChunkCoords(cx, cy, cz); - if (lst != null) { - lst.add(dl); - } - } + public void truncateOverflowingBuffers() { + for(int i = 0; i < lightSourceBuckets.length; ++i) { + List lst = lightSourceBuckets[i]; + int k = lst.size(); + if(k > MAX_LIGHTS_PER_CHUNK) { + lst.sort(comparatorLightRadius); + for(int l = MAX_LIGHTS_PER_CHUNK - 1; l >= MAX_LIGHTS_PER_CHUNK; --l) { + lst.remove(l); + } + } + } + } - public void bucketLightSource(float x, float y, float z, DynamicLightInstance dl) { - int bucketX = MathHelper.floor_float(x / 16.0f); - int bucketY = MathHelper.floor_float(y / 16.0f); - int bucketZ = MathHelper.floor_float(z / 16.0f); - addLightSourceToBucket(bucketX, bucketY, bucketZ, dl); - int minX = bucketX, maxX = bucketX; - int minY = bucketY, maxY = bucketY; - int minZ = bucketZ, maxZ = bucketZ; - float lightLocalX = x - (bucketX << 4); - float lightLocalY = y - (bucketY << 4); - float lightLocalZ = z - (bucketZ << 4); - float radius = dl.radius; - boolean outOfBounds = false; - if (lightLocalX - radius < 0.0f) { - minX -= 1; - outOfBounds = true; - addLightSourceToBucket(bucketX - 1, bucketY, bucketZ, dl); - } - if (lightLocalY - radius < 0.0f) { - minY -= 1; - outOfBounds = true; - addLightSourceToBucket(bucketX, bucketY - 1, bucketZ, dl); - } - if (lightLocalZ - radius < 0.0f) { - minZ -= 1; - outOfBounds = true; - addLightSourceToBucket(bucketX, bucketY, bucketZ - 1, dl); - } - if (lightLocalX + radius >= 16.0f) { - maxX += 1; - outOfBounds = true; - addLightSourceToBucket(bucketX + 1, bucketY, bucketZ, dl); - } - if (lightLocalY + radius >= 16.0f) { - maxY += 1; - outOfBounds = true; - addLightSourceToBucket(bucketX, bucketY + 1, bucketZ, dl); - } - if (lightLocalZ + radius >= 16.0f) { - maxZ += 1; - outOfBounds = true; - addLightSourceToBucket(bucketX, bucketY, bucketZ + 1, dl); - } - if (!outOfBounds) { - return; - } - radius *= radius; - for (int yy = minY; yy <= maxY; ++yy) { - for (int zz = minZ; zz <= maxZ; ++zz) { - for (int xx = minX; xx <= maxX; ++xx) { - if ((xx == bucketX ? 1 : 0) + (yy == bucketY ? 1 : 0) + (zz == bucketZ ? 1 : 0) > 1) { - continue; - } - List lst = getLightSourceBucketRelativeChunkCoords(xx, yy, zz); - if (lst != null) { - int bucketBoundsX = xx << 4; - int bucketBoundsY = yy << 4; - int bucketBoundsZ = zz << 4; - if (EaglerDeferredPipeline.testAabSphere(bucketBoundsX, bucketBoundsY, bucketBoundsZ, - bucketBoundsX + 16, bucketBoundsY + 16, bucketBoundsZ + 16, x, y, z, radius)) { - lst.add(dl); - } - } - } - } - } - } + private static final Comparator comparatorLightRadius = (l1, l2) -> { + return l1.radius < l2.radius ? 1 : -1; + }; - public void truncateOverflowingBuffers() { - for (int i = 0; i < this.lightSourceBuckets.length; ++i) { - List lst = this.lightSourceBuckets[i]; - int k = lst.size(); - if (k > MAX_LIGHTS_PER_CHUNK) { - lst.sort(comparatorLightRadius); - for (int l = MAX_LIGHTS_PER_CHUNK - 1; l >= MAX_LIGHTS_PER_CHUNK; --l) { - lst.remove(l); - } - } - } - } + public void setRenderPos(double currentRenderX, double currentRenderY, double currentRenderZ) { + if (this.currentRenderX != currentRenderX || this.currentRenderY != currentRenderY + || this.currentRenderZ != currentRenderZ || this.currentRenderPosSerial == 0) { + this.currentRenderX = currentRenderX; + this.currentRenderY = currentRenderY; + this.currentRenderZ = currentRenderZ; + ++this.currentRenderPosSerial; + } + } - public void updateLightSourceUBO() { - if (currentLightSourceBucket == null) { - currentBoundLightSourceBucket = null; - if (isChunkLightingEnabled) { - isChunkLightingEnabled = false; - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - chunkLightingDataCopyBuffer.clear(); - chunkLightingDataCopyBuffer.putInt(0); - chunkLightingDataCopyBuffer.flip(); - _wglBufferSubData(_GL_UNIFORM_BUFFER, 0, chunkLightingDataCopyBuffer); - } - } else { - boolean isNew; - if (!isChunkLightingEnabled) { - isChunkLightingEnabled = true; - isNew = true; - } else { - isNew = currentLightSourceBucket != currentBoundLightSourceBucket; - } - currentBoundLightSourceBucket = currentLightSourceBucket; - if (isNew || currentBoundLightSourceBucket.eaglerCheck()) { - populateLightSourceUBOFromBucket(currentBoundLightSourceBucket); - currentBoundLightSourceBucket.eaglerResetCheck(); - } - } - } - - private static final Comparator comparatorLightRadius = (l1, l2) -> { - return l1.radius < l2.radius ? 1 : -1; - }; - - private void populateLightSourceUBOFromBucket(List lights) { - int max = lights.size(); - if (max > MAX_LIGHTS_PER_CHUNK) { - // tmpListLights.clear(); - // tmpListLights.addAll(lights); - // lights = tmpListLights; - // lights.sort(comparatorLightRadius); - max = MAX_LIGHTS_PER_CHUNK; - } - chunkLightingDataCopyBuffer.clear(); - chunkLightingDataCopyBuffer.putInt(max); - if (max > 0) { - chunkLightingDataCopyBuffer.putInt(0); // padding - chunkLightingDataCopyBuffer.putInt(0); // padding - chunkLightingDataCopyBuffer.putInt(0); // padding - for (int i = 0; i < max; ++i) { - DynamicLightInstance dl = lights.get(i); - chunkLightingDataCopyBuffer.putFloat((float) (dl.posX - currentRenderX)); - chunkLightingDataCopyBuffer.putFloat((float) (dl.posY - currentRenderY)); - chunkLightingDataCopyBuffer.putFloat((float) (dl.posZ - currentRenderZ)); - chunkLightingDataCopyBuffer.putFloat(dl.radius); - } - } - chunkLightingDataCopyBuffer.flip(); - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - _wglBufferSubData(_GL_UNIFORM_BUFFER, 0, chunkLightingDataCopyBuffer); - } - - public void setRenderPos(double currentRenderX, double currentRenderY, double currentRenderZ) { - this.currentRenderX = currentRenderX; - this.currentRenderY = currentRenderY; - this.currentRenderZ = currentRenderZ; - } - - public void bindUniformBuffer(int index) { - EaglercraftGPU.bindGLUniformBuffer(buffer_chunkLightingData); - EaglercraftGPU.bindUniformBufferRange(index, buffer_chunkLightingData, 0, - chunkLightingDataCopyBuffer.capacity()); - } - - public void destroy() { - if (chunkLightingDataCopyBuffer != null) { - EagRuntime.freeByteBuffer(chunkLightingDataCopyBuffer); - chunkLightingDataCopyBuffer = null; - } - if (buffer_chunkLightingData != null) { - _wglDeleteBuffers(buffer_chunkLightingData); - buffer_chunkLightingData = null; - } - for (int i = 0; i < this.lightSourceBuckets.length; ++i) { - this.lightSourceBuckets[i] = null; - } - } -} \ No newline at end of file + public void destroy() { + if(chunkLightingDataCopyBuffer != null) { + EagRuntime.freeByteBuffer(chunkLightingDataCopyBuffer); + chunkLightingDataCopyBuffer = null; + } + if(buffer_chunkLightingData != null) { + _wglDeleteBuffers(buffer_chunkLightingData); + buffer_chunkLightingData = null; + } + if(buffer_chunkLightingDataZero != null) { + _wglDeleteBuffers(buffer_chunkLightingDataZero); + buffer_chunkLightingDataZero = null; + } + for(int i = 0; i < lightSourceBuckets.length; ++i) { + lightSourceBuckets[i] = null; + lightSourceBucketsSerials[i] = -1; + lightSourceRenderPosSerials[i] = -1; + } + currentLightSourceBucket = null; + currentLightSourceBucketId = -1; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsStateManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsStateManager.java index 3d4f82c..bf47ed1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsStateManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsStateManager.java @@ -5,6 +5,8 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionPipeline; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; @@ -15,173 +17,167 @@ import net.minecraft.util.MathHelper; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class DynamicLightsStateManager { - static final DynamicLightsPipelineCompiler deferredExtPipeline = new DynamicLightsPipelineCompiler(); - private static List lightInstancePool = new ArrayList(); - private static int instancePoolIndex = 0; - private static int maxListLengthTracker = 0; - static final List lightRenderList = new LinkedList(); - static final Matrix4f inverseViewMatrix = new Matrix4f(); - static int inverseViewMatrixSerial = 0; - static DynamicLightBucketLoader bucketLoader = null; - static DynamicLightsAcceleratedEffectRenderer accelParticleRenderer = null; - static int lastTotal = 0; - private static long lastTick = 0l; + static final DynamicLightsPipelineCompiler deferredExtPipeline = new DynamicLightsPipelineCompiler(); + private static List lightInstancePool = new ArrayList<>(); + private static int instancePoolIndex = 0; + private static int maxListLengthTracker = 0; + static final List lightRenderList = new LinkedList<>(); + static final Matrix4f inverseViewMatrix = new Matrix4f(); + static int inverseViewMatrixSerial = 0; + static DynamicLightBucketLoader bucketLoader = null; + static DynamicLightsAcceleratedEffectRenderer accelParticleRenderer = null; + static int lastTotal = 0; + private static long lastTick = 0l; - public static final void enableDynamicLightsRender() { - if (bucketLoader == null) { - bucketLoader = new DynamicLightBucketLoader(); - bucketLoader.initialize(); - bucketLoader.bindUniformBuffer(0); - FixedFunctionPipeline.loadExtensionPipeline(deferredExtPipeline); - } - if (accelParticleRenderer == null) { - accelParticleRenderer = new DynamicLightsAcceleratedEffectRenderer(); - accelParticleRenderer.initialize(); - } - lightRenderList.clear(); - instancePoolIndex = 0; - maxListLengthTracker = 0; - } + public static final void enableDynamicLightsRender() { + if(bucketLoader == null) { + bucketLoader = new DynamicLightBucketLoader(); + bucketLoader.initialize(); + bucketLoader.bindLightSourceBucket(-999, -999, -999, 0); + FixedFunctionPipeline.loadExtensionPipeline(deferredExtPipeline); + } + if(accelParticleRenderer == null) { + accelParticleRenderer = new DynamicLightsAcceleratedEffectRenderer(); + accelParticleRenderer.initialize(); + } + lightRenderList.clear(); + instancePoolIndex = 0; + maxListLengthTracker = 0; + } - public static final void bindAcceleratedEffectRenderer(EffectRenderer renderer) { - renderer.acceleratedParticleRenderer = accelParticleRenderer; - } + public static final void bindAcceleratedEffectRenderer(EffectRenderer renderer) { + renderer.acceleratedParticleRenderer = accelParticleRenderer; + } - public static final void disableDynamicLightsRender(boolean unloadPipeline) { - if (bucketLoader != null) { - bucketLoader.destroy(); - bucketLoader = null; - if (unloadPipeline) { - FixedFunctionPipeline.loadExtensionPipeline(null); - } - } - if (accelParticleRenderer != null) { - accelParticleRenderer.destroy(); - accelParticleRenderer = null; - } - destroyAll(); - lightRenderList.clear(); - instancePoolIndex = 0; - maxListLengthTracker = 0; - } + public static final void disableDynamicLightsRender(boolean unloadPipeline) { + if(bucketLoader != null) { + bucketLoader.destroy(); + bucketLoader = null; + if(unloadPipeline) { + FixedFunctionPipeline.loadExtensionPipeline(null); + } + } + if(accelParticleRenderer != null) { + accelParticleRenderer.destroy(); + accelParticleRenderer = null; + } + destroyAll(); + lightRenderList.clear(); + instancePoolIndex = 0; + maxListLengthTracker = 0; + } - public static final boolean isDynamicLightsRender() { - return bucketLoader != null; - } + public static final boolean isDynamicLightsRender() { + return bucketLoader != null; + } - public static final boolean isInDynamicLightsPass() { - return GlStateManager.isExtensionPipeline() && bucketLoader != null; - } + public static final boolean isInDynamicLightsPass() { + return GlStateManager.isExtensionPipeline() && bucketLoader != null; + } - public static final void reportForwardRenderObjectPosition(int centerX, int centerY, int centerZ) { - if (bucketLoader != null) { - bucketLoader.loadLightSourceBucket(centerX, centerY, centerZ); - } - } + public static final void reportForwardRenderObjectPosition(int centerX, int centerY, int centerZ) { + if(bucketLoader != null) { + bucketLoader.bindLightSourceBucket(centerX, centerY, centerZ, 0); + } + } - public static final void reportForwardRenderObjectPosition2(float x, float y, float z) { - if (bucketLoader != null) { - float posX = (float) ((x + TileEntityRendererDispatcher.staticPlayerX) - - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerX / 16.0) << 4)); - float posY = (float) ((y + TileEntityRendererDispatcher.staticPlayerY) - - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerY / 16.0) << 4)); - float posZ = (float) ((z + TileEntityRendererDispatcher.staticPlayerZ) - - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerZ / 16.0) << 4)); - bucketLoader.loadLightSourceBucket((int) posX, (int) posY, (int) posZ); - } - } + public static final void reportForwardRenderObjectPosition2(float x, float y, float z) { + if(bucketLoader != null) { + float posX = (float)((x + TileEntityRendererDispatcher.staticPlayerX) - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerX / 16.0) << 4)); + float posY = (float)((y + TileEntityRendererDispatcher.staticPlayerY) - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerY / 16.0) << 4)); + float posZ = (float)((z + TileEntityRendererDispatcher.staticPlayerZ) - (MathHelper.floor_double(TileEntityRendererDispatcher.staticPlayerZ / 16.0) << 4)); + bucketLoader.bindLightSourceBucket((int)posX, (int)posY, (int)posZ, 0); + } + } - public static final void renderDynamicLight(String lightName, double posX, double posY, double posZ, float radius) { - if (bucketLoader != null) { - DynamicLightInstance dl; - if (instancePoolIndex < lightInstancePool.size()) { - dl = lightInstancePool.get(instancePoolIndex); - } else { - lightInstancePool.add(dl = new DynamicLightInstance()); - } - ++instancePoolIndex; - dl.updateLight(posX, posY, posZ, radius); - lightRenderList.add(dl); - } - } + public static final void renderDynamicLight(String lightName, double posX, double posY, double posZ, float radius) { + if(bucketLoader != null) { + DynamicLightInstance dl; + if(instancePoolIndex < lightInstancePool.size()) { + dl = lightInstancePool.get(instancePoolIndex); + }else { + lightInstancePool.add(dl = new DynamicLightInstance()); + } + ++instancePoolIndex; + dl.updateLight(posX, posY, posZ, radius); + lightRenderList.add(dl); + } + } - public static final void clearRenderList() { - if (instancePoolIndex > maxListLengthTracker) { - maxListLengthTracker = instancePoolIndex; - } - lightRenderList.clear(); - instancePoolIndex = 0; - } + public static final void clearRenderList() { + if(instancePoolIndex > maxListLengthTracker) { + maxListLengthTracker = instancePoolIndex; + } + lightRenderList.clear(); + instancePoolIndex = 0; + } - public static final void commitLightSourceBuckets(double renderPosX, double renderPosY, double renderPosZ) { - lastTotal = lightRenderList.size(); - if (bucketLoader != null) { - bucketLoader.clearBuckets(); - int entityChunkOriginX = MathHelper.floor_double(renderPosX / 16.0) << 4; - int entityChunkOriginY = MathHelper.floor_double(renderPosY / 16.0) << 4; - int entityChunkOriginZ = MathHelper.floor_double(renderPosZ / 16.0) << 4; - Iterator itr = lightRenderList.iterator(); - while (itr.hasNext()) { - DynamicLightInstance dl = itr.next(); - float lightChunkPosX = (float) (dl.posX - entityChunkOriginX); - float lightChunkPosY = (float) (dl.posY - entityChunkOriginY); - float lightChunkPosZ = (float) (dl.posZ - entityChunkOriginZ); - bucketLoader.bucketLightSource(lightChunkPosX, lightChunkPosY, lightChunkPosZ, dl); - } - bucketLoader.setRenderPos(renderPosX, renderPosY, renderPosZ); - bucketLoader.truncateOverflowingBuffers(); - } - updateTimers(); - clearRenderList(); - } + public static final void commitLightSourceBuckets(double renderPosX, double renderPosY, double renderPosZ) { + lastTotal = lightRenderList.size(); + if(bucketLoader != null) { + bucketLoader.clearBuckets(); + int entityChunkOriginX = MathHelper.floor_double(renderPosX / 16.0) << 4; + int entityChunkOriginY = MathHelper.floor_double(renderPosY / 16.0) << 4; + int entityChunkOriginZ = MathHelper.floor_double(renderPosZ / 16.0) << 4; + Iterator itr = lightRenderList.iterator(); + while(itr.hasNext()) { + DynamicLightInstance dl = itr.next(); + float lightChunkPosX = (float)(dl.posX - entityChunkOriginX); + float lightChunkPosY = (float)(dl.posY - entityChunkOriginY); + float lightChunkPosZ = (float)(dl.posZ - entityChunkOriginZ); + bucketLoader.bucketLightSource(lightChunkPosX, lightChunkPosY, lightChunkPosZ, dl); + } + bucketLoader.setRenderPos(renderPosX, renderPosY, renderPosZ); + bucketLoader.truncateOverflowingBuffers(); + } + updateTimers(); + clearRenderList(); + } - public static final void setupInverseViewMatrix() { - Matrix4f.invert(GlStateManager.getModelViewReference(), inverseViewMatrix); - inverseViewMatrixSerial = GlStateManager.getModelViewSerial(); - } + public static final void setupInverseViewMatrix() { + Matrix4f.invert(GlStateManager.getModelViewReference(), inverseViewMatrix); + inverseViewMatrixSerial = GlStateManager.getModelViewSerial(); + } - private static final void updateTimers() { - long millis = System.currentTimeMillis(); - if (millis - lastTick > 5000l) { - lastTick = millis; - if (maxListLengthTracker < (lightInstancePool.size() >> 1)) { - List newPool = new ArrayList(Math.max(maxListLengthTracker, 16)); - for (int i = 0; i < maxListLengthTracker; ++i) { - newPool.add(lightInstancePool.get(i)); - } - lightInstancePool = newPool; - } - maxListLengthTracker = 0; - } - } + private static final void updateTimers() { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastTick > 5000l) { + lastTick = millis; + if(maxListLengthTracker < (lightInstancePool.size() >> 1)) { + List newPool = new ArrayList<>(Math.max(maxListLengthTracker, 16)); + for(int i = 0; i < maxListLengthTracker; ++i) { + newPool.add(lightInstancePool.get(i)); + } + lightInstancePool = newPool; + } + maxListLengthTracker = 0; + } + } - public static final void destroyAll() { - lightInstancePool = new ArrayList(); - } + public static final void destroyAll() { + lightInstancePool = new ArrayList<>(); + } - public static String getF3String() { - return "DynamicLightsTotal: " + lastTotal; - } + public static String getF3String() { + return "DynamicLightsTotal: " + lastTotal; + } + + public static boolean isSupported() { + return EaglercraftGPU.checkOpenGLESVersion() >= 300; + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/GuiScreenContentWarning.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/GuiScreenContentWarning.java new file mode 100755 index 0000000..efb2636 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/GuiScreenContentWarning.java @@ -0,0 +1,63 @@ +package net.lax1dude.eaglercraft.v1_8.profanity_filter; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenContentWarning extends GuiScreen { + + private final GuiScreen cont; + private boolean enableState; + private GuiButton optButton; + + public GuiScreenContentWarning(GuiScreen cont) { + this.cont = cont; + } + + public void initGui() { + this.buttonList.clear(); + enableState = mc.gameSettings.enableProfanityFilter; + this.buttonList.add(optButton = new GuiButton(1, this.width / 2 - 100, this.height / 6 + 108, I18n.format("options.profanityFilterButton") + ": " + I18n.format(enableState ? "gui.yes" : "gui.no"))); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 138, I18n.format("gui.done"))); + } + + @Override + protected void actionPerformed(GuiButton parGuiButton) { + if(parGuiButton.id == 0) { + mc.gameSettings.enableProfanityFilter = enableState; + mc.gameSettings.hasShownProfanityFilter = true; + mc.gameSettings.saveOptions(); + mc.displayGuiScreen(cont); + }else if(parGuiButton.id == 1) { + enableState = !enableState; + optButton.displayString = I18n.format("options.profanityFilterButton") + ": " + I18n.format(enableState ? "gui.yes" : "gui.no"); + } + } + + public void drawScreen(int mx, int my, float pt) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + I18n.format("profanityFilterWarning.title"), this.width / 2, 50, 0xFF4444); + this.drawCenteredString(fontRendererObj, I18n.format("profanityFilterWarning.text0"), this.width / 2, 70, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("profanityFilterWarning.text1"), this.width / 2, 82, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("profanityFilterWarning.text2"), this.width / 2, 94, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("profanityFilterWarning.text4"), this.width / 2, 116, 0xCCCCCC); + super.drawScreen(mx, my, pt); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/LookAlikeUnicodeConv.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/LookAlikeUnicodeConv.java new file mode 100755 index 0000000..b2fd8af --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/LookAlikeUnicodeConv.java @@ -0,0 +1,1028 @@ +package net.lax1dude.eaglercraft.v1_8.profanity_filter; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class LookAlikeUnicodeConv { + + public static String convertString(String stringIn) { + char[] ret = null; + char c1, c2; + for(int i = 0, l = stringIn.length(); i < l; ++i) { + c1 = stringIn.charAt(i); + c2 = convertChar(c1); + if(c2 != 0) { + if(ret == null) { + ret = stringIn.toCharArray(); + } + ret[i] = c2; + } + } + return ret != null ? new String(ret) : stringIn; + } + + public static char convertChar(char charIn) { + switch (charIn) { + case 9313: + case 9333: + case 65298: + return '2'; + case 9315: + case 9335: + case 65300: + return '4'; + case 9316: + case 9336: + case 65301: + return '5'; + case 9317: + case 9337: + case 65302: + return '6'; + case 9319: + case 9339: + case 65304: + return '8'; + case 9320: + case 9340: + case 65305: + return '9'; + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + case 256: + case 258: + case 260: + case 461: + case 478: + case 480: + case 506: + case 512: + case 514: + case 550: + case 570: + case 913: + case 1040: + case 1232: + case 1234: + case 7680: + case 7840: + case 7842: + case 7844: + case 7846: + case 7848: + case 7850: + case 7852: + case 7854: + case 7856: + case 7858: + case 7860: + case 7862: + case 7944: + case 7945: + case 7946: + case 7947: + case 7948: + case 7949: + case 7950: + case 7951: + case 8072: + case 8073: + case 8074: + case 8075: + case 8076: + case 8077: + case 8078: + case 8079: + case 8120: + case 8121: + case 8122: + case 8123: + case 8124: + case 9424: + case 65313: + return 'A'; + case 385: + case 386: + case 579: + case 914: + case 1042: + case 7682: + case 7684: + case 7686: + case 9425: + case 65314: + return 'B'; + case 199: + case 262: + case 264: + case 266: + case 268: + case 391: + case 571: + case 1057: + case 1194: + case 7688: + case 9426: + case 65315: + return 'C'; + case 270: + case 272: + case 393: + case 394: + case 7690: + case 7692: + case 7694: + case 7696: + case 7698: + case 9427: + case 65316: + return 'D'; + case 200: + case 201: + case 202: + case 203: + case 274: + case 276: + case 278: + case 280: + case 282: + case 516: + case 518: + case 552: + case 582: + case 904: + case 917: + case 7700: + case 7702: + case 7704: + case 7706: + case 7708: + case 7864: + case 7866: + case 7868: + case 7870: + case 7872: + case 7874: + case 7876: + case 7878: + case 7960: + case 7961: + case 7962: + case 7963: + case 7964: + case 7965: + case 8136: + case 8137: + case 9428: + case 65317: + return 'E'; + case 401: + case 7710: + case 9429: + case 65318: + return 'F'; + case 284: + case 286: + case 288: + case 290: + case 403: + case 484: + case 486: + case 500: + case 7712: + case 9430: + case 65319: + return 'G'; + case 292: + case 294: + case 542: + case 7714: + case 7716: + case 7718: + case 7720: + case 7722: + case 7976: + case 7977: + case 7978: + case 7979: + case 7980: + case 7981: + case 7982: + case 7983: + case 9431: + case 65320: + return 'H'; + case 204: + case 205: + case 206: + case 207: + case 296: + case 298: + case 300: + case 302: + case 304: + case 406: + case 407: + case 463: + case 520: + case 522: + case 906: + case 921: + case 938: + case 1030: + case 7724: + case 7726: + case 7880: + case 7882: + case 7992: + case 7993: + case 7994: + case 7995: + case 7996: + case 7997: + case 7998: + case 7999: + case 9432: + case 65321: + return 'I'; + case 308: + case 584: + case 1032: + case 9433: + case 65322: + return 'J'; + case 310: + case 408: + case 488: + case 922: + case 1036: + case 1050: + case 1178: + case 1180: + case 1182: + case 7728: + case 7730: + case 7732: + case 9434: + case 65323: + return 'K'; + case 313: + case 315: + case 317: + case 319: + case 321: + case 573: + case 7734: + case 7736: + case 7738: + case 7740: + case 9435: + case 65324: + return 'L'; + case 924: + case 7742: + case 7744: + case 7746: + case 9436: + case 65325: + return 'M'; + case 209: + case 323: + case 325: + case 327: + case 413: + case 504: + case 544: + case 925: + case 7748: + case 7750: + case 7752: + case 7754: + case 9437: + case 65326: + return 'N'; + case 48: + case 210: + case 211: + case 212: + case 213: + case 214: + case 332: + case 334: + case 336: + case 416: + case 465: + case 490: + case 492: + case 510: + case 524: + case 526: + case 554: + case 556: + case 558: + case 560: + case 908: + case 927: + case 1054: + case 1254: + case 7756: + case 7758: + case 7760: + case 7762: + case 7884: + case 7886: + case 7888: + case 7890: + case 7892: + case 7894: + case 7896: + case 7898: + case 7900: + case 7902: + case 7904: + case 7906: + case 8008: + case 8009: + case 8010: + case 8011: + case 8012: + case 8013: + case 8184: + case 8185: + case 9438: + case 65296: + case 65327: + return 'O'; + case 420: + case 929: + case 7764: + case 7766: + case 8172: + case 9439: + case 65328: + return 'P'; + case 9440: + case 65329: + return 'Q'; + case 340: + case 342: + case 344: + case 528: + case 530: + case 588: + case 7768: + case 7770: + case 7772: + case 7774: + case 9441: + case 65330: + return 'R'; + case 36: + case 346: + case 348: + case 350: + case 352: + case 536: + case 1029: + case 7776: + case 7778: + case 7780: + case 7782: + case 7784: + case 9442: + case 65331: + return 'S'; + case 354: + case 356: + case 358: + case 428: + case 430: + case 538: + case 574: + case 932: + case 7786: + case 7788: + case 7790: + case 7792: + case 9443: + case 65332: + return 'T'; + case 217: + case 218: + case 219: + case 220: + case 360: + case 362: + case 364: + case 366: + case 368: + case 370: + case 431: + case 467: + case 469: + case 471: + case 473: + case 475: + case 532: + case 534: + case 580: + case 1329: + case 1357: + case 7794: + case 7796: + case 7798: + case 7800: + case 7802: + case 7908: + case 7910: + case 7912: + case 7914: + case 7916: + case 7918: + case 7920: + case 9444: + case 65333: + return 'U'; + case 1140: + case 1142: + case 7804: + case 7806: + case 9445: + case 65334: + return 'V'; + case 372: + case 7808: + case 7810: + case 7812: + case 7814: + case 7816: + case 9446: + case 65335: + return 'W'; + case 935: + case 1061: + case 1202: + case 1276: + case 1278: + case 7818: + case 7820: + case 9447: + case 65336: + return 'X'; + case 221: + case 374: + case 376: + case 435: + case 562: + case 590: + case 933: + case 939: + case 7822: + case 7922: + case 7924: + case 7926: + case 7928: + case 8025: + case 8027: + case 8029: + case 8031: + case 8168: + case 8169: + case 8170: + case 8171: + case 9448: + case 65337: + return 'Y'; + case 377: + case 379: + case 381: + case 437: + case 548: + case 918: + case 7824: + case 7826: + case 7828: + case 9449: + case 65338: + return 'Z'; + case 64: + case 224: + case 225: + case 226: + case 227: + case 228: + case 229: + case 257: + case 259: + case 261: + case 462: + case 479: + case 481: + case 507: + case 513: + case 515: + case 551: + case 940: + case 945: + case 1072: + case 1233: + case 1235: + case 7681: + case 7834: + case 7841: + case 7843: + case 7845: + case 7847: + case 7849: + case 7851: + case 7853: + case 7855: + case 7857: + case 7859: + case 7861: + case 7863: + case 7936: + case 7937: + case 7938: + case 7939: + case 7940: + case 7941: + case 7942: + case 7943: + case 8048: + case 8049: + case 8064: + case 8065: + case 8066: + case 8067: + case 8068: + case 8069: + case 8070: + case 8071: + case 8112: + case 8113: + case 8114: + case 8115: + case 8116: + case 8118: + case 8119: + case 9372: + case 9398: + case 65345: + return 'a'; + case 384: + case 387: + case 388: + case 389: + case 595: + case 946: + case 7683: + case 7685: + case 7687: + case 9373: + case 9399: + case 65346: + return 'b'; + case 231: + case 263: + case 265: + case 267: + case 269: + case 392: + case 572: + case 1089: + case 7689: + case 9374: + case 9400: + case 65347: + return 'c'; + case 271: + case 273: + case 396: + case 598: + case 599: + case 7691: + case 7693: + case 7695: + case 7697: + case 7699: + case 9375: + case 9401: + case 65348: + return 'd'; + case 51: + case 232: + case 233: + case 234: + case 235: + case 275: + case 277: + case 279: + case 281: + case 283: + case 517: + case 519: + case 553: + case 583: + case 1239: + case 7701: + case 7703: + case 7705: + case 7707: + case 7709: + case 7865: + case 7867: + case 7869: + case 7871: + case 7873: + case 7875: + case 7877: + case 7879: + case 9314: + case 9334: + case 9376: + case 9402: + case 65299: + case 65349: + return 'e'; + case 402: + case 7711: + case 9377: + case 9403: + case 65350: + return 'f'; + case 285: + case 287: + case 289: + case 291: + case 485: + case 487: + case 7713: + case 9378: + case 9404: + case 65351: + return 'g'; + case 293: + case 295: + case 543: + case 614: + case 7715: + case 7717: + case 7719: + case 7721: + case 7723: + case 7830: + case 9379: + case 9405: + case 65352: + return 'h'; + case 33: + case 49: + case 236: + case 237: + case 238: + case 239: + case 297: + case 299: + case 301: + case 303: + case 464: + case 521: + case 523: + case 616: + case 617: + case 943: + case 953: + case 970: + case 1110: + case 7725: + case 7727: + case 7881: + case 7883: + case 9312: + case 9332: + case 9380: + case 9406: + case 65297: + case 65353: + return 'i'; + case 309: + case 496: + case 585: + case 669: + case 1112: + case 9381: + case 9407: + case 65354: + return 'j'; + case 311: + case 312: + case 409: + case 489: + case 954: + case 1116: + case 1179: + case 1181: + case 1183: + case 7729: + case 7731: + case 7733: + case 9382: + case 9408: + case 65355: + return 'k'; + case 314: + case 316: + case 318: + case 320: + case 322: + case 410: + case 619: + case 620: + case 621: + case 7735: + case 7737: + case 7739: + case 7741: + case 9383: + case 9409: + case 65356: + return 'l'; + case 625: + case 7743: + case 7745: + case 7747: + case 9384: + case 9410: + case 65357: + return 'm'; + case 241: + case 324: + case 326: + case 328: + case 329: + case 414: + case 505: + case 565: + case 626: + case 627: + case 7749: + case 7751: + case 7753: + case 7755: + case 9385: + case 9411: + case 65358: + return 'n'; + case 242: + case 243: + case 244: + case 245: + case 246: + case 248: + case 333: + case 335: + case 337: + case 417: + case 466: + case 491: + case 493: + case 511: + case 525: + case 527: + case 555: + case 557: + case 559: + case 561: + case 959: + case 972: + case 1086: + case 1255: + case 7757: + case 7759: + case 7761: + case 7763: + case 7885: + case 7887: + case 7889: + case 7891: + case 7893: + case 7895: + case 7897: + case 7899: + case 7901: + case 7903: + case 7905: + case 7907: + case 8000: + case 8001: + case 8002: + case 8003: + case 8004: + case 8005: + case 9386: + case 9412: + case 65359: + return 'o'; + case 421: + case 961: + case 7765: + case 7767: + case 9387: + case 9413: + case 65360: + return 'p'; + case 587: + case 672: + case 9388: + case 9414: + case 65361: + return 'q'; + case 341: + case 343: + case 345: + case 529: + case 531: + case 589: + case 7769: + case 7771: + case 7773: + case 7775: + case 9389: + case 9415: + case 65362: + return 'r'; + case 347: + case 349: + case 351: + case 353: + case 537: + case 575: + case 1109: + case 7777: + case 7779: + case 7781: + case 7783: + case 7785: + case 9390: + case 9416: + case 65363: + return 's'; + case 55: + case 355: + case 357: + case 359: + case 427: + case 429: + case 539: + case 566: + case 964: + case 1090: + case 1197: + case 7787: + case 7789: + case 7791: + case 7793: + case 7831: + case 9318: + case 9338: + case 9391: + case 9417: + case 65303: + case 65364: + return 't'; + case 249: + case 250: + case 251: + case 252: + case 361: + case 363: + case 365: + case 367: + case 369: + case 371: + case 432: + case 468: + case 470: + case 472: + case 474: + case 476: + case 533: + case 535: + case 649: + case 965: + case 973: + case 7795: + case 7797: + case 7799: + case 7801: + case 7803: + case 7909: + case 7911: + case 7913: + case 7915: + case 7917: + case 7919: + case 7921: + case 8016: + case 8017: + case 8018: + case 8019: + case 8020: + case 8021: + case 8022: + case 8023: + case 8160: + case 8161: + case 8162: + case 8163: + case 9392: + case 9418: + case 65365: + return 'u'; + case 1141: + case 1143: + case 7805: + case 7807: + case 9393: + case 9419: + case 65366: + return 'v'; + case 373: + case 7809: + case 7811: + case 7813: + case 7815: + case 7817: + case 7832: + case 9394: + case 9420: + case 65367: + return 'w'; + case 215: + case 967: + case 1093: + case 1203: + case 1277: + case 1279: + case 7819: + case 7821: + case 9395: + case 9421: + case 65368: + return 'x'; + case 253: + case 255: + case 375: + case 436: + case 563: + case 591: + case 947: + case 1118: + case 7823: + case 7833: + case 7923: + case 7925: + case 7927: + case 7929: + case 7935: + case 9396: + case 9422: + case 65369: + return 'y'; + case 378: + case 380: + case 382: + case 438: + case 549: + case 576: + case 656: + case 657: + case 7825: + case 7827: + case 7829: + case 9397: + case 9423: + case 65370: + return 'z'; + } + return 0; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/ProfanityFilter.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/ProfanityFilter.java new file mode 100755 index 0000000..2504e5b --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profanity_filter/ProfanityFilter.java @@ -0,0 +1,534 @@ +package net.lax1dude.eaglercraft.v1_8.profanity_filter; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.event.HoverEvent; +import net.minecraft.util.ChatComponentScore; +import net.minecraft.util.ChatComponentSelector; +import net.minecraft.util.ChatComponentStyle; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ProfanityFilter { + + private static final Logger logger = LogManager.getLogger("ProfanityFilter"); + + protected final Set[] bannedWords; + + private static ProfanityFilter instance = null; + + public static ProfanityFilter getInstance() { + if(instance == null) { + instance = new ProfanityFilter(); + } + return instance; + } + + private ProfanityFilter() { + logger.info("Loading profanity filter hash set..."); + List strs = EagRuntime.getResourceLines("profanity_filter.wlist"); + if(strs == null) { + throw new RuntimeException("File is missing: profanity_filter.wlist"); + } + long start = EagRuntime.steadyTimeMillis(); + Set[] sets = new Set[0]; + int j = 0; + for(String str : strs) { + if(nextDelimiter(str, 0) != -1) continue; + str = LookAlikeUnicodeConv.convertString(str); + int l = str.length(); + if(l == 0) continue; + if(sets.length < l) { + Set[] setsNew = new Set[l]; + System.arraycopy(sets, 0, setsNew, 0, sets.length); + sets = setsNew; + } + --l; + if(sets[l] == null) { + sets[l] = new HashSet(); + } + if(sets[l].add(str)) { + ++j; + } + } + bannedWords = sets; + logger.info("Processed {} entries after {}ms", j, (EagRuntime.steadyTimeMillis() - start)); + } + + public IChatComponent profanityFilterChatComponent(IChatComponent componentIn) { + if(componentIn == null) return null; + IChatComponent cmp = null; + //long start = System.nanoTime(); + try { + cmp = profanityFilterChatComponent0(componentIn); + }catch(Throwable t) { + logger.error("Profanity filter raised an exception on chat component!"); + logger.error(t); + } + //logger.info("Took: {}", ((System.nanoTime() - start) / 1000000.0)); + return cmp != null ? cmp : componentIn; + } + + protected IChatComponent profanityFilterChatComponent0(IChatComponent componentIn) { + if(componentIn instanceof ChatComponentStyle) { + boolean flag = false; + if(componentIn instanceof ChatComponentText) { + ChatComponentText comp = (ChatComponentText)componentIn; + String str = comp.getChatComponentText_TextValue(); + if(StringUtils.isEmpty(str) && componentIn.getSiblings().isEmpty()) { + return componentIn; + } + str = profanityFilterString0(str); + if(str != null) { + IChatComponent replacedComponent = new ChatComponentText(str); + replacedComponent.setChatStyle(comp.getChatStyleIfPresent()); + replacedComponent.getSiblings().addAll(componentIn.getSiblings()); + componentIn = replacedComponent; + flag = true; + } + }else if(componentIn instanceof ChatComponentTranslation) { + ChatComponentTranslation comp = (ChatComponentTranslation)componentIn; + IChatComponent replacedComponent = new ChatComponentTranslation(comp.getKey(), profanityFilterFormatArgs(comp.getFormatArgs())); + replacedComponent.setChatStyle(comp.getChatStyleIfPresent()); + replacedComponent.getSiblings().addAll(componentIn.getSiblings()); + componentIn = replacedComponent; + flag = true; + } + List siblings = componentIn.getSiblings(); + for(int i = 0, l = siblings.size(); i < l; ++i) { + IChatComponent cmp = profanityFilterChatComponent0(siblings.get(i)); + if(cmp != null) { + if(!flag) { + componentIn = shallowCopy(componentIn); + siblings = componentIn.getSiblings(); + flag = true; + } + siblings.set(i, cmp); + } + } + ChatStyle styleOpt = ((ChatComponentStyle)componentIn).getChatStyleIfPresent(); + if(styleOpt != null) { + HoverEvent hoverEvent = styleOpt.getChatHoverEvent(); + if(hoverEvent != null) { + HoverEvent filteredHoverEvent = profanityFilterHoverEvent(hoverEvent); + if(filteredHoverEvent != null) { + if(!flag) { + componentIn = shallowCopy(componentIn); + flag = true; + } + ChatStyle newStyle = styleOpt.createShallowCopy(); + newStyle.setChatHoverEvent(filteredHoverEvent); + componentIn.setChatStyle(newStyle); + } + } + } + return flag ? componentIn : null; + }else { + return null; + } + } + + private Object[] profanityFilterFormatArgs(Object[] formatArgs) { + Object[] ret = profanityFilterFormatArgs0(formatArgs); + return ret != null ? ret : formatArgs; + } + + private Object[] profanityFilterFormatArgs0(Object[] formatArgs) { + Object[] ret = null; + for(int i = 0; i < formatArgs.length; ++i) { + if(formatArgs[i] != null) { + String arg = formatArgs[i].toString(); + arg = profanityFilterString0(arg); + if(arg != null) { + if(ret == null) { + ret = new Object[formatArgs.length]; + System.arraycopy(formatArgs, 0, ret, 0, ret.length); + } + ret[i] = arg; + } + } + + } + return ret; + } + + protected HoverEvent profanityFilterHoverEvent(HoverEvent evtIn) { + if(evtIn.getAction() == HoverEvent.Action.SHOW_TEXT) { + IChatComponent filtered = evtIn.getValue(); + if(filtered != null) { + filtered = profanityFilterChatComponent0(filtered); + if(filtered != null) { + return new HoverEvent(evtIn.getAction(), filtered); + } + } + } + return null; + } + + private static IChatComponent shallowCopy(IChatComponent comp) { + if(comp instanceof ChatComponentStyle) { + if(comp instanceof ChatComponentText) { + ChatComponentText old = (ChatComponentText)comp; + IChatComponent ret = new ChatComponentText(old.getChatComponentText_TextValue()); + ret.setChatStyle(old.getChatStyleIfPresent()); + ret.getSiblings().addAll(comp.getSiblings()); + return ret; + }else if(comp instanceof ChatComponentTranslation) { + ChatComponentTranslation old = (ChatComponentTranslation)comp; + IChatComponent ret = new ChatComponentTranslation(old.getKey(), old.getFormatArgs()); + ret.setChatStyle(old.getChatStyleIfPresent()); + ret.getSiblings().addAll(comp.getSiblings()); + return ret; + }else if(comp instanceof ChatComponentScore) { + ChatComponentScore old = (ChatComponentScore)comp; + IChatComponent ret = new ChatComponentScore(old.getName(), old.getObjective()); + ret.setChatStyle(old.getChatStyleIfPresent()); + ret.getSiblings().addAll(comp.getSiblings()); + return ret; + }else if(comp instanceof ChatComponentSelector) { + ChatComponentSelector old = (ChatComponentSelector)comp; + IChatComponent ret = new ChatComponentSelector(old.getSelector()); + ret.setChatStyle(old.getChatStyleIfPresent()); + ret.getSiblings().addAll(comp.getSiblings()); + return ret; + } + } + return comp.createCopy(); + } + + private static final char[] stars = new char[] { '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*' }; + + public String profanityFilterString(String stringIn) { + if(stringIn == null) return null; + String str = null; + //long start = System.nanoTime(); + try { + str = profanityFilterString0(stringIn); + }catch(Throwable t) { + logger.error("Profanity filter raised an exception on string!"); + logger.error(t); + } + //logger.info("Took: {}", ((System.nanoTime() - start) / 1000000.0)); + return str != null ? str : stringIn; + } + + protected String profanityFilterString0(String stringIn) { + if(StringUtils.isAllBlank(stringIn)) { + return null; + } + int inLen = stringIn.length(); + boolean flag = false; + int i = 0, j; + int k = -1; + StringBuilder b = null; + String str; + List rangeList = new ArrayList(4); + int minCoverage = 40; + int pieceLen; + while((j = nextDelimiter(stringIn, i)) != -1) { + if(j - i > 2) { + if(b != null) { + str = LookAlikeUnicodeConv.convertString(stripColorCodes(b.toString())).toLowerCase(); + pieceLen = str.length(); + b = null; + //System.out.println("\"" + str + "\""); + rangeList.clear(); + if(isBanned(str, rangeList) && evaluateCoverage(pieceLen, rangeList) > (pieceLen * minCoverage / 100)) { + flag = true; + for(int m = 0, n = rangeList.size(); m < n; ++m) { + stringIn = doStar(stringIn, k, i - 1, rangeList.get(m)); + } + } + k = -1; + } + str = LookAlikeUnicodeConv.convertString(stripColorCodes(stringIn.substring(i, j))).toLowerCase(); + pieceLen = str.length(); + //System.out.println("\"" + str + "\""); + rangeList.clear(); + if(isBanned(str, rangeList) && evaluateCoverage(pieceLen, rangeList) > (pieceLen * minCoverage / 100)) { + flag = true; + for(int m = 0, n = rangeList.size(); m < n; ++m) { + stringIn = doStar(stringIn, i, j, rangeList.get(m)); + } + } + }else if(j - i > 0) { + if(b == null) { + k = i; + b = new StringBuilder(stringIn.substring(i, j)); + }else { + b.append(stringIn.substring(i, j)); + } + } + i = j + 1; + } + j = inLen; + if(j - i > 2) { + if(b != null) { + str = LookAlikeUnicodeConv.convertString(stripColorCodes(b.toString())).toLowerCase(); + pieceLen = str.length(); + b = null; + //System.out.println("\"" + str + "\""); + rangeList.clear(); + if(isBanned(str, rangeList) && evaluateCoverage(pieceLen, rangeList) > (pieceLen * minCoverage / 100)) { + flag = true; + for(int m = 0, n = rangeList.size(); m < n; ++m) { + stringIn = doStar(stringIn, k, i - 1, rangeList.get(m)); + } + } + k = -1; + } + str = LookAlikeUnicodeConv.convertString(stripColorCodes(stringIn.substring(i, j))).toLowerCase(); + pieceLen = str.length(); + //System.out.println("\"" + str + "\""); + rangeList.clear(); + if(isBanned(str, rangeList) && evaluateCoverage(pieceLen, rangeList) > (pieceLen * minCoverage / 100)) { + flag = true; + for(int m = 0, n = rangeList.size(); m < n; ++m) { + stringIn = doStar(stringIn, i, j, rangeList.get(m)); + } + } + }else if(j - i > 0) { + if(b == null) { + k = i; + b = new StringBuilder(stringIn.substring(i, j)); + }else { + b.append(stringIn.substring(i, j)); + } + } + if(b != null) { + str = LookAlikeUnicodeConv.convertString(stripColorCodes(b.toString())).toLowerCase(); + pieceLen = str.length(); + b = null; + //System.out.println("\"" + str + "\""); + rangeList.clear(); + if(isBanned(str, rangeList) && evaluateCoverage(pieceLen, rangeList) > (pieceLen * minCoverage / 100)) { + flag = true; + for(int m = 0, n = rangeList.size(); m < n; ++m) { + stringIn = doStar(stringIn, k, stringIn.length(), rangeList.get(m)); + } + } + } + return flag ? stringIn : null; + } + + protected String doStar(String stringIn, int start, int end, int[] rangeReturn) { + int strlen = stringIn.length(); + start += rangeReturn[0]; + end -= rangeReturn[1]; + int len = end - start; + if(len <= 0 || start < 0 || end > strlen) { + logger.warn("Profanity filter attempted out of bounds substring @ strlen: {}, start: {}, end: {}, len: {}", strlen, start, end, len); + return stringIn; + } + StringBuilder fixed = new StringBuilder(stringIn.substring(0, start)); + fixed.append(stars, 0, MathHelper.clamp_int(len, 1, stars.length)); + fixed.append(stringIn, end, stringIn.length()); + return fixed.toString(); + } + + protected static final int[] zeroZero = new int[2]; + + protected boolean isBanned(String word, List rangeReturn) { + int l = word.length(); + if(l == 0) return false; + int i = bannedWords.length; + int k; + boolean flag = false; + for(int j = Math.min(l, i); j >= 3; --j) { + if(j == l) { + Set set = bannedWords[j - 1]; + //System.out.println(" - \"" + word + "\""); + if(set != null && set.contains(word)) { + rangeReturn.add(zeroZero); + return true; + } + }else { + Set set = bannedWords[j - 1]; + if(set != null) { + int m = l - j; + for(int n = 0; n <= m; ++n) { + if(overlaps(n, n + j, l, rangeReturn)) { + //System.out.println("Skipping overlap: " + n + " -> " + (n + j)); + continue; + } + String ss = word.substring(n, n + j); + //System.out.println(" - \"" + ss + "\""); + if(set.contains(ss)) { + rangeReturn.add(new int[] { n, m - n }); + flag = true; + } + } + } + } + } + return flag; + } + + protected static boolean overlaps(int min, int max, int fullLen, List rangeReturn) { + int[] ii; + int j, k; + for(int i = 0, l = rangeReturn.size(); i < l; ++i) { + ii = rangeReturn.get(i); + j = ii[0]; + k = fullLen - ii[1]; + if(min < k && j < max) { + return true; + } + } + return false; + } + + protected static int evaluateCoverage(int fullLen, List rangeReturn) { + int coverage = 0; + int[] ii; + for(int i = 0, l = rangeReturn.size(); i < l; ++i) { + ii = rangeReturn.get(i); + coverage += fullLen - ii[0] - ii[1] - 1; + } + return coverage; + } + + protected static String stripColorCodes(String stringIn) { + int i = stringIn.indexOf('\u00a7'); + if(i != -1) { + int j = 0; + int l = stringIn.length(); + StringBuilder ret = new StringBuilder(l); + do { + if(i - j > 0) { + ret.append(stringIn, j, i); + } + j = i + 2; + }while(j < l && (i = stringIn.indexOf('\u00a7', j)) != -1); + if(l - j > 0) { + ret.append(stringIn, j, l); + } + return ret.toString(); + }else { + return stringIn; + } + } + + private static int nextDelimiter(String stringIn, int startIndex) { + int len = stringIn.length(); + while(startIndex < len) { + char c = stringIn.charAt(startIndex); + switch(c) { + case ' ': + case '.': + case ',': + case '_': + case '+': + case '-': + case '=': + case '|': + case '?': + case '<': + case '>': + case '/': + case '\\': + case '\'': + case '\"': + case '@': + case '#': + case '%': + case '^': + case '&': + case '*': + case '(': + case ')': + case '{': + case '}': + case ':': + case ';': + case '`': + case '~': + case '\n': + case '\r': + case '\t': + case '\u00a0': + return startIndex; + case '!': + if (!(startIndex > 0 && !isDelimiter(stringIn.charAt(startIndex - 1))) + || !(startIndex + 1 < len && !isDelimiter(stringIn.charAt(startIndex + 1)))) { + return startIndex; + } + default: + break; + } + ++startIndex; + } + return -1; + } + + private static boolean isDelimiter(char c) { + switch(c) { + case ' ': + case '.': + case ',': + case '_': + case '+': + case '-': + case '=': + case '|': + case '?': + case '!': + case '<': + case '>': + case '/': + case '\\': + case '\'': + case '\"': + case '@': + case '#': + case '%': + case '^': + case '&': + case '*': + case '(': + case ')': + case '{': + case '}': + case ':': + case ';': + case '`': + case '~': + case '\n': + case '\r': + case '\t': + case '\u00a0': + return true; + } + return false; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java index 8c84bd7..af6327a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java @@ -1,11 +1,5 @@ package net.lax1dude.eaglercraft.v1_8.profile; -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.minecraft.network.PacketBuffer; - /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * @@ -25,42 +19,6 @@ public class CapePackets { public static final int PACKET_MY_CAPE_PRESET = 0x01; public static final int PACKET_MY_CAPE_CUSTOM = 0x02; - public static final int PACKET_GET_OTHER_CAPE = 0x03; - public static final int PACKET_OTHER_CAPE_PRESET = 0x04; - public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05; - - public static void readPluginMessage(PacketBuffer buffer, ServerCapeCache capeCache) throws IOException { - try { - int type = (int)buffer.readByte() & 0xFF; - switch(type) { - case PACKET_OTHER_CAPE_PRESET: { - EaglercraftUUID responseUUID = buffer.readUuid(); - int responsePreset = buffer.readInt(); - if(buffer.isReadable()) { - throw new IOException("PACKET_OTHER_CAPE_PRESET had " + buffer.readableBytes() + " remaining bytes!"); - } - capeCache.cacheCapePreset(responseUUID, responsePreset); - break; - } - case PACKET_OTHER_CAPE_CUSTOM: { - EaglercraftUUID responseUUID = buffer.readUuid(); - byte[] readCape = new byte[1173]; - buffer.readBytes(readCape); - if(buffer.isReadable()) { - throw new IOException("PACKET_OTHER_CAPE_CUSTOM had " + buffer.readableBytes() + " remaining bytes!"); - } - capeCache.cacheCapeCustom(responseUUID, readCape); - break; - } - default: - throw new IOException("Unknown skin packet type: " + type); - } - }catch(IOException ex) { - throw ex; - }catch(Throwable t) { - throw new IOException("Failed to parse cape packet!", t); - } - } public static byte[] writeMyCapePreset(int capeId) { return new byte[] { (byte) PACKET_MY_CAPE_PRESET, (byte) (capeId >>> 24), (byte) (capeId >>> 16), @@ -74,10 +32,4 @@ public class CapePackets { return packet; } - public static PacketBuffer writeGetOtherCape(EaglercraftUUID playerId) throws IOException { - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); - ret.writeByte(PACKET_GET_OTHER_CAPE); - ret.writeUuid(playerId); - return ret; - } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java index 2dbca93..a97aa3b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java @@ -1,410 +1,511 @@ -package net.lax1dude.eaglercraft.v1_8.profile; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.teavm.jso.JSBody; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; -import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.minecraft.client.Minecraft; -import net.minecraft.nbt.CompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.util.ResourceLocation; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglerProfile { - - private static String username; - - public static int presetSkinId; - public static int customSkinId; - - public static int presetCapeId; - public static int customCapeId; - - public static final List customSkins = new ArrayList(); - - public static final List customCapes = new ArrayList(); - - public static final EaglercraftRandom rand; - - public static ResourceLocation getActiveSkinResourceLocation() { - if (presetSkinId == -1) { - if (customSkinId >= 0 && customSkinId < customSkins.size()) { - return customSkins.get(customSkinId).getResource(); - } else { - customSkinId = -1; - presetSkinId = 0; - return DefaultSkins.defaultSkinsMap[0].location; - } - } else { - if (presetSkinId >= 0 && presetSkinId < DefaultSkins.defaultSkinsMap.length) { - return DefaultSkins.defaultSkinsMap[presetSkinId].location; - } else { - presetSkinId = 0; - return DefaultSkins.defaultSkinsMap[0].location; - } - } - } - - public static SkinModel getActiveSkinModel() { - if (presetSkinId == -1) { - if (customSkinId >= 0 && customSkinId < customSkins.size()) { - return customSkins.get(customSkinId).model; - } else { - customSkinId = -1; - presetSkinId = 0; - return DefaultSkins.defaultSkinsMap[0].model; - } - } else { - if (presetSkinId >= 0 && presetSkinId < DefaultSkins.defaultSkinsMap.length) { - return DefaultSkins.defaultSkinsMap[presetSkinId].model; - } else { - presetSkinId = 0; - return DefaultSkins.defaultSkinsMap[0].model; - } - } - } - - public static ResourceLocation getActiveCapeResourceLocation() { - if (presetCapeId == -1) { - if (customCapeId >= 0 && customCapeId < customCapes.size()) { - return customCapes.get(customCapeId).getResource(); - } else { - customCapeId = -1; - presetCapeId = 0; - return DefaultCapes.defaultCapesMap[0].location; - } - } else { - if (presetCapeId >= 0 && presetCapeId < DefaultCapes.defaultCapesMap.length) { - return DefaultCapes.defaultCapesMap[presetCapeId].location; - } else { - presetCapeId = 0; - return DefaultCapes.defaultCapesMap[0].location; - } - } - } - - public static EaglercraftUUID getPlayerUUID() { - return Minecraft.getMinecraft().getSession().getProfile().getId(); - } - - public static String getName() { - return username; - } - - public static void setName(String str) { - username = str; - Minecraft mc = Minecraft.getMinecraft(); - if (mc != null) { - mc.getSession().reset(); - } - } - - public static byte[] getSkinPacket() { - if (presetSkinId == -1) { - if (customSkinId >= 0 && customSkinId < customSkins.size()) { - return SkinPackets.writeMySkinCustom(customSkins.get(customSkinId)); - } else { - customSkinId = -1; - presetSkinId = 0; - return SkinPackets.writeMySkinPreset(0); - } - } else { - if (presetSkinId >= 0 && presetSkinId < DefaultSkins.defaultSkinsMap.length) { - return SkinPackets.writeMySkinPreset(presetSkinId); - } else { - presetSkinId = 0; - return SkinPackets.writeMySkinPreset(0); - } - } - } - - public static byte[] getCapePacket() { - if (presetCapeId == -1) { - if (customCapeId >= 0 && customCapeId < customCapes.size()) { - return CapePackets.writeMyCapeCustom(customCapes.get(customCapeId)); - } else { - customCapeId = -1; - presetCapeId = 0; - return CapePackets.writeMyCapePreset(0); - } - } else { - if (presetCapeId >= 0 && presetCapeId < DefaultCapes.defaultCapesMap.length) { - return CapePackets.writeMyCapePreset(presetCapeId); - } else { - presetCapeId = 0; - return CapePackets.writeMyCapePreset(0); - } - } - } - - private static boolean doesSkinExist(String name) { - for (int i = 0, l = customSkins.size(); i < l; ++i) { - if (customSkins.get(i).name.equalsIgnoreCase(name)) { - return true; - } - } - return false; - } - - private static boolean doesCapeExist(String name) { - for (int i = 0, l = customCapes.size(); i < l; ++i) { - if (customCapes.get(i).name.equalsIgnoreCase(name)) { - return true; - } - } - return false; - } - - public static int addCustomSkin(String fileName, byte[] rawSkin) { - if (doesSkinExist(fileName)) { - String newName; - int i = 2; - while (doesSkinExist(newName = fileName + " (" + i + ")")) { - ++i; - } - fileName = newName; - } - CustomSkin newSkin = new CustomSkin(fileName, rawSkin, SkinModel.STEVE); - newSkin.load(); - int r = customSkins.size(); - customSkins.add(newSkin); - return r; - } - - public static int addCustomCape(String fileName, byte[] rawCape23x17RGB) { - if (doesCapeExist(fileName)) { - String newName; - int i = 2; - while (doesCapeExist(newName = fileName + " (" + i + ")")) { - ++i; - } - fileName = newName; - } - CustomCape newCape = new CustomCape(fileName, rawCape23x17RGB); - newCape.load(); - int r = customCapes.size(); - customCapes.add(newCape); - return r; - } - - public static void clearCustomSkins() { - for (int i = 0, l = customSkins.size(); i < l; ++i) { - customSkins.get(i).delete(); - } - customSkins.clear(); - } - - public static void clearCustomCapes() { - for (int i = 0, l = customCapes.size(); i < l; ++i) { - customCapes.get(i).delete(); - } - customCapes.clear(); - } - - public static void read() { - read(EagRuntime.getStorage("p")); - } - - @JSBody(params = { "name", - "data" }, script = "document.cookie = name + '=' + encodeURIComponent(data) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';") - public static native void setCookie(String name, String data); - - @JSBody(params = { - "name" }, script = "var value = '; ' + document.cookie; var parts = value.split('; ' + name + '='); if (parts.length == 2) return decodeURIComponent(parts.pop().split(';').shift()); return '';") - public static native String getCookie(String name); - - @JSBody(params = { "name", "data" }, script = "localStorage.setItem(name, data);") - public static native void setLocalStorage(String name, String data); - - @JSBody(params = { "name" }, script = "return localStorage.getItem(name);") - public static native String getLocalStorage(String name); - - public static void updateUsernameCookies() { - setCookie("username", username); - setLocalStorage("username", username); - } - - public static void updateUsernameCookieFromLocalStorage() { - String name = getLocalStorage("username"); - if (name != null && !name.isEmpty()) { - username = name; - } - } - - public static void read(byte[] profileStorage) { - if (profileStorage == null) { - return; - } - - NBTTagCompound profile; - try { - profile = CompressedStreamTools.readCompressed(new EaglerInputStream(profileStorage)); - } catch (IOException ex) { - return; - } - - if (profile == null || profile.hasNoTags()) { - return; - } - - presetSkinId = profile.getInteger("presetSkin"); - customSkinId = profile.getInteger("customSkin"); - - if (profile.hasKey("presetCape", 99)) - presetCapeId = profile.getInteger("presetCape"); - if (profile.hasKey("customCape", 99)) - customCapeId = profile.getInteger("customCape"); - - String loadUsername = profile.getString("username").trim(); - - if (!loadUsername.isEmpty()) { - username = loadUsername.replaceAll("[^A-Za-z0-9]", "_"); - updateUsernameCookies(); - } - - clearCustomSkins(); - - NBTTagList skinsList = profile.getTagList("skins", 10); - for (int i = 0, l = skinsList.tagCount(); i < l; ++i) { - NBTTagCompound skin = skinsList.getCompoundTagAt(i); - String skinName = skin.getString("name"); - byte[] skinData = skin.getByteArray("data"); - if (skinData.length != 16384) - continue; - for (int y = 20; y < 32; ++y) { - for (int x = 16; x < 40; ++x) { - skinData[(y << 8) | (x << 2)] = (byte) 0xff; - } - } - int skinModel = skin.getByte("model"); - CustomSkin newSkin = new CustomSkin(skinName, skinData, SkinModel.getModelFromId(skinModel)); - newSkin.load(); - customSkins.add(newSkin); - } - - if (profile.hasKey("capes", 9)) { - clearCustomCapes(); - NBTTagList capesList = profile.getTagList("capes", 10); - for (int i = 0, l = capesList.tagCount(); i < l; ++i) { - NBTTagCompound cape = capesList.getCompoundTagAt(i); - String capeName = cape.getString("name"); - byte[] capeData = cape.getByteArray("data"); - if (capeData.length != 1173) - continue; - CustomCape newCape = new CustomCape(capeName, capeData); - newCape.load(); - customCapes.add(newCape); - } - } - - if (presetSkinId == -1) { - if (customSkinId < 0 || customSkinId >= customSkins.size()) { - presetSkinId = 0; - customSkinId = -1; - } - } else { - customSkinId = -1; - if (presetSkinId < 0 || presetSkinId >= DefaultSkins.defaultSkinsMap.length) { - presetSkinId = 0; - } - } - - } - - public static byte[] write() { - NBTTagCompound profile = new NBTTagCompound(); - profile.setInteger("presetSkin", presetSkinId); - profile.setInteger("customSkin", customSkinId); - profile.setInteger("presetCape", presetCapeId); - profile.setInteger("customCape", customCapeId); - profile.setString("username", username); - NBTTagList skinsList = new NBTTagList(); - for (int i = 0, l = customSkins.size(); i < l; ++i) { - CustomSkin sk = customSkins.get(i); - NBTTagCompound skin = new NBTTagCompound(); - skin.setString("name", sk.name); - skin.setByteArray("data", sk.texture); - skin.setByte("model", (byte) sk.model.id); - skinsList.appendTag(skin); - } - profile.setTag("skins", skinsList); - NBTTagList capesList = new NBTTagList(); - for (int i = 0, l = customCapes.size(); i < l; ++i) { - CustomCape cp = customCapes.get(i); - NBTTagCompound cape = new NBTTagCompound(); - cape.setString("name", cp.name); - cape.setByteArray("data", cp.texture); - capesList.appendTag(cape); - } - profile.setTag("capes", capesList); - EaglerOutputStream bao = new EaglerOutputStream(); - try { - CompressedStreamTools.writeCompressed(profile, bao); - } catch (IOException e) { - return null; - } - return bao.toByteArray(); - } - - public static void save() { - byte[] b = write(); - if (b != null) { - EagRuntime.setStorage("p", b); - } - } - - static { - String[] defaultNames = new String[] { - "Yeeish", "Yeeish", "Yee", "Yee", "Yeer", "Yeeler", "Eagler", "Eagl", - "Darver", "Darvler", "Vool", "Vigg", "Vigg", "Deev", "Yigg", "Yeeg" - }; - - rand = new EaglercraftRandom(); - - do { - username = defaultNames[rand.nextInt(defaultNames.length)] + defaultNames[rand.nextInt(defaultNames.length)] - + (100 + rand.nextInt(900)); - } while (username.length() > 16); - - setName(username); - - do { - presetSkinId = rand.nextInt(DefaultSkins.defaultSkinsMap.length); - } while (DefaultSkins.defaultSkinsMap[presetSkinId].model.highPoly != null); - customSkinId = -1; - - presetCapeId = 0; - customCapeId = -1; - } - -} +package net.lax1dude.eaglercraft.v1_8.profile; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.teavm.jso.JSBody; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights + * Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerProfile { + + private static String username; + + public static int presetSkinId; + public static int customSkinId; + + public static int presetCapeId; + public static int customCapeId; + + public static boolean isServerSkinOverride = false; + public static int overridePresetSkinId = -1; + public static final ResourceLocation overrideCustomSkinTexture = new ResourceLocation("eagler:skins/custom/tex_server_override"); + public static EaglerSkinTexture overrideCustomSkin = null; + public static SkinModel overrideCustomSkinModel = SkinModel.STEVE; + + public static boolean isServerCapeOverride = false; + public static int overridePresetCapeId = -1; + public static final ResourceLocation overrideCustomCapeTexture = new ResourceLocation("eagler:capes/custom/tex_server_override"); + public static EaglerSkinTexture overrideCustomCape = null; + + public static final List customSkins = new ArrayList<>(); + public static final List customCapes = new ArrayList<>(); + + public static final EaglercraftRandom rand; + + public static ResourceLocation getActiveSkinResourceLocation() { + if(isServerSkinOverride) { + if(overridePresetSkinId == -1) { + return overrideCustomSkinTexture; + }else { + if(overridePresetSkinId >= 0 && overridePresetSkinId < DefaultSkins.defaultSkinsMap.length) { + return DefaultSkins.defaultSkinsMap[overridePresetSkinId].location; + }else { + return DefaultSkins.defaultSkinsMap[0].location; + } + } + } + if(presetSkinId == -1) { + if(customSkinId >= 0 && customSkinId < customSkins.size()) { + return customSkins.get(customSkinId).getResource(); + } else { + customSkinId = -1; + presetSkinId = 0; + return DefaultSkins.defaultSkinsMap[0].location; + } + } else { + if (presetSkinId >= 0 && presetSkinId < DefaultSkins.defaultSkinsMap.length) { + return DefaultSkins.defaultSkinsMap[presetSkinId].location; + } else { + presetSkinId = 0; + return DefaultSkins.defaultSkinsMap[0].location; + } + } + } + + public static SkinModel getActiveSkinModel() { + if(isServerSkinOverride) { + if(overridePresetSkinId == -1) { + return overrideCustomSkinModel; + }else { + if(overridePresetSkinId >= 0 && overridePresetSkinId < DefaultSkins.defaultSkinsMap.length) { + return DefaultSkins.defaultSkinsMap[overridePresetSkinId].model; + }else { + return DefaultSkins.defaultSkinsMap[0].model; + } + } + } + if(presetSkinId == -1) { + if(customSkinId >= 0 && customSkinId < customSkins.size()) { + return customSkins.get(customSkinId).model; + } else { + customSkinId = -1; + presetSkinId = 0; + return DefaultSkins.defaultSkinsMap[0].model; + } + } else { + if (presetSkinId >= 0 && presetSkinId < DefaultSkins.defaultSkinsMap.length) { + return DefaultSkins.defaultSkinsMap[presetSkinId].model; + } else { + presetSkinId = 0; + return DefaultSkins.defaultSkinsMap[0].model; + } + } + } + + public static ResourceLocation getActiveCapeResourceLocation() { + if(isServerCapeOverride) { + if(overridePresetCapeId == -1) { + return overrideCustomCapeTexture; + }else { + if(overridePresetCapeId >= 0 && overridePresetCapeId < DefaultCapes.defaultCapesMap.length) { + return DefaultCapes.defaultCapesMap[overridePresetCapeId].location; + }else { + return DefaultCapes.defaultCapesMap[0].location; + } + } + } + if(presetCapeId == -1) { + if(customCapeId >= 0 && customCapeId < customCapes.size()) { + return customCapes.get(customCapeId).getResource(); + } else { + customCapeId = -1; + presetCapeId = 0; + return DefaultCapes.defaultCapesMap[0].location; + } + } else { + if (presetCapeId >= 0 && presetCapeId < DefaultCapes.defaultCapesMap.length) { + return DefaultCapes.defaultCapesMap[presetCapeId].location; + } else { + presetCapeId = 0; + return DefaultCapes.defaultCapesMap[0].location; + } + } + } + + public static EaglercraftUUID getPlayerUUID() { + return Minecraft.getMinecraft().getSession().getProfile().getId(); + } + + public static String getName() { + return username; + } + + public static void setName(String str) { + username = str; + Minecraft mc = Minecraft.getMinecraft(); + if (mc != null) { + mc.getSession().reset(); + } + } + + public static byte[] getSkinPacket(int vers) { + if(presetSkinId == -1) { + if(customSkinId >= 0 && customSkinId < customSkins.size()) { + CustomSkin toSend = customSkins.get(customSkinId); + if(vers <= 3) { + return SkinPackets.writeMySkinCustomV3(toSend); + }else { + return SkinPackets.writeMySkinCustomV4(toSend); + } + }else { + customSkinId = -1; + presetSkinId = 0; + return SkinPackets.writeMySkinPreset(0); + } + } else { + if (presetSkinId >= 0 && presetSkinId < DefaultSkins.defaultSkinsMap.length) { + return SkinPackets.writeMySkinPreset(presetSkinId); + } else { + presetSkinId = 0; + return SkinPackets.writeMySkinPreset(0); + } + } + } + + public static byte[] getCapePacket() { + if (presetCapeId == -1) { + if (customCapeId >= 0 && customCapeId < customCapes.size()) { + return CapePackets.writeMyCapeCustom(customCapes.get(customCapeId)); + } else { + customCapeId = -1; + presetCapeId = 0; + return CapePackets.writeMyCapePreset(0); + } + } else { + if (presetCapeId >= 0 && presetCapeId < DefaultCapes.defaultCapesMap.length) { + return CapePackets.writeMyCapePreset(presetCapeId); + } else { + presetCapeId = 0; + return CapePackets.writeMyCapePreset(0); + } + } + } + + public static void handleForceSkinPreset(int preset) { + isServerSkinOverride = true; + overridePresetSkinId = preset; + ServerSkinCache.needReloadClientSkin = true; + } + + public static void handleForceSkinCustom(int modelID, byte[] datav3) { + if(datav3.length != 16384) { + return; + } + isServerSkinOverride = true; + overridePresetSkinId = -1; + overrideCustomSkinModel = SkinModel.getModelFromId(modelID); + if(overrideCustomSkinModel.highPoly != null) { + overrideCustomSkinModel = SkinModel.STEVE; + } + if(overrideCustomSkin == null) { + overrideCustomSkin = new EaglerSkinTexture(datav3, 64, 64); + Minecraft.getMinecraft().getTextureManager().loadTexture(overrideCustomSkinTexture, overrideCustomSkin); + }else { + overrideCustomSkin.copyPixelsIn(datav3); + } + ServerSkinCache.needReloadClientSkin = true; + } + + public static void handleForceCapePreset(int preset) { + isServerCapeOverride = true; + overridePresetCapeId = preset; + ServerCapeCache.needReloadClientCape = true; + } + + public static void handleForceCapeCustom(byte[] custom) { + if(custom.length != 1173) { + return; + } + byte[] pixels32x32 = new byte[4096]; + SkinConverter.convertCape23x17RGBto32x32RGBA(custom, pixels32x32); + isServerCapeOverride = true; + overridePresetCapeId = -1; + if(overrideCustomCape == null) { + overrideCustomCape = new EaglerSkinTexture(pixels32x32, 32, 32); + Minecraft.getMinecraft().getTextureManager().loadTexture(overrideCustomCapeTexture, overrideCustomCape); + }else { + overrideCustomCape.copyPixelsIn(pixels32x32); + } + ServerCapeCache.needReloadClientCape = true; + } + + public static void clearServerSkinOverride() { + isServerSkinOverride = false; + isServerCapeOverride = false; + } + + private static boolean doesSkinExist(String name) { + for (int i = 0, l = customSkins.size(); i < l; ++i) { + if (customSkins.get(i).name.equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + + private static boolean doesCapeExist(String name) { + for (int i = 0, l = customCapes.size(); i < l; ++i) { + if (customCapes.get(i).name.equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + + public static int addCustomSkin(String fileName, byte[] rawSkin) { + if (doesSkinExist(fileName)) { + String newName; + int i = 2; + while (doesSkinExist(newName = fileName + " (" + i + ")")) { + ++i; + } + fileName = newName; + } + CustomSkin newSkin = new CustomSkin(fileName, rawSkin, SkinModel.STEVE); + newSkin.load(); + int r = customSkins.size(); + customSkins.add(newSkin); + return r; + } + + public static int addCustomCape(String fileName, byte[] rawCape23x17RGB) { + if (doesCapeExist(fileName)) { + String newName; + int i = 2; + while (doesCapeExist(newName = fileName + " (" + i + ")")) { + ++i; + } + fileName = newName; + } + CustomCape newCape = new CustomCape(fileName, rawCape23x17RGB); + newCape.load(); + int r = customCapes.size(); + customCapes.add(newCape); + return r; + } + + public static void clearCustomSkins() { + for (int i = 0, l = customSkins.size(); i < l; ++i) { + customSkins.get(i).delete(); + } + customSkins.clear(); + } + + public static void clearCustomCapes() { + for (int i = 0, l = customCapes.size(); i < l; ++i) { + customCapes.get(i).delete(); + } + customCapes.clear(); + } + + public static void read() { + read(EagRuntime.getStorage("p")); + } + + @JSBody(params = { "name", + "data" }, script = "document.cookie = name + '=' + encodeURIComponent(data) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';") + public static native void setCookie(String name, String data); + + @JSBody(params = { + "name" }, script = "var value = '; ' + document.cookie; var parts = value.split('; ' + name + '='); if (parts.length == 2) return decodeURIComponent(parts.pop().split(';').shift()); return '';") + public static native String getCookie(String name); + + @JSBody(params = { "name", "data" }, script = "localStorage.setItem(name, data);") + public static native void setLocalStorage(String name, String data); + + @JSBody(params = { "name" }, script = "return localStorage.getItem(name);") + public static native String getLocalStorage(String name); + + public static void updateUsernameCookies() { + setCookie("username", username); + setLocalStorage("username", username); + } + + public static void updateUsernameCookieFromLocalStorage() { + String name = getLocalStorage("username"); + if (name != null && !name.isEmpty()) { + username = name; + } + } + + public static void read(byte[] profileStorage) { + if (profileStorage == null) { + return; + } + + NBTTagCompound profile; + try { + profile = CompressedStreamTools.readCompressed(new EaglerInputStream(profileStorage)); + } catch (IOException ex) { + return; + } + + if (profile == null || profile.hasNoTags()) { + return; + } + + presetSkinId = profile.getInteger("presetSkin"); + customSkinId = profile.getInteger("customSkin"); + + if (profile.hasKey("presetCape", 99)) + presetCapeId = profile.getInteger("presetCape"); + if (profile.hasKey("customCape", 99)) + customCapeId = profile.getInteger("customCape"); + + String loadUsername = profile.getString("username").trim(); + + if (!loadUsername.isEmpty()) { + username = loadUsername.replaceAll("[^A-Za-z0-9]", "_"); + updateUsernameCookies(); + } + + clearCustomSkins(); + + NBTTagList skinsList = profile.getTagList("skins", 10); + for (int i = 0, l = skinsList.tagCount(); i < l; ++i) { + NBTTagCompound skin = skinsList.getCompoundTagAt(i); + String skinName = skin.getString("name"); + byte[] skinData = skin.getByteArray("data"); + if (skinData.length != 16384) + continue; + for (int y = 20; y < 32; ++y) { + for (int x = 16; x < 40; ++x) { + skinData[(y << 8) | (x << 2)] = (byte) 0xff; + } + } + int skinModel = skin.getByte("model"); + CustomSkin newSkin = new CustomSkin(skinName, skinData, SkinModel.getModelFromId(skinModel)); + newSkin.load(); + customSkins.add(newSkin); + } + + if (profile.hasKey("capes", 9)) { + clearCustomCapes(); + NBTTagList capesList = profile.getTagList("capes", 10); + for (int i = 0, l = capesList.tagCount(); i < l; ++i) { + NBTTagCompound cape = capesList.getCompoundTagAt(i); + String capeName = cape.getString("name"); + byte[] capeData = cape.getByteArray("data"); + if (capeData.length != 1173) + continue; + CustomCape newCape = new CustomCape(capeName, capeData); + newCape.load(); + customCapes.add(newCape); + } + } + + if (presetSkinId == -1) { + if (customSkinId < 0 || customSkinId >= customSkins.size()) { + presetSkinId = 0; + customSkinId = -1; + } + } else { + customSkinId = -1; + if (presetSkinId < 0 || presetSkinId >= DefaultSkins.defaultSkinsMap.length) { + presetSkinId = 0; + } + } + + } + + public static byte[] write() { + NBTTagCompound profile = new NBTTagCompound(); + profile.setInteger("presetSkin", presetSkinId); + profile.setInteger("customSkin", customSkinId); + profile.setInteger("presetCape", presetCapeId); + profile.setInteger("customCape", customCapeId); + profile.setString("username", username); + NBTTagList skinsList = new NBTTagList(); + for (int i = 0, l = customSkins.size(); i < l; ++i) { + CustomSkin sk = customSkins.get(i); + NBTTagCompound skin = new NBTTagCompound(); + skin.setString("name", sk.name); + skin.setByteArray("data", sk.texture); + skin.setByte("model", (byte) sk.model.id); + skinsList.appendTag(skin); + } + profile.setTag("skins", skinsList); + NBTTagList capesList = new NBTTagList(); + for (int i = 0, l = customCapes.size(); i < l; ++i) { + CustomCape cp = customCapes.get(i); + NBTTagCompound cape = new NBTTagCompound(); + cape.setString("name", cp.name); + cape.setByteArray("data", cp.texture); + capesList.appendTag(cape); + } + profile.setTag("capes", capesList); + EaglerOutputStream bao = new EaglerOutputStream(); + try { + CompressedStreamTools.writeCompressed(profile, bao); + } catch (IOException e) { + return null; + } + return bao.toByteArray(); + } + + public static void save() { + byte[] b = write(); + if (b != null) { + EagRuntime.setStorage("p", b); + } + } + + static { + String[] defaultNames = new String[] { + "Yeeish", "Yeeish", "Yee", "Yee", "Yeer", "Yeeler", "Eagler", "Eagl", + "Darver", "Darvler", "Vool", "Vigg", "Vigg", "Deev", "Yigg", "Yeeg" + }; + + rand = new EaglercraftRandom(); + + do { + username = defaultNames[rand.nextInt(defaultNames.length)] + defaultNames[rand.nextInt(defaultNames.length)] + + (100 + rand.nextInt(900)); + } while (username.length() > 16); + + setName(username); + + do { + presetSkinId = rand.nextInt(DefaultSkins.defaultSkinsMap.length); + } while (DefaultSkins.defaultSkinsMap[presetSkinId].model.highPoly != null); + customSkinId = -1; + + presetCapeId = 0; + customCapeId = -1; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java index 56e5b94..2ecc324 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java @@ -1,108 +1,116 @@ -package net.lax1dude.eaglercraft.v1_8.profile; - -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; -import net.minecraft.client.renderer.texture.ITextureObject; -import net.minecraft.client.renderer.texture.TextureUtil; -import net.minecraft.client.resources.IResourceManager; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EaglerSkinTexture implements ITextureObject { - - private final int[] pixels; - private final int width; - private final int height; - - private int textureId = -1; - - public EaglerSkinTexture(int[] pixels, int width, int height) { - if (pixels.length != width * height) { - throw new IllegalArgumentException( - "Wrong data length " + pixels.length * 4 + " for " + width + "x" + height + " texture"); - } - this.pixels = pixels; - this.width = width; - this.height = height; - } - - public EaglerSkinTexture(byte[] pixels, int width, int height) { - if (pixels.length != width * height * 4) { - throw new IllegalArgumentException( - "Wrong data length " + pixels.length + " for " + width + "x" + height + " texture"); - } - int[] p = new int[pixels.length >> 2]; - for (int i = 0, j; i < p.length; ++i) { - j = i << 2; - p[i] = (((int) pixels[j] & 0xFF) << 24) | (((int) pixels[j + 1] & 0xFF) << 16) - | (((int) pixels[j + 2] & 0xFF) << 8) | ((int) pixels[j + 3] & 0xFF); - } - this.pixels = p; - this.width = width; - this.height = height; - } - - public void copyPixelsIn(int[] pixels) { - if (this.pixels.length != pixels.length) { - throw new IllegalArgumentException( - "Tried to copy " + pixels.length + " pixels into a " + this.pixels.length + " pixel texture"); - } - System.arraycopy(pixels, 0, this.pixels, 0, pixels.length); - if (textureId != -1) { - TextureUtil.uploadTextureImageSub(textureId, new ImageData(width, height, pixels, true), 0, 0, false, - false); - } - } - - @Override - public void loadTexture(IResourceManager var1) throws IOException { - if (textureId == -1) { - textureId = GlStateManager.generateTexture(); - TextureUtil.uploadTextureImageAllocate(textureId, new ImageData(width, height, pixels, true), false, false); - } - } - - @Override - public int getGlTextureId() { - return textureId; - } - - @Override - public void setBlurMipmap(boolean var1, boolean var2) { - // no - } - - @Override - public void restoreLastBlurMipmap() { - // no - } - - public void free() { - GlStateManager.deleteTexture(textureId); - textureId = -1; - } - -} +package net.lax1dude.eaglercraft.v1_8.profile; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.minecraft.client.renderer.texture.ITextureObject; +import net.minecraft.client.renderer.texture.TextureUtil; +import net.minecraft.client.resources.IResourceManager; + +/** + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerSkinTexture implements ITextureObject { + + private final int[] pixels; + private final int width; + private final int height; + + private int textureId = -1; + + public EaglerSkinTexture(int[] pixels, int width, int height) { + if(pixels.length != width * height) { + throw new IllegalArgumentException("Wrong data length " + pixels.length * 4 + " for " + width + "x" + height + " texture"); + } + this.pixels = pixels; + this.width = width; + this.height = height; + } + + public EaglerSkinTexture(byte[] pixels, int width, int height) { + if(pixels.length != width * height * 4) { + throw new IllegalArgumentException("Wrong data length " + pixels.length + " for " + width + "x" + height + " texture"); + } + this.pixels = convertToInt(pixels); + this.width = width; + this.height = height; + } + + public static int[] convertToInt(byte[] pixels) { + int[] p = new int[pixels.length >> 2]; + for(int i = 0, j; i < p.length; ++i) { + j = i << 2; + p[i] = (((int) pixels[j] & 0xFF) << 24) | (((int) pixels[j + 1] & 0xFF) << 16) + | (((int) pixels[j + 2] & 0xFF) << 8) | ((int) pixels[j + 3] & 0xFF); + } + return p; + } + + public void copyPixelsIn(byte[] pixels) { + copyPixelsIn(convertToInt(pixels)); + } + + public void copyPixelsIn(int[] pixels) { + if(this.pixels.length != pixels.length) { + throw new IllegalArgumentException("Tried to copy " + pixels.length + " pixels into a " + this.pixels.length + " pixel texture"); + } + System.arraycopy(pixels, 0, this.pixels, 0, pixels.length); + if(textureId != -1) { + TextureUtil.uploadTextureImageSub(textureId, new ImageData(width, height, pixels, true), 0, 0, false, false); + } + } + + @Override + public void loadTexture(IResourceManager var1) throws IOException { + if(textureId == -1) { + textureId = GlStateManager.generateTexture(); + TextureUtil.uploadTextureImageAllocate(textureId, new ImageData(width, height, pixels, true), false, false); + } + } + + @Override + public int getGlTextureId() { + return textureId; + } + + @Override + public void setBlurMipmap(boolean var1, boolean var2) { + // no + } + + @Override + public void restoreLastBlurMipmap() { + // no + } + + public void free() { + GlStateManager.deleteTexture(textureId); + textureId = -1; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int[] getData() { + return pixels; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java index 683bbc4..bec4f42 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java @@ -2,7 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.profile; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; import net.minecraft.client.gui.GuiButton; @@ -11,24 +11,16 @@ import net.minecraft.client.multiplayer.GuiConnecting; import net.minecraft.client.resources.I18n; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -48,19 +40,19 @@ public class GuiAuthenticationScreen extends GuiScreen { this.retAfterAuthScreen = retAfterAuthScreen; this.parent = parent; String authRequired = HandshakePacketTypes.AUTHENTICATION_REQUIRED; - if (message.startsWith(authRequired)) { + if(message.startsWith(authRequired)) { message = message.substring(authRequired.length()).trim(); } - if (message.length() > 0 && message.charAt(0) == '[') { + if(message.length() > 0 && message.charAt(0) == '[') { int idx = message.indexOf(']', 1); - if (idx != -1) { + if(idx != -1) { String authType = message.substring(1, idx); int type = Integer.MAX_VALUE; try { type = Integer.parseInt(authType); - } catch (NumberFormatException ex) { + }catch(NumberFormatException ex) { } - if (type != Integer.MAX_VALUE) { + if(type != Integer.MAX_VALUE) { authTypeForWarning = type; message = message.substring(idx + 1).trim(); } @@ -70,10 +62,10 @@ public class GuiAuthenticationScreen extends GuiScreen { } public void initGui() { - if (authTypeForWarning != Integer.MAX_VALUE) { + if(authTypeForWarning != Integer.MAX_VALUE) { GuiScreen scr = ConnectionHandshake.displayAuthProtocolConfirm(authTypeForWarning, parent, this); authTypeForWarning = Integer.MAX_VALUE; - if (scr != null) { + if(scr != null) { mc.displayGuiScreen(scr); allowPlaintext = true; return; @@ -86,8 +78,7 @@ public class GuiAuthenticationScreen extends GuiScreen { continueButton.enabled = false; this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 80 + 37, I18n.format("gui.cancel", new Object[0]))); - this.password = new GuiPasswordTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 40, - 200, 20); // 116 + this.password = new GuiPasswordTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 40, 200, 20); // 116 this.password.setFocused(true); this.password.setCanLoseFocus(false); } @@ -97,13 +88,10 @@ public class GuiAuthenticationScreen extends GuiScreen { } protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.id == 1) { + if(parGuiButton.id == 1) { this.mc.displayGuiScreen(new GuiConnecting(retAfterAuthScreen, password.getText())); - } else { + }else { this.mc.displayGuiScreen(parent); - if (!PlatformNetworking.playConnectionState().isClosed()) { - PlatformNetworking.playDisconnect(); - } } } @@ -118,9 +106,9 @@ public class GuiAuthenticationScreen extends GuiScreen { protected void keyTyped(char parChar1, int parInt1) { String pass = password.getText(); - if (parInt1 == KeyboardConstants.KEY_RETURN && pass.length() > 0) { + if(parInt1 == KeyboardConstants.KEY_RETURN && pass.length() > 0) { this.mc.displayGuiScreen(new GuiConnecting(retAfterAuthScreen, pass, allowPlaintext)); - } else { + }else { this.password.textboxKeyTyped(parChar1, parInt1); this.continueButton.enabled = password.getText().length() > 0; } @@ -131,4 +119,14 @@ public class GuiAuthenticationScreen extends GuiScreen { this.password.mouseClicked(parInt1, parInt2, parInt3); } + @Override + public boolean showCopyPasteButtons() { + return password.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + password.fireInputEvent(event, param); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java index d6ac038..e4cee9f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java @@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.profile; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; @@ -51,7 +52,6 @@ public class GuiScreenEditCape extends GuiScreen { public GuiScreenEditCape(GuiScreenEditProfile parent) { this.parent = parent; - updateOptions(); } public void initGui() { @@ -61,6 +61,7 @@ public class GuiScreenEditCape extends GuiScreen { buttonList.add(new GuiButton(0, width / 2 - 100, height / 6 + 168, I18n.format("gui.done"))); buttonList.add(new GuiButton(1, width / 2 - 21, height / 6 + 80, 71, 20, I18n.format("editCape.addCape"))); buttonList.add(new GuiButton(2, width / 2 - 21 + 71, height / 6 + 80, 72, 20, I18n.format("editCape.clearCape"))); + updateOptions(); } private void updateOptions() { @@ -243,7 +244,7 @@ public class GuiScreenEditCape extends GuiScreen { if(EagRuntime.fileChooserHasResult()) { FileChooserResult result = EagRuntime.getFileChooserResult(); if(result != null) { - ImageData loadedCape = ImageData.loadImageFile(result.fileData); + ImageData loadedCape = ImageData.loadImageFile(result.fileData, ImageData.getMimeFromType(result.fileName)); if(loadedCape != null) { if((loadedCape.width == 32 || loadedCape.width == 64) && loadedCape.height == 32) { byte[] resized = new byte[1173]; @@ -258,12 +259,12 @@ public class GuiScreenEditCape extends GuiScreen { EagRuntime.showPopup("The selected image '" + result.fileName + "' is not the right size!\nEaglercraft only supports 32x32 or 64x32 capes"); } }else { - EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a PNG file!"); + EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a supported format!"); } } } if(dropDownOpen) { - if(Mouse.isButtonDown(0)) { + if(PointerInputAbstraction.getVCursorButtonDown(0)) { int skinX = width / 2 - 20; int skinY = height / 6 + 73; int skinWidth = 140; @@ -356,4 +357,4 @@ public class GuiScreenEditCape extends GuiScreen { } } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java index 6814efc..4910f39 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java @@ -3,8 +3,10 @@ package net.lax1dude.eaglercraft.v1_8.profile; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; import net.minecraft.client.audio.PositionedSoundRecord; @@ -22,21 +24,14 @@ import java.io.IOException; /** * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -64,7 +59,6 @@ public class GuiScreenEditProfile extends GuiScreen { public GuiScreenEditProfile(GuiScreen parent) { this.parent = parent; - updateOptions(); } public void initGui() { @@ -73,28 +67,28 @@ public class GuiScreenEditProfile extends GuiScreen { usernameField = new GuiTextField(0, fontRendererObj, width / 2 - 20 + 1, height / 6 + 24 + 1, 138, 20); usernameField.setFocused(true); usernameField.setText(EaglerProfile.getName()); - selectedSlot = EaglerProfile.presetSkinId == -1 ? EaglerProfile.customSkinId - : (EaglerProfile.presetSkinId + EaglerProfile.customSkins.size()); + usernameField.setMaxStringLength(16); + selectedSlot = EaglerProfile.presetSkinId == -1 ? EaglerProfile.customSkinId : (EaglerProfile.presetSkinId + EaglerProfile.customSkins.size()); buttonList.add(new GuiButton(0, width / 2 - 100, height / 6 + 168, I18n.format("gui.done"))); buttonList.add(new GuiButton(1, width / 2 - 21, height / 6 + 110, 71, 20, I18n.format("editProfile.addSkin"))); - buttonList.add( - new GuiButton(2, width / 2 - 21 + 71, height / 6 + 110, 72, 20, I18n.format("editProfile.clearSkin"))); + buttonList.add(new GuiButton(2, width / 2 - 21 + 71, height / 6 + 110, 72, 20, I18n.format("editProfile.clearSkin"))); + updateOptions(); } private void updateOptions() { DefaultSkins[] arr = DefaultSkins.defaultSkinsMap; - if (!EagRuntime.getConfiguration().isAllowFNAWSkins()) { + if(!EagRuntime.getConfiguration().isAllowFNAWSkins()) { DefaultSkins[] arrNoFNAW = new DefaultSkins[arr.length - 5]; System.arraycopy(arr, 0, arrNoFNAW, 0, arrNoFNAW.length); arr = arrNoFNAW; } int numCustom = EaglerProfile.customSkins.size(); String[] n = new String[numCustom + arr.length]; - for (int i = 0; i < numCustom; ++i) { + for(int i = 0; i < numCustom; ++i) { n[i] = EaglerProfile.customSkins.get(i).name; } int numDefault = arr.length; - for (int j = 0; j < numDefault; ++j) { + for(int j = 0; j < numDefault; ++j) { n[numCustom + j] = arr[j].name; } dropDownOptions = n; @@ -105,200 +99,191 @@ public class GuiScreenEditProfile extends GuiScreen { drawCenteredString(fontRendererObj, screenTitle, width / 2, 15, 16777215); drawString(fontRendererObj, I18n.format("editProfile.username"), width / 2 - 20, height / 6 + 8, 10526880); drawString(fontRendererObj, I18n.format("editProfile.playerSkin"), width / 2 - 20, height / 6 + 66, 10526880); - + mousex = mx; mousey = my; - + int skinX = width / 2 - 120; int skinY = height / 6 + 8; int skinWidth = 80; int skinHeight = 130; - + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xFFA0A0A0); drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, 0xFF000015); - + GlStateManager.pushMatrix(); GlStateManager.translate(skinX + 2, skinY - 9, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); - if (selectedSlot > dropDownOptions.length - 1) { + if(selectedSlot > dropDownOptions.length - 1) { selectedSlot = 0; } int numberOfCustomSkins = EaglerProfile.customSkins.size(); int skid = selectedSlot - numberOfCustomSkins; - SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model - : DefaultSkins.getSkinFromId(skid).model; - if (selectedSkinModel == SkinModel.STEVE || selectedSkinModel == SkinModel.ALEX - || (selectedSkinModel.highPoly != null && !this.mc.gameSettings.enableFNAWSkins)) { + SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model : DefaultSkins.getSkinFromId(skid).model; + if(selectedSkinModel == SkinModel.STEVE || selectedSkinModel == SkinModel.ALEX || (selectedSkinModel.highPoly != null && !this.mc.gameSettings.enableFNAWSkins)) { String capesText = I18n.format("editProfile.capes"); int color = 10526880; - if (mx > skinX - 10 && my > skinY - 16 - && mx < skinX + (fontRendererObj.getStringWidth(capesText) * 0.75f) + 10 && my < skinY + 7) { + if(mx > skinX - 10 && my > skinY - 16 && mx < skinX + (fontRendererObj.getStringWidth(capesText) * 0.75f) + 10 && my < skinY + 7) { color = 0xFFCCCC44; Mouse.showCursor(EnumCursorType.HAND); } this.drawString(this.fontRendererObj, EnumChatFormatting.UNDERLINE + capesText, 0, 0, color); } - + GlStateManager.popMatrix(); - + usernameField.drawTextBox(); - if (dropDownOpen || newSkinWaitSteveOrAlex) { + if(dropDownOpen || newSkinWaitSteveOrAlex) { super.drawScreen(0, 0, partialTicks); - } else { + }else { super.drawScreen(mx, my, partialTicks); } - - if (selectedSkinModel.highPoly != null) { - drawCenteredString(fontRendererObj, I18n.format( - this.mc.gameSettings.enableFNAWSkins ? "editProfile.disableFNAW" : "editProfile.enableFNAW"), - width / 2, height / 6 + 150, 10526880); + + if(selectedSkinModel.highPoly != null) { + drawCenteredString(fontRendererObj, I18n.format(this.mc.gameSettings.enableFNAWSkins ? "editProfile.disableFNAW" : "editProfile.enableFNAW"), width / 2, height / 6 + 150, 10526880); } - + skinX = width / 2 - 20; skinY = height / 6 + 82; skinWidth = 140; skinHeight = 22; - + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 21, skinY + skinHeight - 1, -16777216); drawRect(skinX + skinWidth - 20, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, -16777216); - + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - + mc.getTextureManager().bindTexture(eaglerGui); drawTexturedModalRect(skinX + skinWidth - 18, skinY + 3, 0, 0, 16, 16); - + drawString(fontRendererObj, dropDownOptions[selectedSlot], skinX + 5, skinY + 7, 14737632); - + skinX = width / 2 - 20; skinY = height / 6 + 103; skinWidth = 140; skinHeight = (height - skinY - 10); slotsVisible = (skinHeight / 10); - if (slotsVisible > dropDownOptions.length) - slotsVisible = dropDownOptions.length; + if(slotsVisible > dropDownOptions.length) slotsVisible = dropDownOptions.length; skinHeight = slotsVisible * 10 + 7; skinsHeight = skinHeight; - if (scrollPos == -1) { + if(scrollPos == -1) { scrollPos = selectedSlot - 2; } - if (scrollPos > (dropDownOptions.length - slotsVisible)) { + if(scrollPos > (dropDownOptions.length - slotsVisible)) { scrollPos = (dropDownOptions.length - slotsVisible); } - if (scrollPos < 0) { + if(scrollPos < 0) { scrollPos = 0; } - if (dropDownOpen) { + if(dropDownOpen) { drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, -16777216); - for (int i = 0; i < slotsVisible; i++) { - if (i + scrollPos < dropDownOptions.length) { - if (selectedSlot == i + scrollPos) { - drawRect(skinX + 1, skinY + i * 10 + 4, skinX + skinWidth - 1, skinY + i * 10 + 14, 0x77ffffff); - } else if (mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i * 10 + 5) - && my < (skinY + i * 10 + 15)) { - drawRect(skinX + 1, skinY + i * 10 + 4, skinX + skinWidth - 1, skinY + i * 10 + 14, 0x55ffffff); + for(int i = 0; i < slotsVisible; i++) { + if(i + scrollPos < dropDownOptions.length) { + if(selectedSlot == i + scrollPos) { + drawRect(skinX + 1, skinY + i*10 + 4, skinX + skinWidth - 1, skinY + i*10 + 14, 0x77ffffff); + }else if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i*10 + 5) && my < (skinY + i*10 + 15)) { + drawRect(skinX + 1, skinY + i*10 + 4, skinX + skinWidth - 1, skinY + i*10 + 14, 0x55ffffff); } - drawString(fontRendererObj, dropDownOptions[i + scrollPos], skinX + 5, skinY + 5 + i * 10, - 14737632); + drawString(fontRendererObj, dropDownOptions[i + scrollPos], skinX + 5, skinY + 5 + i*10, 14737632); } } int scrollerSize = skinHeight * slotsVisible / dropDownOptions.length; int scrollerPos = skinHeight * scrollPos / dropDownOptions.length; - drawRect(skinX + skinWidth - 4, skinY + scrollerPos + 1, skinX + skinWidth - 1, - skinY + scrollerPos + scrollerSize, 0xff888888); + drawRect(skinX + skinWidth - 4, skinY + scrollerPos + 1, skinX + skinWidth - 1, skinY + scrollerPos + scrollerSize, 0xff888888); } - if (!EagRuntime.getConfiguration().isDemo()) { + if(!EagRuntime.getConfiguration().isDemo()) { GlStateManager.pushMatrix(); GlStateManager.scale(0.75f, 0.75f, 0.75f); GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); String text = I18n.format("editProfile.importExport"); - + int w = mc.fontRendererObj.getStringWidth(text); boolean hover = mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12; - if (hover) { + if(hover) { Mouse.showCursor(EnumCursorType.HAND); } - + drawString(mc.fontRendererObj, EnumChatFormatting.UNDERLINE + text, 5, 5, hover ? 0xFFEEEE22 : 0xFFCCCCCC); - + GlStateManager.popMatrix(); } int xx = width / 2 - 80; int yy = height / 6 + 130; - - if (newSkinWaitSteveOrAlex && selectedSlot < numberOfCustomSkins) { + + if(newSkinWaitSteveOrAlex && selectedSlot < numberOfCustomSkins) { skinWidth = 70; skinHeight = 120; - + CustomSkin newSkin = EaglerProfile.customSkins.get(selectedSlot); - + GlStateManager.clear(GL_DEPTH_BUFFER_BIT); - + skinX = width / 2 - 90; skinY = height / 4; xx = skinX + 35; yy = skinY + 117; - + boolean mouseOver = mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight; int cc = mouseOver ? 0xFFDDDD99 : 0xFF555555; - + GlStateManager.enableBlend(); GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); drawRect(0, 0, width, height, 0xbb000000); drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xbb000000); GlStateManager.disableBlend(); - + drawRect(skinX, skinY, skinX + 1, skinY + skinHeight, cc); drawRect(skinX, skinY, skinX + skinWidth, skinY + 1, cc); drawRect(skinX + skinWidth - 1, skinY, skinX + skinWidth, skinY + skinHeight, cc); drawRect(skinX, skinY + skinHeight - 1, skinX + skinWidth, skinY + skinHeight, cc); - - if (mouseOver) { + + if(mouseOver) { drawCenteredString(fontRendererObj, "Steve", skinX + skinWidth / 2, skinY + skinHeight + 6, cc); } - + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.STEVE, newSkin.getResource(), EaglerProfile.getActiveCapeResourceLocation()); - + skinX = width / 2 + 20; skinY = height / 4; xx = skinX + 35; yy = skinY + 117; - + mouseOver = mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight; cc = mouseOver ? 0xFFDDDD99 : 0xFF555555; - + GlStateManager.enableBlend(); GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xbb000000); GlStateManager.disableBlend(); - + drawRect(skinX, skinY, skinX + 1, skinY + skinHeight, cc); drawRect(skinX, skinY, skinX + skinWidth, skinY + 1, cc); drawRect(skinX + skinWidth - 1, skinY, skinX + skinWidth, skinY + skinHeight, cc); drawRect(skinX, skinY + skinHeight - 1, skinX + skinWidth, skinY + skinHeight, cc); - - if (mouseOver) { + + if(mouseOver) { drawCenteredString(fontRendererObj, "Alex", skinX + skinWidth / 2, skinY + skinHeight + 8, cc); } - + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.ALEX, newSkin.getResource(), EaglerProfile.getActiveCapeResourceLocation()); - } else { + }else { skinX = this.width / 2 - 120; skinY = this.height / 6 + 8; skinWidth = 80; skinHeight = 130; - + ResourceLocation texture; - if (skid < 0) { + if(skid < 0) { texture = EaglerProfile.customSkins.get(selectedSlot).getResource(); - } else { + }else { texture = DefaultSkins.getSkinFromId(skid).location; } @@ -306,19 +291,19 @@ public class GuiScreenEditProfile extends GuiScreen { newSkinWaitSteveOrAlex ? height / 2 : my, false, selectedSkinModel, texture, EaglerProfile.getActiveCapeResourceLocation()); } - + } public void handleMouseInput() throws IOException { super.handleMouseInput(); - if (dropDownOpen) { + if(dropDownOpen) { int var1 = Mouse.getEventDWheel(); - if (var1 < 0) { + if(var1 < 0) { scrollPos += 3; } - if (var1 > 0) { + if(var1 > 0) { scrollPos -= 3; - if (scrollPos < 0) { + if(scrollPos < 0) { scrollPos = 0; } } @@ -326,14 +311,14 @@ public class GuiScreenEditProfile extends GuiScreen { } protected void actionPerformed(GuiButton par1GuiButton) { - if (!dropDownOpen) { - if (par1GuiButton.id == 0) { + if(!dropDownOpen) { + if(par1GuiButton.id == 0) { safeProfile(); EaglerProfile.save(); this.mc.displayGuiScreen((GuiScreen) parent); - } else if (par1GuiButton.id == 1) { + }else if(par1GuiButton.id == 1) { EagRuntime.displayFileChooser("image/png", "png"); - } else if (par1GuiButton.id == 2) { + }else if(par1GuiButton.id == 2) { EaglerProfile.clearCustomSkins(); safeProfile(); EaglerProfile.save(); @@ -345,22 +330,22 @@ public class GuiScreenEditProfile extends GuiScreen { public void updateScreen() { usernameField.updateCursorCounter(); - if (EagRuntime.fileChooserHasResult()) { + if(EagRuntime.fileChooserHasResult()) { FileChooserResult result = EagRuntime.getFileChooserResult(); - if (result != null) { - ImageData loadedSkin = ImageData.loadImageFile(result.fileData); - if (loadedSkin != null) { + if(result != null) { + ImageData loadedSkin = ImageData.loadImageFile(result.fileData, ImageData.getMimeFromType(result.fileName)); + if(loadedSkin != null) { boolean isLegacy = loadedSkin.width == 64 && loadedSkin.height == 32; boolean isModern = loadedSkin.width == 64 && loadedSkin.height == 64; - if (isLegacy) { + if(isLegacy) { ImageData newSkin = new ImageData(64, 64, true); SkinConverter.convert64x32to64x64(loadedSkin, newSkin); loadedSkin = newSkin; isModern = true; } - if (isModern) { + if(isModern) { byte[] rawSkin = new byte[16384]; - for (int i = 0, j, k; i < 4096; ++i) { + for(int i = 0, j, k; i < 4096; ++i) { j = i << 2; k = loadedSkin.pixels[i]; rawSkin[j] = (byte)(k >>> 24); @@ -368,45 +353,43 @@ public class GuiScreenEditProfile extends GuiScreen { rawSkin[j + 2] = (byte)(k >>> 8); rawSkin[j + 3] = (byte)(k & 0xFF); } - for (int y = 20; y < 32; ++y) { - for (int x = 16; x < 40; ++x) { - rawSkin[(y << 8) | (x << 2)] = (byte) 0xff; + for(int y = 20; y < 32; ++y) { + for(int x = 16; x < 40; ++x) { + rawSkin[(y << 8) | (x << 2)] = (byte)0xff; } } int k; - if ((k = EaglerProfile.addCustomSkin(result.fileName, rawSkin)) != -1) { + if((k = EaglerProfile.addCustomSkin(result.fileName, rawSkin)) != -1) { selectedSlot = k; newSkinWaitSteveOrAlex = true; updateOptions(); safeProfile(); EaglerProfile.save(); } - } else { - EagRuntime.showPopup("The selected image '" + result.fileName - + "' is not the right size!\nEaglercraft only supports 64x32 or 64x64 skins"); + }else { + EagRuntime.showPopup("The selected image '" + result.fileName + "' is not the right size!\nEaglercraft only supports 64x32 or 64x64 skins"); } - } else { - EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a PNG file!"); + }else { + EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a supported format!"); } } } - if (dropDownOpen) { - if (Mouse.isButtonDown(0)) { + if(dropDownOpen) { + if(PointerInputAbstraction.getVCursorButtonDown(0)) { int skinX = width / 2 - 20; int skinY = height / 6 + 103; int skinWidth = 140; - if (mousex >= (skinX + skinWidth - 10) && mousex < (skinX + skinWidth) && mousey >= skinY - && mousey < (skinY + skinsHeight)) { + if(mousex >= (skinX + skinWidth - 10) && mousex < (skinX + skinWidth) && mousey >= skinY && mousey < (skinY + skinsHeight)) { dragging = true; } - if (dragging) { + if(dragging) { int scrollerSize = skinsHeight * slotsVisible / dropDownOptions.length; scrollPos = (mousey - skinY - (scrollerSize / 2)) * dropDownOptions.length / skinsHeight; } - } else { + }else { dragging = false; } - } else { + }else { dragging = false; } } @@ -417,64 +400,58 @@ public class GuiScreenEditProfile extends GuiScreen { protected void keyTyped(char c, int k) { usernameField.textboxKeyTyped(c, k); - + String text = usernameField.getText(); - if (text.length() > 16) - text = text.substring(0, 16); + if(text.length() > 16) text = text.substring(0, 16); text = text.replaceAll("[^A-Za-z0-9]", "_"); usernameField.updateText(text); - - if (k == 200 && selectedSlot > 0) { + + if(k == 200 && selectedSlot > 0) { --selectedSlot; scrollPos = selectedSlot - 2; } - if (k == 208 && selectedSlot < (dropDownOptions.length - 1)) { + if(k == 208 && selectedSlot < (dropDownOptions.length - 1)) { ++selectedSlot; scrollPos = selectedSlot - 2; } } - + protected void mouseClicked(int mx, int my, int button) { usernameField.mouseClicked(mx, my, button); if (button == 0) { - if (!EagRuntime.getConfiguration().isDemo()) { + if(!EagRuntime.getConfiguration().isDemo()) { int w = mc.fontRendererObj.getStringWidth(I18n.format("editProfile.importExport")); - if (mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12) { + if(mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12) { safeProfile(); EaglerProfile.save(); mc.displayGuiScreen(new GuiScreenImportExportProfile(this)); - mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); return; } } - + int skinX, skinY; int skid = selectedSlot - EaglerProfile.customSkins.size(); - SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model - : DefaultSkins.getSkinFromId(skid).model; - if (selectedSkinModel == SkinModel.STEVE || selectedSkinModel == SkinModel.ALEX - || (selectedSkinModel.highPoly != null && !this.mc.gameSettings.enableFNAWSkins)) { + SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model : DefaultSkins.getSkinFromId(skid).model; + if(selectedSkinModel == SkinModel.STEVE || selectedSkinModel == SkinModel.ALEX || (selectedSkinModel.highPoly != null && !this.mc.gameSettings.enableFNAWSkins)) { skinX = this.width / 2 - 120; skinY = this.height / 6 + 8; String capesText = I18n.format("editProfile.capes"); - if (mx > skinX - 10 && my > skinY - 16 - && mx < skinX + (fontRendererObj.getStringWidth(capesText) * 0.75f) + 10 && my < skinY + 7) { + if(mx > skinX - 10 && my > skinY - 16 && mx < skinX + (fontRendererObj.getStringWidth(capesText) * 0.75f) + 10 && my < skinY + 7) { safeProfile(); this.mc.displayGuiScreen(new GuiScreenEditCape(this)); - mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); return; } } - - if (newSkinWaitSteveOrAlex) { + + if(newSkinWaitSteveOrAlex) { skinX = width / 2 - 90; skinY = height / 4; int skinWidth = 70; int skinHeight = 120; - if (mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { - if (selectedSlot < EaglerProfile.customSkins.size()) { + if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { + if(selectedSlot < EaglerProfile.customSkins.size()) { newSkinWaitSteveOrAlex = false; EaglerProfile.customSkins.get(selectedSlot).model = SkinModel.STEVE; safeProfile(); @@ -483,21 +460,21 @@ public class GuiScreenEditProfile extends GuiScreen { } skinX = width / 2 + 20; skinY = height / 4; - if (mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { - if (selectedSlot < EaglerProfile.customSkins.size()) { + if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { + if(selectedSlot < EaglerProfile.customSkins.size()) { EaglerProfile.customSkins.get(selectedSlot).model = SkinModel.ALEX; newSkinWaitSteveOrAlex = false; safeProfile(); } } return; - } else if (selectedSlot < EaglerProfile.customSkins.size()) { + }else if(selectedSlot < EaglerProfile.customSkins.size()) { skinX = width / 2 - 120; skinY = height / 6 + 18; int skinWidth = 80; int skinHeight = 120; - if (mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { - if (selectedSlot < EaglerProfile.customSkins.size()) { + if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { + if(selectedSlot < EaglerProfile.customSkins.size()) { newSkinWaitSteveOrAlex = true; return; } @@ -505,29 +482,28 @@ public class GuiScreenEditProfile extends GuiScreen { } skinX = width / 2 + 140 - 40; skinY = height / 6 + 82; - - if (mx >= skinX && mx < (skinX + 20) && my >= skinY && my < (skinY + 22)) { + + if(mx >= skinX && mx < (skinX + 20) && my >= skinY && my < (skinY + 22)) { dropDownOpen = !dropDownOpen; return; } - + skinX = width / 2 - 20; skinY = height / 6 + 82; int skinWidth = 140; int skinHeight = skinsHeight; - - if (!(mx >= skinX && mx < (skinX + skinWidth) && my >= skinY && my < (skinY + skinHeight + 22))) { + + if(!(mx >= skinX && mx < (skinX + skinWidth) && my >= skinY && my < (skinY + skinHeight + 22))) { dragging = false; - if (dropDownOpen) { + if(dropDownOpen) { dropDownOpen = false; return; } - } else if (dropDownOpen && !dragging) { + }else if(dropDownOpen && !dragging) { skinY += 21; - for (int i = 0; i < slotsVisible; i++) { - if (i + scrollPos < dropDownOptions.length) { - if (mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i * 10 + 5) - && my < (skinY + i * 10 + 15)) { + for(int i = 0; i < slotsVisible; i++) { + if(i + scrollPos < dropDownOptions.length) { + if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i * 10 + 5) && my < (skinY + i * 10 + 15)) { selectedSlot = i + scrollPos; dropDownOpen = false; dragging = false; @@ -539,24 +515,34 @@ public class GuiScreenEditProfile extends GuiScreen { } super.mouseClicked(mx, my, button); } - + protected void safeProfile() { int customLen = EaglerProfile.customSkins.size(); - if (selectedSlot < customLen) { + if(selectedSlot < customLen) { EaglerProfile.presetSkinId = -1; EaglerProfile.customSkinId = selectedSlot; - } else { + }else { EaglerProfile.presetSkinId = selectedSlot - customLen; EaglerProfile.customSkinId = -1; } String name = usernameField.getText().trim(); - while (name.length() < 3) { + while(name.length() < 3) { name = name + "_"; } - if (name.length() > 16) { + if(name.length() > 16) { name = name.substring(0, 16); } EaglerProfile.setName(name); } + @Override + public boolean showCopyPasteButtons() { + return usernameField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + usernameField.fireInputEvent(event, param); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java index e0a0a31..a08dc91 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java @@ -14,21 +14,14 @@ import net.minecraft.client.resources.I18n; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -57,24 +50,15 @@ public class GuiScreenImportProfile extends GuiScreen { } public void initGui() { - this.buttonList.add(importProfile = new GuiButton(2, this.width / 2 - 100, this.height / 4, - I18n.format("settingsBackup.import.option.profile") + " " - + I18n.format(doImportProfile ? "gui.yes" : "gui.no"))); + this.buttonList.add(importProfile = new GuiButton(2, this.width / 2 - 100, this.height / 4, I18n.format("settingsBackup.import.option.profile") + " " + I18n.format(doImportProfile ? "gui.yes" : "gui.no"))); importProfile.enabled = importer.hasProfile(); - this.buttonList.add(importSettings = new GuiButton(3, this.width / 2 - 100, this.height / 4 + 25, - I18n.format("settingsBackup.import.option.settings") + " " - + I18n.format(doImportSettings ? "gui.yes" : "gui.no"))); + this.buttonList.add(importSettings = new GuiButton(3, this.width / 2 - 100, this.height / 4 + 25, I18n.format("settingsBackup.import.option.settings") + " " + I18n.format(doImportSettings ? "gui.yes" : "gui.no"))); importSettings.enabled = importer.hasProfile(); - this.buttonList.add(importServers = new GuiButton(4, this.width / 2 - 100, this.height / 4 + 50, - I18n.format("settingsBackup.import.option.servers") + " " - + I18n.format(doImportServers ? "gui.yes" : "gui.no"))); + this.buttonList.add(importServers = new GuiButton(4, this.width / 2 - 100, this.height / 4 + 50, I18n.format("settingsBackup.import.option.servers") + " " + I18n.format(doImportServers ? "gui.yes" : "gui.no"))); importServers.enabled = importer.hasServers(); - this.buttonList.add(importResourcePacks = new GuiButton(5, this.width / 2 - 100, this.height / 4 + 75, - I18n.format("settingsBackup.import.option.resourcePacks") + " " - + I18n.format(doImportResourcePacks ? "gui.yes" : "gui.no"))); + this.buttonList.add(importResourcePacks = new GuiButton(5, this.width / 2 - 100, this.height / 4 + 75, I18n.format("settingsBackup.import.option.resourcePacks") + " " + I18n.format(doImportResourcePacks ? "gui.yes" : "gui.no"))); importResourcePacks.enabled = importer.hasResourcePacks() && EaglerFolderResourcePack.isSupported(); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 115, - I18n.format("settingsBackup.import.option.import"))); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 115, I18n.format("settingsBackup.import.option.import"))); this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 140, I18n.format("gui.cancel"))); } @@ -87,20 +71,17 @@ public class GuiScreenImportProfile extends GuiScreen { } protected void actionPerformed(GuiButton par1GuiButton) { - if (par1GuiButton.id == 0) { - if (!doImportProfile && !doImportSettings && !doImportServers && !doImportResourcePacks) { + if(par1GuiButton.id == 0) { + if(!doImportProfile && !doImportSettings && !doImportServers && !doImportResourcePacks) { mc.displayGuiScreen(back); - } else { - mc.loadingScreen.eaglerShow(I18n.format("settingsBackup.importing.1"), - I18n.format("settingsBackup.importing.2")); + }else { + mc.loadingScreen.eaglerShow(I18n.format("settingsBackup.importing.1"), I18n.format("settingsBackup.importing.2")); try { - List list1 = new ArrayList(mc.gameSettings.resourcePacks); - List list2 = new ArrayList(mc.gameSettings.field_183018_l); - importer.importProfileAndSettings(doImportProfile, doImportSettings, doImportServers, - doImportResourcePacks); - boolean resourcePacksChanged = !mc.gameSettings.resourcePacks.equals(list1) - || !mc.gameSettings.field_183018_l.equals(list2); - if (resourcePacksChanged || (doImportResourcePacks && (list1.size() > 0 || list2.size() > 0))) { + List list1 = new ArrayList<>(mc.gameSettings.resourcePacks); + List list2 = new ArrayList<>(mc.gameSettings.field_183018_l); + importer.importProfileAndSettings(doImportProfile, doImportSettings, doImportServers, doImportResourcePacks); + boolean resourcePacksChanged = !mc.gameSettings.resourcePacks.equals(list1) || !mc.gameSettings.field_183018_l.equals(list2); + if(resourcePacksChanged || (doImportResourcePacks && (list1.size() > 0 || list2.size() > 0))) { mc.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), I18n.format("resourcePack.load.pleaseWait")); mc.getResourcePackRepository().reconstruct(mc.gameSettings); @@ -109,35 +90,29 @@ public class GuiScreenImportProfile extends GuiScreen { mc.displayGuiScreen(back); } catch (IOException e) { EagRuntime.debugPrintStackTrace(e); - mc.displayGuiScreen(new GuiScreenGenericErrorMessage("settingsBackup.importing.failed.1", - "settingsBackup.importing.failed.2", back)); + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("settingsBackup.importing.failed.1", "settingsBackup.importing.failed.2", back)); } } - } else if (par1GuiButton.id == 1) { + }else if(par1GuiButton.id == 1) { mc.displayGuiScreen(back); - } else if (par1GuiButton.id == 2) { + }else if(par1GuiButton.id == 2) { doImportProfile = !doImportProfile; - importProfile.displayString = I18n.format("settingsBackup.import.option.profile") + " " - + I18n.format(doImportProfile ? "gui.yes" : "gui.no"); - } else if (par1GuiButton.id == 3) { + importProfile.displayString = I18n.format("settingsBackup.import.option.profile") + " " + I18n.format(doImportProfile ? "gui.yes" : "gui.no"); + }else if(par1GuiButton.id == 3) { doImportSettings = !doImportSettings; - importSettings.displayString = I18n.format("settingsBackup.import.option.settings") + " " - + I18n.format(doImportSettings ? "gui.yes" : "gui.no"); - } else if (par1GuiButton.id == 4) { + importSettings.displayString = I18n.format("settingsBackup.import.option.settings") + " " + I18n.format(doImportSettings ? "gui.yes" : "gui.no"); + }else if(par1GuiButton.id == 4) { doImportServers = !doImportServers; - importServers.displayString = I18n.format("settingsBackup.import.option.servers") + " " - + I18n.format(doImportServers ? "gui.yes" : "gui.no"); - } else if (par1GuiButton.id == 5) { + importServers.displayString = I18n.format("settingsBackup.import.option.servers") + " " + I18n.format(doImportServers ? "gui.yes" : "gui.no"); + }else if(par1GuiButton.id == 5) { doImportResourcePacks = !doImportResourcePacks; - importResourcePacks.displayString = I18n.format("settingsBackup.import.option.resourcePacks") + " " - + I18n.format(doImportResourcePacks ? "gui.yes" : "gui.no"); + importResourcePacks.displayString = I18n.format("settingsBackup.import.option.resourcePacks") + " " + I18n.format(doImportResourcePacks ? "gui.yes" : "gui.no"); } } public void drawScreen(int par1, int par2, float par3) { this.drawDefaultBackground(); - this.drawCenteredString(this.fontRendererObj, I18n.format("settingsBackup.import.title"), this.width / 2, - this.height / 4 - 25, 16777215); + this.drawCenteredString(this.fontRendererObj, I18n.format("settingsBackup.import.title"), this.width / 2, this.height / 4 - 25, 16777215); super.drawScreen(par1, par2, par3); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java index 4445b32..8ac3901 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java @@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.profile; import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; @@ -167,15 +168,13 @@ public class RenderHighPoly extends RenderPlayer { EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(highPolySkin.bodyModel)); } float jumpFactor = 0.0f; - - if (highPolySkin.headModel != null) { - if (highPolySkin == HighPolySkin.BABY_CHARLES) { - long millis = System.currentTimeMillis(); - float partialTicks = (float) ((millis - abstractclientplayer.eaglerHighPolyAnimationTick) - * 0.02); - // long l50 = millis / 50l * 50l; - // boolean runTick = par1EntityPlayer.eaglerHighPolyAnimationTick < l50 && - // millis >= l50; + + if(highPolySkin.headModel != null) { + if(highPolySkin == HighPolySkin.BABY_CHARLES) { + long millis = EagRuntime.steadyTimeMillis(); + float partialTicks = (float) ((millis - abstractclientplayer.eaglerHighPolyAnimationTick) * 0.02); + //long l50 = millis / 50l * 50l; + //boolean runTick = par1EntityPlayer.eaglerHighPolyAnimationTick < l50 && millis >= l50; abstractclientplayer.eaglerHighPolyAnimationTick = millis; if (partialTicks < 0.0f) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java index 11cf67d..788e398 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java @@ -1,242 +1,249 @@ -package net.lax1dude.eaglercraft.v1_8.profile; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.play.client.C17PacketCustomPayload; -import net.minecraft.util.ResourceLocation; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ServerCapeCache { - - private static final Logger logger = LogManager.getLogger("ServerCapeCache"); - - public class CapeCacheEntry { - - protected final boolean isPresetCape; - protected final int presetCapeId; - protected final CacheCustomCape customCape; - - protected long lastCacheHit = System.currentTimeMillis(); - - protected CapeCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { - this.isPresetCape = false; - this.presetCapeId = -1; - this.customCape = new CacheCustomCape(textureInstance, resourceLocation); - ServerCapeCache.this.textureManager.loadTexture(resourceLocation, textureInstance); - } - - /** - * Use only for the constant for the client player - */ - protected CapeCacheEntry(ResourceLocation resourceLocation) { - this.isPresetCape = false; - this.presetCapeId = -1; - this.customCape = new CacheCustomCape(null, resourceLocation); - } - - protected CapeCacheEntry(int presetSkinId) { - this.isPresetCape = true; - this.presetCapeId = presetSkinId; - this.customCape = null; - } - - public ResourceLocation getResourceLocation() { - if(isPresetCape) { - return DefaultCapes.getCapeFromId(presetCapeId).location; - }else { - if(customCape != null) { - return customCape.resourceLocation; - }else { - return null; - } - } - } - - protected void free() { - if(!isPresetCape && customCape.resourceLocation != null) { - ServerCapeCache.this.textureManager.deleteTexture(customCape.resourceLocation); - } - } - - } - - protected static class CacheCustomCape { - - protected final EaglerSkinTexture textureInstance; - protected final ResourceLocation resourceLocation; - - protected CacheCustomCape(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { - this.textureInstance = textureInstance; - this.resourceLocation = resourceLocation; - } - - } - - private final CapeCacheEntry defaultCacheEntry = new CapeCacheEntry(0); - private final Map capesCache = new HashMap(); - private final Map waitingCapes = new HashMap(); - private final Map evictedCapes = new HashMap(); - - private final EaglercraftNetworkManager networkManager; - protected final TextureManager textureManager; - - private final EaglercraftUUID clientPlayerId; - private final CapeCacheEntry clientPlayerCacheEntry; - - private long lastFlush = System.currentTimeMillis(); - private long lastFlushReq = System.currentTimeMillis(); - private long lastFlushEvict = System.currentTimeMillis(); - - private static int texId = 0; - - public ServerCapeCache(EaglercraftNetworkManager networkManager, TextureManager textureManager) { - this.networkManager = networkManager; - this.textureManager = textureManager; - this.clientPlayerId = EaglerProfile.getPlayerUUID(); - this.clientPlayerCacheEntry = new CapeCacheEntry(EaglerProfile.getActiveCapeResourceLocation()); - } - - public CapeCacheEntry getClientPlayerCape() { - return clientPlayerCacheEntry; - } - - public CapeCacheEntry getCape(EaglercraftUUID player) { - if(player.equals(clientPlayerId)) { - return clientPlayerCacheEntry; - } - CapeCacheEntry etr = capesCache.get(player); - if(etr == null) { - if(!waitingCapes.containsKey(player) && !evictedCapes.containsKey(player)) { - waitingCapes.put(player, System.currentTimeMillis()); - PacketBuffer buffer; - try { - buffer = CapePackets.writeGetOtherCape(player); - }catch(IOException ex) { - logger.error("Could not write cape request packet!"); - logger.error(ex); - return defaultCacheEntry; - } - networkManager.sendPacket(new C17PacketCustomPayload("EAG|Capes-1.8", buffer)); - } - return defaultCacheEntry; - }else { - etr.lastCacheHit = System.currentTimeMillis(); - return etr; - } - } - - public void cacheCapePreset(EaglercraftUUID player, int presetId) { - if(waitingCapes.remove(player) != null) { - CapeCacheEntry etr = capesCache.remove(player); - if(etr != null) { - etr.free(); - } - capesCache.put(player, new CapeCacheEntry(presetId)); - }else { - logger.error("Unsolicited cape response recieved for \"{}\"! (preset {})", player, presetId); - } - } - - public void cacheCapeCustom(EaglercraftUUID player, byte[] pixels) { - if(waitingCapes.remove(player) != null) { - CapeCacheEntry etr = capesCache.remove(player); - if(etr != null) { - etr.free(); - } - byte[] pixels32x32 = new byte[4096]; - SkinConverter.convertCape23x17RGBto32x32RGBA(pixels, pixels32x32); - try { - etr = new CapeCacheEntry(new EaglerSkinTexture(pixels32x32, 32, 32), - new ResourceLocation("eagler:capes/multiplayer/tex_" + texId++)); - }catch(Throwable t) { - etr = new CapeCacheEntry(0); - logger.error("Could not process custom skin packet for \"{}\"!", player); - logger.error(t); - } - capesCache.put(player, etr); - }else { - logger.error("Unsolicited skin response recieved for \"{}\"!", player); - } - } - - public void flush() { - long millis = System.currentTimeMillis(); - if(millis - lastFlushReq > 5000l) { - lastFlushReq = millis; - if(!waitingCapes.isEmpty()) { - Iterator waitingItr = waitingCapes.values().iterator(); - while(waitingItr.hasNext()) { - if(millis - waitingItr.next().longValue() > 30000l) { - waitingItr.remove(); - } - } - } - } - if(millis - lastFlushEvict > 1000l) { - lastFlushEvict = millis; - if(!evictedCapes.isEmpty()) { - Iterator evictItr = evictedCapes.values().iterator(); - while(evictItr.hasNext()) { - if(millis - evictItr.next().longValue() > 3000l) { - evictItr.remove(); - } - } - } - } - if(millis - lastFlush > 60000l) { - lastFlush = millis; - if(!capesCache.isEmpty()) { - Iterator entryItr = capesCache.values().iterator(); - while(entryItr.hasNext()) { - CapeCacheEntry etr = entryItr.next(); - if(millis - etr.lastCacheHit > 900000l) { // 15 minutes - entryItr.remove(); - etr.free(); - } - } - } - } - } - - public void destroy() { - Iterator entryItr = capesCache.values().iterator(); - while(entryItr.hasNext()) { - entryItr.next().free(); - } - capesCache.clear(); - waitingCapes.clear(); - evictedCapes.clear(); - } - - public void evictCape(EaglercraftUUID uuid) { - evictedCapes.put(uuid, Long.valueOf(System.currentTimeMillis())); - CapeCacheEntry etr = capesCache.remove(uuid); - if(etr != null) { - etr.free(); - } - } - -} \ No newline at end of file +package net.lax1dude.eaglercraft.v1_8.profile; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetOtherCapeEAG; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerCapeCache { + + private static final Logger logger = LogManager.getLogger("ServerCapeCache"); + + public class CapeCacheEntry { + + protected final boolean isPresetCape; + protected final int presetCapeId; + protected final CacheCustomCape customCape; + + protected long lastCacheHit = EagRuntime.steadyTimeMillis(); + + protected CapeCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { + this.isPresetCape = false; + this.presetCapeId = -1; + this.customCape = new CacheCustomCape(textureInstance, resourceLocation); + ServerCapeCache.this.textureManager.loadTexture(resourceLocation, textureInstance); + } + + /** + * Use only for the constant for the client player + */ + protected CapeCacheEntry(ResourceLocation resourceLocation) { + this.isPresetCape = false; + this.presetCapeId = -1; + this.customCape = new CacheCustomCape(null, resourceLocation); + } + + protected CapeCacheEntry(int presetSkinId) { + this.isPresetCape = true; + this.presetCapeId = presetSkinId; + this.customCape = null; + } + + public ResourceLocation getResourceLocation() { + if(isPresetCape) { + return DefaultCapes.getCapeFromId(presetCapeId).location; + }else { + if(customCape != null) { + return customCape.resourceLocation; + }else { + return null; + } + } + } + + protected void free() { + if(!isPresetCape && customCape.resourceLocation != null) { + ServerCapeCache.this.textureManager.deleteTexture(customCape.resourceLocation); + } + } + + } + + protected static class CacheCustomCape { + + protected final EaglerSkinTexture textureInstance; + protected final ResourceLocation resourceLocation; + + protected CacheCustomCape(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { + this.textureInstance = textureInstance; + this.resourceLocation = resourceLocation; + } + + } + + private final CapeCacheEntry defaultCacheEntry = new CapeCacheEntry(0); + private final Map capesCache = new HashMap<>(); + private final Map waitingCapes = new HashMap<>(); + private final Map evictedCapes = new HashMap<>(); + + private final NetHandlerPlayClient netHandler; + protected final TextureManager textureManager; + + private final EaglercraftUUID clientPlayerId; + private CapeCacheEntry clientPlayerCacheEntry; + + private long lastFlush = EagRuntime.steadyTimeMillis(); + private long lastFlushReq = EagRuntime.steadyTimeMillis(); + private long lastFlushEvict = EagRuntime.steadyTimeMillis(); + + private static int texId = 0; + public static boolean needReloadClientCape = false; + + public ServerCapeCache(NetHandlerPlayClient netHandler, TextureManager textureManager) { + this.netHandler = netHandler; + this.textureManager = textureManager; + this.clientPlayerId = EaglerProfile.getPlayerUUID(); + reloadClientPlayerCape(); + } + + public void reloadClientPlayerCape() { + needReloadClientCape = false; + this.clientPlayerCacheEntry = new CapeCacheEntry(EaglerProfile.getActiveCapeResourceLocation()); + } + + public CapeCacheEntry getClientPlayerCape() { + return clientPlayerCacheEntry; + } + + public CapeCacheEntry getCape(EaglercraftUUID player) { + if(player.equals(clientPlayerId)) { + return clientPlayerCacheEntry; + } + CapeCacheEntry etr = capesCache.get(player); + if(etr == null) { + if(!waitingCapes.containsKey(player) && !evictedCapes.containsKey(player)) { + waitingCapes.put(player, EagRuntime.steadyTimeMillis()); + netHandler.sendEaglerMessage(new CPacketGetOtherCapeEAG(player.msb, player.lsb)); + } + return defaultCacheEntry; + }else { + etr.lastCacheHit = EagRuntime.steadyTimeMillis(); + return etr; + } + } + + public void cacheCapePreset(EaglercraftUUID player, int presetId) { + if(waitingCapes.remove(player) != null) { + CapeCacheEntry etr = capesCache.remove(player); + if(etr != null) { + etr.free(); + } + capesCache.put(player, new CapeCacheEntry(presetId)); + }else { + logger.error("Unsolicited cape response recieved for \"{}\"! (preset {})", player, presetId); + } + } + + public void cacheCapeCustom(EaglercraftUUID player, byte[] pixels) { + if(waitingCapes.remove(player) != null) { + CapeCacheEntry etr = capesCache.remove(player); + if(etr != null) { + etr.free(); + } + byte[] pixels32x32 = new byte[4096]; + SkinConverter.convertCape23x17RGBto32x32RGBA(pixels, pixels32x32); + try { + etr = new CapeCacheEntry(new EaglerSkinTexture(pixels32x32, 32, 32), + new ResourceLocation("eagler:capes/multiplayer/tex_" + texId++)); + }catch(Throwable t) { + etr = new CapeCacheEntry(0); + logger.error("Could not process custom skin packet for \"{}\"!", player); + logger.error(t); + } + capesCache.put(player, etr); + }else { + logger.error("Unsolicited skin response recieved for \"{}\"!", player); + } + } + + public void flush() { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastFlushReq > 5000l) { + lastFlushReq = millis; + if(!waitingCapes.isEmpty()) { + Iterator waitingItr = waitingCapes.values().iterator(); + while(waitingItr.hasNext()) { + if(millis - waitingItr.next().longValue() > 30000l) { + waitingItr.remove(); + } + } + } + } + if(millis - lastFlushEvict > 1000l) { + lastFlushEvict = millis; + if(!evictedCapes.isEmpty()) { + Iterator evictItr = evictedCapes.values().iterator(); + while(evictItr.hasNext()) { + if(millis - evictItr.next().longValue() > 3000l) { + evictItr.remove(); + } + } + } + } + if(millis - lastFlush > 60000l) { + lastFlush = millis; + if(!capesCache.isEmpty()) { + Iterator entryItr = capesCache.values().iterator(); + while(entryItr.hasNext()) { + CapeCacheEntry etr = entryItr.next(); + if(millis - etr.lastCacheHit > 900000l) { // 15 minutes + entryItr.remove(); + etr.free(); + } + } + } + } + if(needReloadClientCape) { + reloadClientPlayerCape(); + } + } + + public void destroy() { + Iterator entryItr = capesCache.values().iterator(); + while(entryItr.hasNext()) { + entryItr.next().free(); + } + capesCache.clear(); + waitingCapes.clear(); + evictedCapes.clear(); + } + + public void evictCape(EaglercraftUUID uuid) { + evictedCapes.put(uuid, Long.valueOf(EagRuntime.steadyTimeMillis())); + CapeCacheEntry etr = capesCache.remove(uuid); + if(etr != null) { + etr.free(); + } + } + + public void handleInvalidate(EaglercraftUUID uuid) { + CapeCacheEntry etr = capesCache.remove(uuid); + if(etr != null) { + etr.free(); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java index 75faf01..f11cf4b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java @@ -1,40 +1,32 @@ package net.lax1dude.eaglercraft.v1_8.profile; -import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; import net.lax1dude.eaglercraft.v1_8.mojang.authlib.TexturesProperty; -import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetOtherSkinEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetSkinByURLEAG; +import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.play.client.C17PacketCustomPayload; import net.minecraft.util.ResourceLocation; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -44,21 +36,20 @@ public class ServerSkinCache { private static final Logger logger = LogManager.getLogger("ServerSkinCache"); public class SkinCacheEntry { - + protected final boolean isPresetSkin; protected final int presetSkinId; protected final CacheCustomSkin customSkin; - - protected long lastCacheHit = System.currentTimeMillis(); - - protected SkinCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation, - SkinModel model) { + + protected long lastCacheHit = EagRuntime.steadyTimeMillis(); + + protected SkinCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation, SkinModel model) { this.isPresetSkin = false; this.presetSkinId = -1; this.customSkin = new CacheCustomSkin(textureInstance, resourceLocation, model); ServerSkinCache.this.textureManager.loadTexture(resourceLocation, textureInstance); } - + /** * Use only for the constant for the client player */ @@ -67,39 +58,39 @@ public class ServerSkinCache { this.presetSkinId = -1; this.customSkin = new CacheCustomSkin(null, resourceLocation, model); } - + protected SkinCacheEntry(int presetSkinId) { this.isPresetSkin = true; this.presetSkinId = presetSkinId; this.customSkin = null; } - + public ResourceLocation getResourceLocation() { - if (isPresetSkin) { + if(isPresetSkin) { return DefaultSkins.getSkinFromId(presetSkinId).location; - } else { - if (customSkin != null) { + }else { + if(customSkin != null) { return customSkin.resourceLocation; - } else { + }else { return DefaultSkins.DEFAULT_STEVE.location; } } } - + public SkinModel getSkinModel() { - if (isPresetSkin) { + if(isPresetSkin) { return DefaultSkins.getSkinFromId(presetSkinId).model; - } else { - if (customSkin != null) { + }else { + if(customSkin != null) { return customSkin.model; - } else { + }else { return DefaultSkins.DEFAULT_STEVE.model; } } } - + protected void free() { - if (!isPresetSkin) { + if(!isPresetSkin) { ServerSkinCache.this.textureManager.deleteTexture(customSkin.resourceLocation); } } @@ -107,13 +98,12 @@ public class ServerSkinCache { } protected static class CacheCustomSkin { - + protected final EaglerSkinTexture textureInstance; protected final ResourceLocation resourceLocation; protected final SkinModel model; - - protected CacheCustomSkin(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation, - SkinModel model) { + + protected CacheCustomSkin(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation, SkinModel model) { this.textureInstance = textureInstance; this.resourceLocation = resourceLocation; this.model = model; @@ -122,10 +112,10 @@ public class ServerSkinCache { } protected static class WaitingSkin { - + protected final long timeout; protected final SkinModel model; - + protected WaitingSkin(long timeout, SkinModel model) { this.timeout = timeout; this.model = model; @@ -135,28 +125,33 @@ public class ServerSkinCache { private final SkinCacheEntry defaultCacheEntry = new SkinCacheEntry(0); private final SkinCacheEntry defaultSlimCacheEntry = new SkinCacheEntry(1); - private final Map skinsCache = new HashMap(); - private final Map waitingSkins = new HashMap(); - private final Map evictedSkins = new HashMap(); + private final Map skinsCache = new HashMap<>(); + private final Map waitingSkins = new HashMap<>(); + private final Map evictedSkins = new HashMap<>(); - private final EaglercraftNetworkManager networkManager; + private final NetHandlerPlayClient netHandler; protected final TextureManager textureManager; - + private final EaglercraftUUID clientPlayerId; - private final SkinCacheEntry clientPlayerCacheEntry; + private SkinCacheEntry clientPlayerCacheEntry; - private long lastFlush = System.currentTimeMillis(); - private long lastFlushReq = System.currentTimeMillis(); - private long lastFlushEvict = System.currentTimeMillis(); + private long lastFlush = EagRuntime.steadyTimeMillis(); + private long lastFlushReq = EagRuntime.steadyTimeMillis(); + private long lastFlushEvict = EagRuntime.steadyTimeMillis(); private static int texId = 0; + public static boolean needReloadClientSkin = false; - public ServerSkinCache(EaglercraftNetworkManager networkManager, TextureManager textureManager) { - this.networkManager = networkManager; + public ServerSkinCache(NetHandlerPlayClient netHandler, TextureManager textureManager) { + this.netHandler = netHandler; this.textureManager = textureManager; this.clientPlayerId = EaglerProfile.getPlayerUUID(); - this.clientPlayerCacheEntry = new SkinCacheEntry(EaglerProfile.getActiveSkinResourceLocation(), - EaglerProfile.getActiveSkinModel()); + reloadClientPlayerSkin(); + } + + public void reloadClientPlayerSkin() { + needReloadClientSkin = false; + this.clientPlayerCacheEntry = new SkinCacheEntry(EaglerProfile.getActiveSkinResourceLocation(), EaglerProfile.getActiveSkinModel()); } public SkinCacheEntry getClientPlayerSkin() { @@ -165,27 +160,27 @@ public class ServerSkinCache { public SkinCacheEntry getSkin(GameProfile player) { EaglercraftUUID uuid = player.getId(); - if (uuid != null && uuid.equals(clientPlayerId)) { + if(uuid != null && uuid.equals(clientPlayerId)) { return clientPlayerCacheEntry; } TexturesProperty props = player.getTextures(); - if (props.eaglerPlayer || props.skin == null) { - if (uuid != null) { + if(props.eaglerPlayer || props.skin == null) { + if(uuid != null) { return _getSkin(uuid); - } else { - if ("slim".equalsIgnoreCase(props.model)) { + }else { + if("slim".equalsIgnoreCase(props.model)) { return defaultSlimCacheEntry; - } else { + }else { return defaultCacheEntry; } } - } else { + }else { return getSkin(props.skin, SkinModel.getModelFromId(props.model)); } } public SkinCacheEntry getSkin(EaglercraftUUID player) { - if (player.equals(clientPlayerId)) { + if(player.equals(clientPlayerId)) { return clientPlayerCacheEntry; } return _getSkin(player); @@ -193,153 +188,146 @@ public class ServerSkinCache { private SkinCacheEntry _getSkin(EaglercraftUUID player) { SkinCacheEntry etr = skinsCache.get(player); - if (etr == null) { - if (!waitingSkins.containsKey(player) && !evictedSkins.containsKey(player)) { - waitingSkins.put(player, new WaitingSkin(System.currentTimeMillis(), null)); - PacketBuffer buffer; - try { - buffer = SkinPackets.writeGetOtherSkin(player); - } catch (IOException ex) { - logger.error("Could not write skin request packet!"); - logger.error(ex); - return defaultCacheEntry; - } - networkManager.sendPacket(new C17PacketCustomPayload("EAG|Skins-1.8", buffer)); + if(etr == null) { + if(!waitingSkins.containsKey(player) && !evictedSkins.containsKey(player)) { + waitingSkins.put(player, new WaitingSkin(EagRuntime.steadyTimeMillis(), null)); + netHandler.sendEaglerMessage(new CPacketGetOtherSkinEAG(player.msb, player.lsb)); } return defaultCacheEntry; - } else { - etr.lastCacheHit = System.currentTimeMillis(); + }else { + etr.lastCacheHit = EagRuntime.steadyTimeMillis(); return etr; } } public SkinCacheEntry getSkin(String url, SkinModel skinModelResponse) { - if (url.length() > 0xFFFF) { + if(url.length() > 0xFFFF) { return skinModelResponse == SkinModel.ALEX ? defaultSlimCacheEntry : defaultCacheEntry; } EaglercraftUUID generatedUUID = SkinPackets.createEaglerURLSkinUUID(url); SkinCacheEntry etr = skinsCache.get(generatedUUID); - if (etr != null) { - etr.lastCacheHit = System.currentTimeMillis(); + if(etr != null) { + etr.lastCacheHit = EagRuntime.steadyTimeMillis(); return etr; - } else { - if (!waitingSkins.containsKey(generatedUUID) && !evictedSkins.containsKey(generatedUUID)) { - waitingSkins.put(generatedUUID, new WaitingSkin(System.currentTimeMillis(), skinModelResponse)); - PacketBuffer buffer; - try { - buffer = SkinPackets.writeGetSkinByURL(generatedUUID, url); - } catch (IOException ex) { - logger.error("Could not write skin request packet!"); - logger.error(ex); - return skinModelResponse == SkinModel.ALEX ? defaultSlimCacheEntry : defaultCacheEntry; - } - networkManager.sendPacket(new C17PacketCustomPayload("EAG|Skins-1.8", buffer)); + }else { + if(!waitingSkins.containsKey(generatedUUID) && !evictedSkins.containsKey(generatedUUID)) { + waitingSkins.put(generatedUUID, new WaitingSkin(EagRuntime.steadyTimeMillis(), skinModelResponse)); + netHandler.sendEaglerMessage(new CPacketGetSkinByURLEAG(generatedUUID.msb, generatedUUID.lsb, url)); } } return skinModelResponse == SkinModel.ALEX ? defaultSlimCacheEntry : defaultCacheEntry; } public void cacheSkinPreset(EaglercraftUUID player, int presetId) { - if (waitingSkins.remove(player) != null) { + if(waitingSkins.remove(player) != null) { SkinCacheEntry etr = skinsCache.remove(player); - if (etr != null) { + if(etr != null) { etr.free(); } skinsCache.put(player, new SkinCacheEntry(presetId)); - } else { + }else { logger.error("Unsolicited skin response recieved for \"{}\"! (preset {})", player, presetId); } } - + public void cacheSkinCustom(EaglercraftUUID player, byte[] pixels, SkinModel model) { WaitingSkin waitingSkin; - if ((waitingSkin = waitingSkins.remove(player)) != null) { + if((waitingSkin = waitingSkins.remove(player)) != null) { SkinCacheEntry etr = skinsCache.remove(player); - if (etr != null) { + if(etr != null) { etr.free(); } - if (waitingSkin.model != null) { + if(waitingSkin.model != null) { model = waitingSkin.model; - } else if (model == null) { + }else if(model == null) { model = (player.hashCode() & 1) != 0 ? SkinModel.ALEX : SkinModel.STEVE; } try { etr = new SkinCacheEntry(new EaglerSkinTexture(pixels, model.width, model.height), new ResourceLocation("eagler:skins/multiplayer/tex_" + texId++), model); - } catch (Throwable t) { + }catch(Throwable t) { etr = new SkinCacheEntry(0); logger.error("Could not process custom skin packet for \"{}\"!", player); logger.error(t); } skinsCache.put(player, etr); - } else { - logger.error("Unsolicited skin response recieved for \"{}\"! (custom {}x{})", player, model.width, - model.height); + }else { + logger.error("Unsolicited skin response recieved for \"{}\"! (custom {}x{})", player, model.width, model.height); } } - + public SkinModel getRequestedSkinType(EaglercraftUUID waiting) { WaitingSkin waitingSkin; - if ((waitingSkin = waitingSkins.get(waiting)) != null) { + if((waitingSkin = waitingSkins.get(waiting)) != null) { return waitingSkin.model; - } else { + }else { return null; } } - + public void flush() { - long millis = System.currentTimeMillis(); - if (millis - lastFlushReq > 5000l) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastFlushReq > 5000l) { lastFlushReq = millis; - if (!waitingSkins.isEmpty()) { + if(!waitingSkins.isEmpty()) { Iterator waitingItr = waitingSkins.values().iterator(); - while (waitingItr.hasNext()) { - if (millis - waitingItr.next().timeout > 30000l) { + while(waitingItr.hasNext()) { + if(millis - waitingItr.next().timeout > 20000l) { waitingItr.remove(); } } } } - if (millis - lastFlushEvict > 1000l) { + if(millis - lastFlushEvict > 1000l) { lastFlushEvict = millis; - if (!evictedSkins.isEmpty()) { + if(!evictedSkins.isEmpty()) { Iterator evictItr = evictedSkins.values().iterator(); - while (evictItr.hasNext()) { - if (millis - evictItr.next().longValue() > 3000l) { + while(evictItr.hasNext()) { + if(millis - evictItr.next().longValue() > 3000l) { evictItr.remove(); } } } } - if (millis - lastFlush > 60000l) { + if(millis - lastFlush > 60000l) { lastFlush = millis; - if (!skinsCache.isEmpty()) { + if(!skinsCache.isEmpty()) { Iterator entryItr = skinsCache.values().iterator(); - while (entryItr.hasNext()) { + while(entryItr.hasNext()) { SkinCacheEntry etr = entryItr.next(); - if (millis - etr.lastCacheHit > 900000l) { // 15 minutes + if(millis - etr.lastCacheHit > 900000l) { // 15 minutes entryItr.remove(); etr.free(); } } } } + if(needReloadClientSkin) { + reloadClientPlayerSkin(); + } } - + public void destroy() { Iterator entryItr = skinsCache.values().iterator(); - while (entryItr.hasNext()) { + while(entryItr.hasNext()) { entryItr.next().free(); } skinsCache.clear(); waitingSkins.clear(); evictedSkins.clear(); } - + public void evictSkin(EaglercraftUUID uuid) { - evictedSkins.put(uuid, Long.valueOf(System.currentTimeMillis())); + evictedSkins.put(uuid, Long.valueOf(EagRuntime.steadyTimeMillis())); SkinCacheEntry etr = skinsCache.remove(uuid); - if (etr != null) { + if(etr != null) { + etr.free(); + } + } + + public void handleInvalidate(EaglercraftUUID uuid) { + SkinCacheEntry etr = skinsCache.remove(uuid); + if(etr != null) { etr.free(); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java index e2181f9..541c51e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java @@ -4,24 +4,16 @@ import java.util.HashMap; import java.util.Map; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -31,17 +23,17 @@ public enum SkinModel { LONG_ARMS(3, HighPolySkin.LONG_ARMS), WEIRD_CLIMBER_DUDE(4, HighPolySkin.WEIRD_CLIMBER_DUDE), LAXATIVE_DUDE(5, HighPolySkin.LAXATIVE_DUDE), BABY_CHARLES(6, HighPolySkin.BABY_CHARLES), BABY_WINSTON(7, HighPolySkin.BABY_WINSTON); - - public final int id; + + public final int id; public final int width; public final int height; public final String profileSkinType; public final boolean sanitize; public final HighPolySkin highPoly; - + public static final SkinModel[] skinModels = new SkinModel[8]; - private static final Map skinModelsByName = new HashMap(); - + private static final Map skinModelsByName = new HashMap<>(); + private SkinModel(int id, int w, int h, String profileSkinType, boolean sanitize) { this.id = id; this.width = w; @@ -50,7 +42,7 @@ public enum SkinModel { this.sanitize = sanitize; this.highPoly = null; } - + private SkinModel(int id, HighPolySkin highPoly) { this.id = id; this.width = 256; @@ -62,7 +54,7 @@ public enum SkinModel { public static SkinModel getModelFromId(String str) { SkinModel mdl = skinModelsByName.get(str.toLowerCase()); - if (mdl == null) { + if(mdl == null) { return skinModels[0]; } return mdl; @@ -70,19 +62,19 @@ public enum SkinModel { public static SkinModel getModelFromId(int id) { SkinModel s = null; - if (id >= 0 && id < skinModels.length) { + if(id >= 0 && id < skinModels.length) { s = skinModels[id]; } - if (s != null) { + if(s != null) { return s; - } else { + }else { return STEVE; } } - + static { SkinModel[] arr = values(); - for (int i = 0; i < arr.length; ++i) { + for(int i = 0; i < arr.length; ++i) { skinModels[arr[i].id] = arr[i]; skinModelsByName.put(arr[i].profileSkinType, arr[i]); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java index 3abb60b..b91d701 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java @@ -1,32 +1,20 @@ package net.lax1dude.eaglercraft.v1_8.profile; -import java.io.IOException; - import net.lax1dude.eaglercraft.v1_8.ArrayUtils; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.crypto.MD5Digest; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.minecraft.network.PacketBuffer; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -35,102 +23,36 @@ public class SkinPackets { public static final int PACKET_MY_SKIN_PRESET = 0x01; public static final int PACKET_MY_SKIN_CUSTOM = 0x02; - public static final int PACKET_GET_OTHER_SKIN = 0x03; - public static final int PACKET_OTHER_SKIN_PRESET = 0x04; - public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05; - public static final int PACKET_GET_SKIN_BY_URL = 0x06; - public static final int PACKET_INSTALL_NEW_SKIN = 0x07; - - public static void readPluginMessage(PacketBuffer buffer, ServerSkinCache skinCache) throws IOException { - try { - int type = (int) buffer.readByte() & 0xFF; - switch (type) { - case PACKET_OTHER_SKIN_PRESET: { - EaglercraftUUID responseUUID = buffer.readUuid(); - int responsePreset = buffer.readInt(); - if (buffer.isReadable()) { - throw new IOException( - "PACKET_OTHER_SKIN_PRESET had " + buffer.readableBytes() + " remaining bytes!"); - } - skinCache.cacheSkinPreset(responseUUID, responsePreset); - break; - } - case PACKET_OTHER_SKIN_CUSTOM: { - EaglercraftUUID responseUUID = buffer.readUuid(); - int model = (int) buffer.readByte() & 0xFF; - SkinModel modelId; - if (model == (byte) 0xFF) { - modelId = skinCache.getRequestedSkinType(responseUUID); - } else { - modelId = SkinModel.getModelFromId(model & 0x7F); - if ((model & 0x80) != 0 && modelId.sanitize) { - modelId = SkinModel.STEVE; - } - } - if (modelId.highPoly != null) { - modelId = SkinModel.STEVE; - } - int bytesToRead = modelId.width * modelId.height * 4; - byte[] readSkin = new byte[bytesToRead]; - buffer.readBytes(readSkin); - if (buffer.isReadable()) { - throw new IOException( - "PACKET_MY_SKIN_CUSTOM had " + buffer.readableBytes() + " remaining bytes!"); - } - skinCache.cacheSkinCustom(responseUUID, readSkin, modelId); - break; - } - default: - throw new IOException("Unknown skin packet type: " + type); - } - } catch (IOException ex) { - throw ex; - } catch (Throwable t) { - throw new IOException("Failed to parse skin packet!", t); - } - } public static byte[] writeMySkinPreset(int skinId) { return new byte[] { (byte) PACKET_MY_SKIN_PRESET, (byte) (skinId >>> 24), (byte) (skinId >>> 16), (byte) (skinId >>> 8), (byte) (skinId & 0xFF) }; } - public static byte[] writeMySkinCustom(CustomSkin customSkin) { - byte[] packet = new byte[2 + customSkin.texture.length]; + public static byte[] writeMySkinCustomV3(CustomSkin customSkin) { + byte[] packet = new byte[2 + 16384]; packet[0] = (byte) PACKET_MY_SKIN_CUSTOM; packet[1] = (byte) customSkin.model.id; - System.arraycopy(customSkin.texture, 0, packet, 2, customSkin.texture.length); + System.arraycopy(customSkin.texture, 0, packet, 2, 16384); return packet; } - public static PacketBuffer writeCreateCustomSkull(byte[] customSkin) { - int len = 3 + customSkin.length; - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(len, len)); - ret.writeByte(PACKET_INSTALL_NEW_SKIN); - ret.writeShort(customSkin.length); - ret.writeBytes(customSkin); - return ret; + public static byte[] writeMySkinCustomV4(CustomSkin customSkin) { + byte[] packet = new byte[2 + 12288]; + packet[0] = (byte) PACKET_MY_SKIN_CUSTOM; + packet[1] = (byte) customSkin.model.id; + byte[] v3data = customSkin.texture; + for(int i = 0, j, k; i < 4096; ++i) { + j = i << 2; + k = i * 3 + 2; + packet[k] = v3data[j + 1]; + packet[k + 1] = v3data[j + 2]; + packet[k + 2] = (byte)((v3data[j + 3] >>> 1) | (v3data[j] & 0x80)); + } + return packet; } - public static PacketBuffer writeGetOtherSkin(EaglercraftUUID skinId) throws IOException { - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); - ret.writeByte(PACKET_GET_OTHER_SKIN); - ret.writeUuid(skinId); - return ret; - } - - public static PacketBuffer writeGetSkinByURL(EaglercraftUUID skinId, String skinUrl) throws IOException { - int len = 19 + skinUrl.length(); - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(len, len)); - ret.writeByte(PACKET_GET_SKIN_BY_URL); - ret.writeUuid(skinId); - byte[] url = ArrayUtils.asciiString(skinUrl); - ret.writeShort((int) url.length); - ret.writeBytes(url); - return ret; - } - - public static EaglercraftUUID createEaglerURLSkinUUID(String skinUrl) { + public static EaglercraftUUID createEaglerURLSkinUUID(String skinUrl){ MD5Digest dg = new MD5Digest(); byte[] bytes = ArrayUtils.asciiString("EaglercraftSkinURL:" + skinUrl); dg.update(bytes, 0, bytes.length); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java index d366279..999c396 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java @@ -1,5 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.profile; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; @@ -11,24 +12,16 @@ import net.minecraft.client.renderer.RenderHelper; import net.minecraft.util.ResourceLocation; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -38,7 +31,7 @@ public class SkinPreviewRenderer { private static ModelPlayer playerModelSteve = null; private static ModelPlayer playerModelAlex = null; private static ModelZombie playerModelZombie = null; - + public static void initialize() { playerModelSteve = new ModelPlayer(0.0f, false); playerModelSteve.isChild = false; @@ -52,74 +45,72 @@ public class SkinPreviewRenderer { renderPreview(x, y, mx, my, false, skinModel, null, null); } - public static void renderPreview(int x, int y, int mx, int my, boolean capeMode, SkinModel skinModel, - ResourceLocation skinTexture, ResourceLocation capeTexture) { + public static void renderPreview(int x, int y, int mx, int my, boolean capeMode, SkinModel skinModel, ResourceLocation skinTexture, ResourceLocation capeTexture) { ModelBiped model; - switch (skinModel) { - case STEVE: - default: - model = playerModelSteve; - break; - case ALEX: - model = playerModelAlex; - break; - case ZOMBIE: - model = playerModelZombie; - break; - case LONG_ARMS: - case WEIRD_CLIMBER_DUDE: - case LAXATIVE_DUDE: - case BABY_CHARLES: - case BABY_WINSTON: - if (skinModel.highPoly != null && Minecraft.getMinecraft().gameSettings.enableFNAWSkins) { - renderHighPoly(x, y, mx, my, skinModel.highPoly); - return; - } - model = playerModelSteve; - break; + switch(skinModel) { + case STEVE: + default: + model = playerModelSteve; + break; + case ALEX: + model = playerModelAlex; + break; + case ZOMBIE: + model = playerModelZombie; + break; + case LONG_ARMS: + case WEIRD_CLIMBER_DUDE: + case LAXATIVE_DUDE: + case BABY_CHARLES: + case BABY_WINSTON: + if(skinModel.highPoly != null && Minecraft.getMinecraft().gameSettings.enableFNAWSkins) { + renderHighPoly(x, y, mx, my, skinModel.highPoly); + return; + } + model = playerModelSteve; + break; } - + GlStateManager.enableTexture2D(); GlStateManager.disableBlend(); GlStateManager.disableCull(); GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - + GlStateManager.pushMatrix(); GlStateManager.translate(x, y - 80.0f, 100.0f); GlStateManager.scale(50.0f, 50.0f, 50.0f); GlStateManager.rotate(180.0f, 1.0f, 0.0f, 0.0f); GlStateManager.scale(1.0f, -1.0f, 1.0f); - + RenderHelper.enableGUIStandardItemLighting(); - + GlStateManager.translate(0.0f, 1.0f, 0.0f); - if (capeMode) { + if(capeMode) { GlStateManager.rotate(140.0f, 0.0f, 1.0f, 0.0f); mx = x - (x - mx) - 20; GlStateManager.rotate(((y - my) * -0.02f), 1.0f, 0.0f, 0.0f); - } else { + }else { GlStateManager.rotate(((y - my) * -0.06f), 1.0f, 0.0f, 0.0f); } GlStateManager.rotate(((x - mx) * 0.06f), 0.0f, 1.0f, 0.0f); GlStateManager.translate(0.0f, -1.0f, 0.0f); - - if (skinTexture != null) { + + if(skinTexture != null) { Minecraft.getMinecraft().getTextureManager().bindTexture(skinTexture); } - - model.render(null, 0.0f, 0.0f, (float) (System.currentTimeMillis() % 2000000) / 50f, ((x - mx) * 0.06f), - ((y - my) * -0.1f), 0.0625f); - - if (capeTexture != null && model instanceof ModelPlayer) { + + model.render(null, 0.0f, 0.0f, (float)(EagRuntime.steadyTimeMillis() % 2000000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625f); + + if(capeTexture != null && model instanceof ModelPlayer) { Minecraft.getMinecraft().getTextureManager().bindTexture(capeTexture); GlStateManager.pushMatrix(); GlStateManager.translate(0.0F, 0.0F, 0.125F); GlStateManager.rotate(6.0F, 1.0F, 0.0F, 0.0F); GlStateManager.rotate(180.0F, 0.0F, 1.0F, 0.0F); - ((ModelPlayer) model).renderCape(0.0625f); + ((ModelPlayer)model).renderCape(0.0625f); GlStateManager.popMatrix(); } - + GlStateManager.popMatrix(); GlStateManager.disableLighting(); } @@ -129,55 +120,55 @@ public class SkinPreviewRenderer { GlStateManager.disableBlend(); GlStateManager.disableCull(); GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - + GlStateManager.pushMatrix(); GlStateManager.translate(x, y - 80.0f, 100.0f); GlStateManager.scale(50.0f, 50.0f, 50.0f); GlStateManager.rotate(180.0f, 1.0f, 0.0f, 0.0f); GlStateManager.scale(1.0f, -1.0f, 1.0f); - + RenderHelper.enableGUIStandardItemLighting(); - + GlStateManager.translate(0.0f, 1.0f, 0.0f); GlStateManager.rotate(((y - my) * -0.06f), 1.0f, 0.0f, 0.0f); GlStateManager.rotate(((x - mx) * 0.06f), 0.0f, 1.0f, 0.0f); GlStateManager.rotate(180.0f, 0.0f, 0.0f, 1.0f); GlStateManager.translate(0.0f, -0.6f, 0.0f); - + GlStateManager.scale(HighPolySkin.highPolyScale, HighPolySkin.highPolyScale, HighPolySkin.highPolyScale); Minecraft.getMinecraft().getTextureManager().bindTexture(msh.texture); - - if (msh.bodyModel != null) { + + if(msh.bodyModel != null) { EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(msh.bodyModel)); } - - if (msh.headModel != null) { + + if(msh.headModel != null) { EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(msh.headModel)); } - - if (msh.limbsModel != null && msh.limbsModel.length > 0) { - for (int i = 0; i < msh.limbsModel.length; ++i) { + + if(msh.limbsModel != null && msh.limbsModel.length > 0) { + for(int i = 0; i < msh.limbsModel.length; ++i) { float offset = 0.0f; - if (msh.limbsOffset != null) { - if (msh.limbsOffset.length == 1) { + if(msh.limbsOffset != null) { + if(msh.limbsOffset.length == 1) { offset = msh.limbsOffset[0]; - } else { + }else { offset = msh.limbsOffset[i]; } } - if (offset != 0.0f || msh.limbsInitialRotation != 0.0f) { + if(offset != 0.0f || msh.limbsInitialRotation != 0.0f) { GlStateManager.pushMatrix(); - if (offset != 0.0f) { + if(offset != 0.0f) { GlStateManager.translate(0.0f, offset, 0.0f); } - if (msh.limbsInitialRotation != 0.0f) { + if(msh.limbsInitialRotation != 0.0f) { GlStateManager.rotate(msh.limbsInitialRotation, 1.0f, 0.0f, 0.0f); } } - + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(msh.limbsModel[i])); - - if (offset != 0.0f || msh.limbsInitialRotation != 0.0f) { + + if(offset != 0.0f || msh.limbsInitialRotation != 0.0f) { GlStateManager.popMatrix(); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/EnumScreenRecordingCodec.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/EnumScreenRecordingCodec.java new file mode 100755 index 0000000..bcb9e3e --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/EnumScreenRecordingCodec.java @@ -0,0 +1,152 @@ +package net.lax1dude.eaglercraft.v1_8.recording; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumScreenRecordingCodec { + + CODEC_MP4_H264_GENERIC_AAC("MP4 (video: H.264 Default, audio: AAC LC)", "mp4", "video/mp4", "avc1", "mp4a.40.2", false), + CODEC_MP4_H264_GENERIC_OPUS("MP4 (video: H.264 Default, audio: Opus)", "mp4", "video/mp4", "avc1", "opus", false), + CODEC_MP4_H264_L40_AAC("MP4 (video: H.264 CBP L4.0, audio: AAC LC)", "mp4", "video/mp4", "avc1.424028", "mp4a.40.2", true), + CODEC_MP4_H264_L40_OPUS("MP4 (video: H.264 CBP L4.0, audio: Opus)", "mp4", "video/mp4", "avc1.424028", "opus", true), + CODEC_MP4_H264_L42_AAC("MP4 (video: H.264 CBP L4.2, audio: AAC LC)", "mp4", "video/mp4", "avc1.42402A", "mp4a.40.2", true), + CODEC_MP4_H264_L42_OPUS("MP4 (video: H.264 CBP L4.2, audio: Opus)", "mp4", "video/mp4", "avc1.42402A", "opus", true), + CODEC_MP4_H264_L50_AAC("MP4 (video: H.264 CBP L5.0, audio: AAC LC)", "mp4", "video/mp4", "avc1.424032", "mp4a.40.2", true), + CODEC_MP4_H264_L50_OPUS("MP4 (video: H.264 CBP L5.0, audio: Opus)", "mp4", "video/mp4", "avc1.424032", "opus", true), + CODEC_MP4_H264_L52_AAC("MP4 (video: H.264 CBP L5.2, audio: AAC LC)", "mp4", "video/mp4", "avc1.424034", "mp4a.40.2", true), + CODEC_MP4_H264_L52_OPUS("MP4 (video: H.264 CBP L5.2, audio: Opus)", "mp4", "video/mp4", "avc1.424034", "opus", true), + + CODEC_MP4_VP9_GENERIC_AAC("MP4 (video: VP9 Default, audio: AAC LC)", "mp4", "video/mp4", "vp9", "mp4a.40.2", false), + CODEC_MP4_VP9_GENERIC_OPUS("MP4 (video: VP9 Default, audio: Opus)", "mp4", "video/mp4", "vp9", "opus", false), + CODEC_MP4_VP9_L40_AAC("MP4 (video: VP9 8-bit L4.0, audio: AAC LC)", "mp4", "video/mp4", "vp9.00.40.08", "mp4a.40.2", true), + CODEC_MP4_VP9_L40_OPUS("MP4 (video: VP9 8-bit L4.0, audio: Opus)", "mp4", "video/mp4", "vp9.00.40.08", "opus", true), + CODEC_MP4_VP9_L41_AAC("MP4 (video: VP9 8-bit L4.1, audio: AAC LC)", "mp4", "video/mp4", "vp9.00.41.08", "mp4a.40.2", true), + CODEC_MP4_VP9_L41_OPUS("MP4 (video: VP9 8-bit L4.1, audio: Opus)", "mp4", "video/mp4", "vp9.00.41.08", "opus", true), + CODEC_MP4_VP9_L50_AAC("MP4 (video: VP9 8-bit L5.0, audio: AAC LC)", "mp4", "video/mp4", "vp9.00.50.08", "mp4a.40.2", true), + CODEC_MP4_VP9_L50_OPUS("MP4 (video: VP9 8-bit L5.0, audio: Opus)", "mp4", "video/mp4", "vp9.00.50.08", "opus", true), + CODEC_MP4_VP9_L51_AAC("MP4 (video: VP9 8-bit L5.1, audio: AAC LC)", "mp4", "video/mp4", "vp9.00.51.08", "mp4a.40.2", true), + CODEC_MP4_VP9_L51_OPUS("MP4 (video: VP9 8-bit L5.1, audio: Opus)", "mp4", "video/mp4", "vp9.00.51.08", "opus", true), + + CODEC_MP4_GENERIC("MP4 (Default Codecs)", "mp4", "video/mp4", null, null, false), + + CODEC_WEBM_H264_GENERIC_OPUS("WEBM (video: H.264 Default, audio: Opus)", "webm", "video/webm", "avc1", "opus", false), + CODEC_WEBM_H264_GENERIC_VORBIS("WEBM (video: H.264 Default, audio: Vorbis)", "webm", "video/webm", "avc1", "vorbis", false), + CODEC_WEBM_H264_L40_OPUS("WEBM (video: H.264 CBP L4.0, audio: Opus)", "webm", "video/webm", "avc1.424028", "opus", true), + CODEC_WEBM_H264_L40_VORBIS("WEBM (video: H.264 CBP L4.0, audio: Vorbis)", "webm", "video/webm", "avc1.424028", "vorbis", true), + CODEC_WEBM_H264_L42_OPUS("WEBM (video: H.264 CBP L4.2, audio: Opus)", "webm", "video/webm", "avc1.42402A", "opus", true), + CODEC_WEBM_H264_L42_VORBIS("WEBM (video: H.264 CBP L4.2, audio: Vorbis)", "webm", "video/webm", "avc1.42402A", "vorbis", true), + CODEC_WEBM_H264_L50_OPUS("WEBM (video: H.264 CBP L5.0, audio: Opus)", "webm", "video/webm", "avc1.424032", "opus", true), + CODEC_WEBM_H264_L50_VORBIS("WEBM (video: H.264 CBP L5.0, audio: Vorbis)", "webm", "video/webm", "avc1.424032", "vorbis", true), + CODEC_WEBM_H264_L52_OPUS("WEBM (video: H.264 CBP L5.2, audio: Opus)", "webm", "video/webm", "avc1.424034", "opus", true), + CODEC_WEBM_H264_L52_VORBIS("WEBM (video: H.264 CBP L5.2, audio: Vorbis)", "webm", "video/webm", "avc1.424034", "vorbis", true), + + CODEC_WEBM_VP9_GENERIC_OPUS("WEBM (video: VP9 Default, audio: Opus)", "webm", "video/webm", "vp9", "opus", false), + CODEC_WEBM_VP9_GENERIC_VORBIS("WEBM (video: VP9 Default, audio: Vorbis)", "webm", "video/webm", "vp9", "vorbis", false), + CODEC_WEBM_VP9_L40_OPUS("WEBM (video: VP9 8-bit L4.0, audio: Opus)", "webm", "video/webm", "vp9.00.40.08", "opus", true), + CODEC_WEBM_VP9_L40_VORBIS("WEBM (video: VP9 8-bit L4.0, audio: Vorbis)", "webm", "video/webm", "vp9.00.40.08", "vorbis", true), + CODEC_WEBM_VP9_L41_OPUS("WEBM (video: VP9 8-bit L4.1, audio: Opus)", "webm", "video/webm", "vp9.00.41.08", "opus", true), + CODEC_WEBM_VP9_L41_VORBIS("WEBM (video: VP9 8-bit L4.1, audio: Vorbis)", "webm", "video/webm", "vp9.00.41.08", "vorbis", true), + CODEC_WEBM_VP9_L50_OPUS("WEBM (video: VP9 8-bit L5.0, audio: Opus)", "webm", "video/webm", "vp9.00.50.08", "opus", true), + CODEC_WEBM_VP9_L50_VORBIS("WEBM (video: VP9 8-bit L5.0, audio: Vorbis)", "webm", "video/webm", "vp9.00.50.08", "vorbis", true), + CODEC_WEBM_VP9_L51_OPUS("WEBM (video: VP9 8-bit L5.1, audio: Opus)", "webm", "video/webm", "vp9.00.51.08", "opus", true), + CODEC_WEBM_VP9_L51_VORBIS("WEBM (video: VP9 8-bit L5.1, audio: Vorbis)", "webm", "video/webm", "vp9.00.51.08", "vorbis", true), + + CODEC_WEBM_VP8_GENERIC_OPUS("WEBM (video: VP8 Default, audio: Opus)", "webm", "video/webm", "vp8", "opus", false), + CODEC_WEBM_VP8_GENERIC_VORBIS("WEBM (video: VP8 Default, audio: Vorbis)", "webm", "video/webm", "vp8", "vorbis", false), + + CODEC_WEBM_GENERIC("WEBM (Default Codecs)", "webm", "video/webm", null, null, false); + + public static final EnumScreenRecordingCodec[] preferred_codec_order = new EnumScreenRecordingCodec[] { + CODEC_MP4_H264_GENERIC_AAC, + CODEC_MP4_H264_L52_AAC, + CODEC_MP4_H264_L50_AAC, + CODEC_MP4_H264_L42_AAC, + CODEC_MP4_H264_L40_AAC, + CODEC_MP4_H264_GENERIC_OPUS, + CODEC_MP4_H264_L40_OPUS, + CODEC_MP4_H264_L42_OPUS, + CODEC_MP4_H264_L50_OPUS, + CODEC_MP4_H264_L52_OPUS, + CODEC_WEBM_H264_GENERIC_OPUS, + CODEC_WEBM_H264_L52_OPUS, + CODEC_WEBM_H264_L50_OPUS, + CODEC_WEBM_H264_L42_OPUS, + CODEC_WEBM_H264_L40_OPUS, + CODEC_WEBM_H264_GENERIC_VORBIS, + CODEC_WEBM_H264_L52_VORBIS, + CODEC_WEBM_H264_L50_VORBIS, + CODEC_WEBM_H264_L42_VORBIS, + CODEC_WEBM_H264_L40_VORBIS, + CODEC_MP4_VP9_GENERIC_AAC, + CODEC_MP4_VP9_L51_AAC, + CODEC_MP4_VP9_L50_AAC, + CODEC_MP4_VP9_L41_AAC, + CODEC_MP4_VP9_L40_AAC, + CODEC_MP4_VP9_GENERIC_OPUS, + CODEC_MP4_VP9_L51_OPUS, + CODEC_MP4_VP9_L50_OPUS, + CODEC_MP4_VP9_L41_OPUS, + CODEC_MP4_VP9_L40_OPUS, + CODEC_WEBM_VP9_GENERIC_OPUS, + CODEC_WEBM_VP9_L51_OPUS, + CODEC_WEBM_VP9_L50_OPUS, + CODEC_WEBM_VP9_L41_OPUS, + CODEC_WEBM_VP9_L40_OPUS, + CODEC_WEBM_VP9_GENERIC_VORBIS, + CODEC_WEBM_VP9_L51_VORBIS, + CODEC_WEBM_VP9_L50_VORBIS, + CODEC_WEBM_VP9_L41_VORBIS, + CODEC_WEBM_VP9_L40_VORBIS, + CODEC_WEBM_VP8_GENERIC_OPUS, + CODEC_WEBM_VP8_GENERIC_VORBIS, + CODEC_MP4_GENERIC, + CODEC_WEBM_GENERIC + }; + + public final String name; + public final String fileExt; + public final String container; + public final String videoCodec; + public final String audioCodec; + public final boolean advanced; + public final String mimeType; + + private EnumScreenRecordingCodec(String name, String fileExt, String container, String videoCodec, String audioCodec, boolean advanced) { + this.name = name; + this.fileExt = fileExt; + this.container = container; + this.videoCodec = videoCodec; + this.audioCodec = audioCodec; + this.advanced = advanced; + if(videoCodec != null || audioCodec != null) { + StringBuilder mimeBuilder = new StringBuilder(container); + mimeBuilder.append(";codecs=\""); + if(videoCodec != null) { + mimeBuilder.append(videoCodec); + if(audioCodec != null) { + mimeBuilder.append(','); + } + } + if(audioCodec != null) { + mimeBuilder.append(audioCodec); + } + mimeBuilder.append("\""); + this.mimeType = mimeBuilder.toString(); + }else { + this.mimeType = container; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenRecordingNote.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenRecordingNote.java new file mode 100755 index 0000000..ab37074 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenRecordingNote.java @@ -0,0 +1,52 @@ +package net.lax1dude.eaglercraft.v1_8.recording; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenRecordingNote extends GuiScreen { + + private GuiScreen cont; + + public static boolean hasShown = false; + + public GuiScreenRecordingNote(GuiScreen cont) { + this.cont = cont; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 108, I18n.format("gui.done"))); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("options.recordingNote.title"), this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, I18n.format("options.recordingNote.text0"), this.width / 2, 90, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("options.recordingNote.text1"), this.width / 2, 102, 16777215); + super.drawScreen(par1, par2, par3); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + hasShown = true; + this.mc.displayGuiScreen(cont); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenRecordingSettings.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenRecordingSettings.java new file mode 100755 index 0000000..2f810af --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenRecordingSettings.java @@ -0,0 +1,201 @@ +package net.lax1dude.eaglercraft.v1_8.recording; + +import net.lax1dude.eaglercraft.v1_8.HString; +import net.lax1dude.eaglercraft.v1_8.internal.ScreenRecordParameters; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiSlider2; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenRecordingSettings extends GuiScreen { + + private static final Logger logger = LogManager.getLogger("GuiScreenRecordingSettings"); + + protected final GuiScreen parent; + + protected GuiButton recordButton; + protected GuiButton codecButton; + protected GuiSlider2 videoResolutionSlider; + protected GuiSlider2 videoFrameRateSlider; + protected GuiSlider2 audioBitrateSlider; + protected GuiSlider2 videoBitrateSlider; + protected GuiSlider2 microphoneVolumeSlider; + protected GuiSlider2 gameVolumeSlider; + protected boolean dirty = false; + + public GuiScreenRecordingSettings(GuiScreen parent) { + this.parent = parent; + } + + public void initGui() { + buttonList.clear(); + buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 168, I18n.format("gui.done"))); + buttonList.add(codecButton = new GuiButton(1, this.width / 2 + 65, this.height / 6 - 2, 75, 20, I18n.format("options.screenRecording.codecButton"))); + boolean isRecording = ScreenRecordingController.isRecording(); + buttonList.add(recordButton = new GuiButton(2, this.width / 2 + 15, this.height / 6 + 28, 125, 20, + I18n.format(isRecording ? "options.screenRecording.stop" : "options.screenRecording.start"))); + buttonList.add(videoResolutionSlider = new GuiSlider2(3, this.width / 2 - 155, this.height / 6 + 64, 150, 20, (mc.gameSettings.screenRecordResolution - 1) / 3.999f, 1.0f) { + @Override + protected String updateDisplayString() { + int i = (int)(sliderValue * 3.999f); + return I18n.format("options.screenRecording.videoResolution") + ": x" + HString.format("%.2f", 1.0f / (int)Math.pow(2.0, i)); + } + @Override + protected void onChange() { + mc.gameSettings.screenRecordResolution = 1 + (int)(sliderValue * 3.999f); + dirty = true; + } + }); + buttonList.add(videoFrameRateSlider = new GuiSlider2(4, this.width / 2 + 5, this.height / 6 + 64, 150, 20, (Math.max(mc.gameSettings.screenRecordFPS, 9) - 9) / 51.999f, 1.0f) { + @Override + protected String updateDisplayString() { + int i = (int)(sliderValue * 51.999f); + return I18n.format("options.screenRecording.videoFPS") + ": " + (i <= 0 ? I18n.format("options.screenRecording.onVSync") : 9 + i); + } + @Override + protected void onChange() { + int i = (int)(sliderValue * 51.999f); + mc.gameSettings.screenRecordFPS = i <= 0 ? -1 : 9 + i; + dirty = true; + } + }); + buttonList.add(videoBitrateSlider = new GuiSlider2(5, this.width / 2 - 155, this.height / 6 + 98, 150, 20, MathHelper.sqrt_float(MathHelper.clamp_float((mc.gameSettings.screenRecordVideoBitrate - 250) / 19750.999f, 0.0f, 1.0f)), 1.0f) { + @Override + protected String updateDisplayString() { + return I18n.format("options.screenRecording.videoBitrate") + ": " + (250 + (int)(sliderValue * sliderValue * 19750.999f)) + "kbps"; + } + @Override + protected void onChange() { + mc.gameSettings.screenRecordVideoBitrate = 250 + (int)(sliderValue * sliderValue * 19750.999f); + dirty = true; + } + }); + buttonList.add(audioBitrateSlider = new GuiSlider2(6, this.width / 2 + 5, this.height / 6 + 98, 150, 20, MathHelper.sqrt_float(MathHelper.clamp_float((mc.gameSettings.screenRecordAudioBitrate - 24) / 232.999f, 0.0f, 1.0f)), 1.0f) { + @Override + protected String updateDisplayString() { + return I18n.format("options.screenRecording.audioBitrate") + ": " + (24 + (int)(sliderValue * sliderValue * 232.999f)) + "kbps"; + } + @Override + protected void onChange() { + mc.gameSettings.screenRecordAudioBitrate = 24 + (int)(sliderValue * sliderValue * 232.999f); + dirty = true; + } + }); + buttonList.add(gameVolumeSlider = new GuiSlider2(7, this.width / 2 - 155, this.height / 6 + 130, 150, 20, mc.gameSettings.screenRecordGameVolume, 1.0f) { + @Override + protected String updateDisplayString() { + return I18n.format("options.screenRecording.gameVolume") + ": " + (int)(sliderValue * 100.999f) + "%"; + } + @Override + protected void onChange() { + mc.gameSettings.screenRecordGameVolume = sliderValue; + ScreenRecordingController.setGameVolume(sliderValue); + dirty = true; + } + }); + buttonList.add(microphoneVolumeSlider = new GuiSlider2(8, this.width / 2 + 5, this.height / 6 + 130, 150, 20, mc.gameSettings.screenRecordMicVolume, 1.0f) { + @Override + protected String updateDisplayString() { + return I18n.format("options.screenRecording.microphoneVolume") + ": " + (int)(sliderValue * 100.999f) + "%"; + } + @Override + protected void onChange() { + mc.gameSettings.screenRecordMicVolume = sliderValue; + ScreenRecordingController.setMicrophoneVolume(sliderValue); + dirty = true; + } + }); + codecButton.enabled = !isRecording; + videoResolutionSlider.enabled = !isRecording; + videoFrameRateSlider.enabled = !isRecording; + audioBitrateSlider.enabled = !isRecording; + videoBitrateSlider.enabled = !isRecording; + microphoneVolumeSlider.enabled = !ScreenRecordingController.isMicVolumeLocked(); + } + + protected void actionPerformed(GuiButton parGuiButton) { + if(parGuiButton.id == 0) { + if(dirty) { + mc.gameSettings.saveOptions(); + dirty = false; + } + mc.displayGuiScreen(parent); + }else if(parGuiButton.id == 1) { + mc.displayGuiScreen(new GuiScreenSelectCodec(this, mc.gameSettings.screenRecordCodec)); + }else if(parGuiButton.id == 2) { + if(!ScreenRecordingController.isRecording()) { + try { + ScreenRecordingController.startRecording(new ScreenRecordParameters(mc.gameSettings.screenRecordCodec, + mc.gameSettings.screenRecordResolution, mc.gameSettings.screenRecordVideoBitrate, + mc.gameSettings.screenRecordAudioBitrate, mc.gameSettings.screenRecordFPS)); + }catch(Throwable t) { + logger.error("Failed to begin screen recording!"); + logger.error(t); + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("options.screenRecording.failed", t.toString(), parent)); + } + }else { + ScreenRecordingController.endRecording(); + } + } + } + + public void drawScreen(int i, int j, float var3) { + drawDefaultBackground(); + drawCenteredString(fontRendererObj, I18n.format("options.screenRecording.title"), this.width / 2, 15, 16777215); + if(mc.gameSettings.screenRecordCodec == null) { + mc.gameSettings.screenRecordCodec = ScreenRecordingController.getDefaultCodec(); + } + + String codecString = mc.gameSettings.screenRecordCodec.name; + int codecStringWidth = fontRendererObj.getStringWidth(codecString); + drawString(fontRendererObj, codecString, this.width / 2 + 60 - codecStringWidth, this.height / 6 + 4, 0xFFFFFF); + + boolean isRecording = ScreenRecordingController.isRecording(); + codecButton.enabled = !isRecording; + videoResolutionSlider.enabled = !isRecording; + videoFrameRateSlider.enabled = !isRecording; + audioBitrateSlider.enabled = !isRecording; + videoBitrateSlider.enabled = !isRecording; + microphoneVolumeSlider.enabled = !ScreenRecordingController.isMicVolumeLocked(); + recordButton.displayString = I18n.format(isRecording ? "options.screenRecording.stop" : "options.screenRecording.start"); + String statusString = I18n.format("options.screenRecording.status", + (isRecording ? EnumChatFormatting.GREEN : EnumChatFormatting.RED) + I18n.format(isRecording ? "options.screenRecording.status.1" : "options.screenRecording.status.0")); + int statusStringWidth = fontRendererObj.getStringWidth(statusString); + drawString(fontRendererObj, statusString, this.width / 2 + 10 - statusStringWidth, this.height / 6 + 34, 0xFFFFFF); + + super.drawScreen(i, j, var3); + } + + protected void handleCodecCallback(EnumScreenRecordingCodec codec) { + EnumScreenRecordingCodec oldCodec = mc.gameSettings.screenRecordCodec; + if(ScreenRecordingController.codecs.contains(codec)) { + mc.gameSettings.screenRecordCodec = codec; + }else { + mc.gameSettings.screenRecordCodec = ScreenRecordingController.getDefaultCodec(); + } + if(oldCodec != mc.gameSettings.screenRecordCodec) { + dirty = true; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenSelectCodec.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenSelectCodec.java new file mode 100755 index 0000000..32d67de --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiScreenSelectCodec.java @@ -0,0 +1,92 @@ +package net.lax1dude.eaglercraft.v1_8.recording; + +import java.io.IOException; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenSelectCodec extends GuiScreen { + + protected final GuiScreenRecordingSettings parent; + protected List codecs; + protected int selectedCodec; + protected GuiSlotSelectCodec slots; + protected GuiButton showAllButton; + protected boolean showAll; + protected EnumScreenRecordingCodec codec; + + public GuiScreenSelectCodec(GuiScreenRecordingSettings parent, EnumScreenRecordingCodec codec) { + this.parent = parent; + this.codec = codec; + } + + public void initGui() { + showAll = codec.advanced; + codecs = showAll ? ScreenRecordingController.advancedCodecsOrdered : ScreenRecordingController.simpleCodecsOrdered; + selectedCodec = codecs.indexOf(codec); + buttonList.clear(); + buttonList.add(showAllButton = new GuiButton(0, this.width / 2 - 154, this.height - 38, 150, 20, + I18n.format("options.recordingCodec.showAdvancedCodecs", I18n.format(showAll ? "gui.yes" : "gui.no")))); + buttonList.add(new GuiButton(1, this.width / 2 + 4, this.height - 38, 150, 20, I18n.format("gui.done"))); + slots = new GuiSlotSelectCodec(this, 32, height - 45); + } + + protected void actionPerformed(GuiButton parGuiButton) { + if(parGuiButton.id == 0) { + changeStateShowAll(!showAll); + showAllButton.displayString = I18n.format("options.recordingCodec.showAdvancedCodecs", I18n.format(showAll ? "gui.yes" : "gui.no")); + }else if(parGuiButton.id == 1) { + if(selectedCodec >= 0 && selectedCodec < codecs.size()) { + parent.handleCodecCallback(codecs.get(selectedCodec)); + } + mc.displayGuiScreen(parent); + } + } + + protected void changeStateShowAll(boolean newShowAll) { + if(newShowAll == showAll) return; + EnumScreenRecordingCodec oldCodec = codecs.get(selectedCodec >= 0 && selectedCodec < codecs.size() ? selectedCodec : 0); + codecs = newShowAll ? ScreenRecordingController.advancedCodecsOrdered : ScreenRecordingController.simpleCodecsOrdered; + showAll = newShowAll; + selectedCodec = codecs.indexOf(oldCodec); + } + + public void drawScreen(int i, int j, float var3) { + slots.drawScreen(i, j, var3); + this.drawCenteredString(this.fontRendererObj, I18n.format("options.recordingCodec.title"), this.width / 2, 16, 16777215); + super.drawScreen(i, j, var3); + } + + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + slots.handleMouseInput(); + } + + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + slots.handleTouchInput(); + } + + static Minecraft getMC(GuiScreenSelectCodec screen) { + return screen.mc; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiSlotSelectCodec.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiSlotSelectCodec.java new file mode 100755 index 0000000..3e2575b --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/GuiSlotSelectCodec.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.recording; + +import net.minecraft.client.gui.GuiSlot; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiSlotSelectCodec extends GuiSlot { + + protected final GuiScreenSelectCodec screen; + + public GuiSlotSelectCodec(GuiScreenSelectCodec screen, int topIn, int bottomIn) { + super(GuiScreenSelectCodec.getMC(screen), screen.width, screen.height, topIn, bottomIn, 18); + this.screen = screen; + } + + @Override + protected int getSize() { + return screen.codecs.size(); + } + + @Override + protected void elementClicked(int var1, boolean var2, int var3, int var4) { + if(var1 < screen.codecs.size()) { + screen.selectedCodec = var1; + } + } + + @Override + protected boolean isSelected(int var1) { + return screen.selectedCodec == var1; + } + + @Override + protected void drawBackground() { + screen.drawDefaultBackground(); + } + + @Override + protected void drawSlot(int id, int xx, int yy, int width, int height, int ii) { + if(id < screen.codecs.size()) { + this.screen.drawString(mc.fontRendererObj, screen.codecs.get(id).name, xx + 4, yy + 3, 0xFFFFFF); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/ScreenRecordingController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/ScreenRecordingController.java new file mode 100755 index 0000000..cf805c6 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/recording/ScreenRecordingController.java @@ -0,0 +1,99 @@ +package net.lax1dude.eaglercraft.v1_8.recording; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformScreenRecord; +import net.lax1dude.eaglercraft.v1_8.internal.ScreenRecordParameters; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ScreenRecordingController { + + public static final int DEFAULT_FPS = 30; + public static final int DEFAULT_RESOLUTION = 1; + public static final int DEFAULT_AUDIO_BITRATE = 120; + public static final int DEFAULT_VIDEO_BITRATE = 2500; + public static final float DEFAULT_GAME_VOLUME = 1.0f; + public static final float DEFAULT_MIC_VOLUME = 0.0f; + + public static final List simpleCodecsOrdered = new ArrayList<>(); + public static final List advancedCodecsOrdered = new ArrayList<>(); + public static final Set codecs = new HashSet<>(); + private static boolean supported = false; + + public static void initialize() { + simpleCodecsOrdered.clear(); + advancedCodecsOrdered.clear(); + codecs.clear(); + supported = PlatformScreenRecord.isSupported(); + if(supported) { + EnumScreenRecordingCodec[] codecsOrdered = EnumScreenRecordingCodec.preferred_codec_order; + for(int i = 0; i < codecsOrdered.length; ++i) { + EnumScreenRecordingCodec codec = codecsOrdered[i]; + if(PlatformScreenRecord.isCodecSupported(codec)) { + if(!codec.advanced) { + simpleCodecsOrdered.add(codec); + } + advancedCodecsOrdered.add(codec); + codecs.add(codec); + } + } + } + if(codecs.isEmpty()) { + supported = false; + } + } + + public static boolean isSupported() { + return supported; + } + + public static void setGameVolume(float volume) { + PlatformScreenRecord.setGameVolume(volume); + } + + public static void setMicrophoneVolume(float volume) { + PlatformScreenRecord.setMicrophoneVolume(volume); + } + + public static void startRecording(ScreenRecordParameters params) { + PlatformScreenRecord.startRecording(params); + } + + public static void endRecording() { + PlatformScreenRecord.endRecording(); + } + + public static boolean isRecording() { + return PlatformScreenRecord.isRecording(); + } + + public static boolean isMicVolumeLocked() { + return PlatformScreenRecord.isMicVolumeLocked(); + } + + public static boolean isVSyncLocked() { + return PlatformScreenRecord.isVSyncLocked(); + } + + public static EnumScreenRecordingCodec getDefaultCodec() { + return simpleCodecsOrdered.isEmpty() ? (advancedCodecsOrdered.isEmpty() ? null : advancedCodecsOrdered.get(0)) : simpleCodecsOrdered.get(0); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java index 18d7fe2..2fdf6dc 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java @@ -1,462 +1,506 @@ -package net.lax1dude.eaglercraft.v1_8.socket; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import org.teavm.jso.JSBody; - -import net.lax1dude.eaglercraft.v1_8.ArrayUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; -import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.profile.GuiAuthenticationScreen; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiDisconnected; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.multiplayer.GuiConnecting; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.IChatComponent; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ConnectionHandshake { - - private static final long baseTimeout = 15000l; - - private static final int protocolV2 = 2; - private static final int protocolV3 = 3; - - private static final Logger logger = LogManager.getLogger(); - - public static String pluginVersion = null; - public static String pluginBrand = null; - - public static boolean attemptHandshake(Minecraft mc, GuiConnecting connecting, GuiScreen ret, String password, - boolean allowPlaintext) { - try { - pluginVersion = null; - pluginBrand = null; - EaglerOutputStream bao = new EaglerOutputStream(); - DataOutputStream d = new DataOutputStream(bao); - - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_VERSION); - - d.writeByte(2); // legacy protocol version - - d.writeShort(2); // supported eagler protocols count - d.writeShort(protocolV2); // client supports v2 - d.writeShort(protocolV3); // client supports v3 - - d.writeShort(1); // supported game protocols count - d.writeShort(47); // client supports 1.8 protocol - - String clientBrand = EaglercraftVersion.projectForkName; - d.writeByte(clientBrand.length()); - d.writeBytes(clientBrand); - - String clientVers = EaglercraftVersion.projectOriginVersion; - d.writeByte(clientVers.length()); - d.writeBytes(clientVers); - - d.writeBoolean(password != null); - - String username = mc.getSession().getProfile().getName(); - d.writeByte(username.length()); - d.writeBytes(username); - - PlatformNetworking.writePlayPacket(bao.toByteArray()); - - byte[] read = awaitNextPacket(baseTimeout); - if (read == null) { - logger.error("Read timed out while waiting for server protocol response!"); - return false; - } - - DataInputStream di = new DataInputStream(new EaglerInputStream(read)); - - int type = di.read(); - if (type == HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH) { - - StringBuilder protocols = new StringBuilder(); - int c = di.readShort(); - for (int i = 0; i < c; ++i) { - if (i > 0) { - protocols.append(", "); - } - protocols.append("v").append(di.readShort()); - } - - StringBuilder games = new StringBuilder(); - c = di.readShort(); - for (int i = 0; i < c; ++i) { - if (i > 0) { - games.append(", "); - } - games.append("mc").append(di.readShort()); - } - - logger.info("Incompatible client: v2 & mc47"); - logger.info("Server supports: {}", protocols); - logger.info("Server supports: {}", games); - - int msgLen = di.read(); - byte[] dat = new byte[msgLen]; - di.read(dat); - String msg = new String(dat, StandardCharsets.UTF_8); - - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(msg))); - - return false; - } else if (type == HandshakePacketTypes.PROTOCOL_SERVER_VERSION) { - int serverVers = di.readShort(); - - if (serverVers != protocolV2 && serverVers != protocolV3) { - logger.info("Incompatible server version: {}", serverVers); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - new ChatComponentText(serverVers < protocolV2 ? "Outdated Server" : "Outdated Client"))); - return false; - } - - int gameVers = di.readShort(); - if (gameVers != 47) { - logger.info("Incompatible minecraft protocol version: {}", gameVers); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - new ChatComponentText("This server does not support 1.8!"))); - return false; - } - - logger.info("Server protocol: {}", serverVers); - - int msgLen = di.read(); - byte[] dat = new byte[msgLen]; - di.read(dat); - pluginBrand = ArrayUtils.asciiString(dat); - - msgLen = di.read(); - dat = new byte[msgLen]; - di.read(dat); - pluginVersion = ArrayUtils.asciiString(dat); - - logger.info("Server version: {}", pluginVersion); - logger.info("Server brand: {}", pluginBrand); - - int authType = di.read(); - int saltLength = (int) di.readShort() & 0xFFFF; - - byte[] salt = new byte[saltLength]; - di.read(salt); - - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN); - - d.writeByte(username.length()); - d.writeBytes(username); - - String requestedServer = "default"; - d.writeByte(requestedServer.length()); - d.writeBytes(requestedServer); - - if (authType != 0 && password != null && password.length() > 0) { - if (authType == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { - if (allowPlaintext) { - logger.warn("Server is using insecure plaintext authentication"); - d.writeByte(password.length() << 1); - d.writeChars(password); - } else { - logger.error( - "Plaintext authentication was attempted but no user confirmation has been given to proceed"); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - new ChatComponentText(EnumChatFormatting.RED - + "Plaintext authentication was attempted but no user confirmation has been given to proceed"))); - return false; - } - } else if (authType == HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256) { - SHA256Digest digest = new SHA256Digest(); - - int passLen = password.length(); - - digest.update((byte)((passLen >>> 8) & 0xFF)); - digest.update((byte)(passLen & 0xFF)); - - for(int i = 0; i < passLen; ++i) { - char codePoint = password.charAt(i); - digest.update((byte)((codePoint >>> 8) & 0xFF)); - digest.update((byte)(codePoint & 0xFF)); - } - - digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_SAVE, 0, 32); - - byte[] hashed = new byte[32]; - digest.doFinal(hashed, 0); - - digest.reset(); - - digest.update(hashed, 0, 32); - digest.update(salt, 0, 32); - digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); - - digest.doFinal(hashed, 0); - - digest.reset(); - - digest.update(hashed, 0, 32); - digest.update(salt, 32, 32); - digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); - - digest.doFinal(hashed, 0); - - d.writeByte(32); - d.write(hashed); - } else if (authType == HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { - SHA256Digest digest = new SHA256Digest(); - - byte[] passwd = password.getBytes(StandardCharsets.UTF_8); - digest.update(passwd, 0, passwd.length); - - byte[] hashed = new byte[32]; - digest.doFinal(hashed, 0); - - byte[] toHexAndSalt = new byte[64]; - for (int i = 0; i < 32; ++i) { - toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; - toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; - } - - digest.reset(); - digest.update(toHexAndSalt, 0, 64); - digest.update(salt, 0, salt.length); - - digest.doFinal(hashed, 0); - - for (int i = 0; i < 32; ++i) { - toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; - toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; - } - - d.writeByte(64); - d.write(toHexAndSalt); - } else { - logger.error("Unsupported authentication type: {}", authType); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - new ChatComponentText( - EnumChatFormatting.RED + "Unsupported authentication type: " + authType + "\n\n" - + EnumChatFormatting.GRAY + "(Use a newer version of the client)"))); - return false; - } - } else { - d.writeByte(0); - } - - PlatformNetworking.writePlayPacket(bao.toByteArray()); - - read = awaitNextPacket(baseTimeout); - if (read == null) { - logger.error("Read timed out while waiting for login negotiation response!"); - return false; - } - - di = new DataInputStream(new EaglerInputStream(read)); - type = di.read(); - if (type == HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN) { - msgLen = di.read(); - dat = new byte[msgLen]; - di.read(dat); - - String serverUsername = ArrayUtils.asciiString(dat); - - Minecraft.getMinecraft().getSession().update(serverUsername, - new EaglercraftUUID(di.readLong(), di.readLong())); - - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); - String profileDataType = "skin_v1"; - d.writeByte(profileDataType.length()); - d.writeBytes(profileDataType); - byte[] packetSkin = EaglerProfile.getSkinPacket(); - if (packetSkin.length > 0xFFFF) { - throw new IOException("Skin packet is too long: " + packetSkin.length); - } - d.writeShort(packetSkin.length); - d.write(packetSkin); - PlatformNetworking.writePlayPacket(bao.toByteArray()); - - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); - profileDataType = "cape_v1"; - d.writeByte(profileDataType.length()); - d.writeBytes(profileDataType); - byte[] packetCape = EaglerProfile.getCapePacket(); - if (packetCape.length > 0xFFFF) { - throw new IOException("Cape packet is too long: " + packetCape.length); - } - d.writeShort(packetCape.length); - d.write(packetCape); - PlatformNetworking.writePlayPacket(bao.toByteArray()); - - byte[] packetSignatureData = UpdateService.getClientSignatureData(); - if (packetSignatureData != null) { - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); - profileDataType = "update_cert_v1"; - d.writeByte(profileDataType.length()); - d.writeBytes(profileDataType); - if (packetSignatureData.length > 0xFFFF) { - throw new IOException( - "Update certificate login packet is too long: " + packetSignatureData.length); - } - d.writeShort(packetSignatureData.length); - d.write(packetSignatureData); - PlatformNetworking.writePlayPacket(bao.toByteArray()); - } - - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN); - PlatformNetworking.writePlayPacket(bao.toByteArray()); - - read = awaitNextPacket(baseTimeout); - if (read == null) { - logger.error("Read timed out while waiting for login confirmation response!"); - return false; - } - - di = new DataInputStream(new EaglerInputStream(read)); - type = di.read(); - if (type == HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN) { - return true; - } else if (type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { - showError(mc, connecting, ret, di, serverVers == protocolV2); - return false; - } else { - return false; - } - } else if (type == HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN) { - if (serverVers == protocolV2) { - msgLen = di.read(); - } else { - msgLen = di.readUnsignedShort(); - } - dat = new byte[msgLen]; - di.read(dat); - String errStr = new String(dat, StandardCharsets.UTF_8); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - IChatComponent.Serializer.jsonToComponent(errStr))); - return false; - } else if (type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { - showError(mc, connecting, ret, di, serverVers == protocolV2); - return false; - } else { - return false; - } - } else if (type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { - showError(mc, connecting, ret, di, true); - return false; - } else { - return false; - } - } catch (Throwable t) { - logger.error("Exception in handshake"); - logger.error(t); - return false; - } - - } - - private static byte[] awaitNextPacket(long timeout) { - long millis = System.currentTimeMillis(); - byte[] b; - while ((b = PlatformNetworking.readPlayPacket()) == null) { - if (PlatformNetworking.playConnectionState().isClosed()) { - return null; - } - try { - Thread.sleep(50l); - } catch (InterruptedException e) { - } - if (System.currentTimeMillis() - millis > timeout) { - PlatformNetworking.playDisconnect(); - return null; - } - } - return b; - } - - @JSBody(params = {}, script = "window.onbeforeunload = null; location.reload();") - public static native void reloadPage(); - - private static void showError(Minecraft mc, GuiConnecting connecting, GuiScreen scr, DataInputStream err, - boolean v2) throws IOException { - int errorCode = err.read(); - int msgLen = v2 ? err.read() : err.readUnsignedShort(); - byte[] dat = new byte[msgLen]; - err.read(dat); - String errStr = new String(dat, StandardCharsets.UTF_8); - logger.info("Server Error Code {}: {}", errorCode, errStr); - if (errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_BLOCKED) { - RateLimitTracker.registerBlock(PlatformNetworking.getCurrentURI()); - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr)); - } else if (errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) { - RateLimitTracker.registerLockOut(PlatformNetworking.getCurrentURI()); - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr)); - } else if (errorCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) { - if (IChatComponent.Serializer.jsonToComponent(errStr).getUnformattedText().toLowerCase() - .contains("reload page")) { - EaglerProfile.updateUsernameCookieFromLocalStorage(); - reloadPage(); - } - mc.displayGuiScreen( - new GuiDisconnected(scr, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr))); - } else if (connecting != null && errorCode == HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED) { - mc.displayGuiScreen(new GuiAuthenticationScreen(connecting, scr, errStr)); - } else { - mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", - new ChatComponentText("Server Error Code " + errorCode + "\n" + errStr))); - } - } - - public static GuiScreen displayAuthProtocolConfirm(int protocol, GuiScreen no, GuiScreen yes) { - if (protocol == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { - return new GuiHandshakeApprove("plaintext", no, yes); - } else if (protocol != HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256 - && protocol != HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { - return new GuiHandshakeApprove("unsupportedAuth", no); - } else { - return null; - } - } - - private static final byte[] HEX = new byte[] { - (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', - (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' - }; -} +package net.lax1dude.eaglercraft.v1_8.socket; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.profile.GuiAuthenticationScreen; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiDisconnected; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.GuiConnecting; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ConnectionHandshake { + + private static final long baseTimeout = 10000l; + + private static final int protocolV2 = 2; + private static final int protocolV3 = 3; + private static final int protocolV4 = 4; + + private static final Logger logger = LogManager.getLogger(); + + public static String pluginVersion = null; + public static String pluginBrand = null; + public static int protocolVersion = -1; + + public static byte[] getSPHandshakeProtocolData() { + try { + EaglerOutputStream bao = new EaglerOutputStream(); + DataOutputStream d = new DataOutputStream(bao); + d.writeShort(3); // supported eagler protocols count + d.writeShort(protocolV2); // client supports v2 + d.writeShort(protocolV3); // client supports v3 + d.writeShort(protocolV4); // client supports v4 + return bao.toByteArray(); + }catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + public static boolean attemptHandshake(Minecraft mc, IWebSocketClient client, GuiConnecting connecting, + GuiScreen ret, String password, boolean allowPlaintext, boolean enableCookies, byte[] cookieData) { + try { + EaglerProfile.clearServerSkinOverride(); + PauseMenuCustomizeState.reset(); + pluginVersion = null; + pluginBrand = null; + protocolVersion = -1; + EaglerOutputStream bao = new EaglerOutputStream(); + DataOutputStream d = new DataOutputStream(bao); + + d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_VERSION); + + d.writeByte(2); // legacy protocol version + + d.write(getSPHandshakeProtocolData()); // write supported eagler protocol versions + + d.writeShort(1); // supported game protocols count + d.writeShort(47); // client supports 1.8 protocol + + String clientBrand = EaglercraftVersion.projectForkName; + d.writeByte(clientBrand.length()); + d.writeBytes(clientBrand); + + String clientVers = EaglercraftVersion.projectOriginVersion; + d.writeByte(clientVers.length()); + d.writeBytes(clientVers); + + d.writeBoolean(password != null); + + String username = mc.getSession().getProfile().getName(); + d.writeByte(username.length()); + d.writeBytes(username); + + client.send(bao.toByteArray()); + + byte[] read = awaitNextPacket(client, baseTimeout); + if(read == null) { + logger.error("Read timed out while waiting for server protocol response!"); + return false; + } + + DataInputStream di = new DataInputStream(new EaglerInputStream(read)); + + int type = di.read(); + if(type == HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH) { + + StringBuilder protocols = new StringBuilder(); + int c = di.readShort(); + for(int i = 0; i < c; ++i) { + if(i > 0) { + protocols.append(", "); + } + protocols.append("v").append(di.readShort()); + } + + StringBuilder games = new StringBuilder(); + c = di.readShort(); + for(int i = 0; i < c; ++i) { + if(i > 0) { + games.append(", "); + } + games.append("mc").append(di.readShort()); + } + + logger.info("Incompatible client: v2/v3/v4 & mc47"); + logger.info("Server supports: {}", protocols); + logger.info("Server supports: {}", games); + + int msgLen = di.read(); + byte[] dat = new byte[msgLen]; + di.read(dat); + String msg = new String(dat, StandardCharsets.UTF_8); + + mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(msg))); + + return false; + }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_VERSION) { + protocolVersion = di.readShort(); + + if(protocolVersion != protocolV2 && protocolVersion != protocolV3 && protocolVersion != protocolV4) { + logger.info("Incompatible server version: {}", protocolVersion); + mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(protocolVersion < protocolV2 ? "Outdated Server" : "Outdated Client"))); + return false; + } + + int gameVers = di.readShort(); + if(gameVers != 47) { + logger.info("Incompatible minecraft protocol version: {}", gameVers); + mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText("This server does not support 1.8!"))); + return false; + } + + logger.info("Server protocol: {}", protocolVersion); + + int msgLen = di.read(); + byte[] dat = new byte[msgLen]; + di.read(dat); + pluginBrand = ArrayUtils.asciiString(dat); + + msgLen = di.read(); + dat = new byte[msgLen]; + di.read(dat); + pluginVersion = ArrayUtils.asciiString(dat); + + logger.info("Server version: {}", pluginVersion); + logger.info("Server brand: {}", pluginBrand); + + int authType = di.read(); + int saltLength = (int)di.readShort() & 0xFFFF; + + byte[] salt = new byte[saltLength]; + di.read(salt); + + bao.reset(); + d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN); + + d.writeByte(username.length()); + d.writeBytes(username); + + String requestedServer = "default"; + d.writeByte(requestedServer.length()); + d.writeBytes(requestedServer); + + if(authType != 0 && password != null && password.length() > 0) { + if(authType == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { + if(allowPlaintext) { + logger.warn("Server is using insecure plaintext authentication"); + d.writeByte(password.length() << 1); + d.writeChars(password); + }else { + logger.error("Plaintext authentication was attempted but no user confirmation has been given to proceed"); + mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", + new ChatComponentText(EnumChatFormatting.RED + "Plaintext authentication was attempted but no user confirmation has been given to proceed"))); + return false; + } + }else if(authType == HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256) { + SHA256Digest digest = new SHA256Digest(); + + int passLen = password.length(); + + digest.update((byte)((passLen >>> 8) & 0xFF)); + digest.update((byte)(passLen & 0xFF)); + + for(int i = 0; i < passLen; ++i) { + char codePoint = password.charAt(i); + digest.update((byte)((codePoint >>> 8) & 0xFF)); + digest.update((byte)(codePoint & 0xFF)); + } + + digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_SAVE, 0, 32); + + byte[] hashed = new byte[32]; + digest.doFinal(hashed, 0); + + digest.reset(); + + digest.update(hashed, 0, 32); + digest.update(salt, 0, 32); + digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); + + digest.doFinal(hashed, 0); + + digest.reset(); + + digest.update(hashed, 0, 32); + digest.update(salt, 32, 32); + digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); + + digest.doFinal(hashed, 0); + + d.writeByte(32); + d.write(hashed); + }else if(authType == HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { + SHA256Digest digest = new SHA256Digest(); + + byte[] passwd = password.getBytes(StandardCharsets.UTF_8); + digest.update(passwd, 0, passwd.length); + + byte[] hashed = new byte[32]; + digest.doFinal(hashed, 0); + + byte[] toHexAndSalt = new byte[64]; + for(int i = 0; i < 32; ++i) { + toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; + toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; + } + + digest.reset(); + digest.update(toHexAndSalt, 0, 64); + digest.update(salt, 0, salt.length); + + digest.doFinal(hashed, 0); + + for(int i = 0; i < 32; ++i) { + toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; + toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; + } + + d.writeByte(64); + d.write(toHexAndSalt); + }else { + logger.error("Unsupported authentication type: {}", authType); + mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", + new ChatComponentText(EnumChatFormatting.RED + "Unsupported authentication type: " + authType + "\n\n" + EnumChatFormatting.GRAY + "(Use a newer version of the client)"))); + return false; + } + }else { + d.writeByte(0); + } + if(protocolVersion >= protocolV4) { + d.writeBoolean(enableCookies); + if(enableCookies && cookieData != null) { + d.writeByte(cookieData.length); + d.write(cookieData); + }else { + d.writeByte(0); + } + } + + client.send(bao.toByteArray()); + + read = awaitNextPacket(client, baseTimeout); + if(read == null) { + logger.error("Read timed out while waiting for login negotiation response!"); + return false; + } + + di = new DataInputStream(new EaglerInputStream(read)); + type = di.read(); + if(type == HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN) { + msgLen = di.read(); + dat = new byte[msgLen]; + di.read(dat); + + String serverUsername = ArrayUtils.asciiString(dat); + + Minecraft.getMinecraft().getSession().update(serverUsername, new EaglercraftUUID(di.readLong(), di.readLong())); + + Map profileDataToSend = new HashMap<>(); + + if(protocolVersion >= 4) { + bao.reset(); + d.writeLong(EaglercraftVersion.clientBrandUUID.msb); + d.writeLong(EaglercraftVersion.clientBrandUUID.lsb); + profileDataToSend.put("brand_uuid_v1", bao.toByteArray()); + } + + byte[] packetSkin = EaglerProfile.getSkinPacket(protocolVersion); + if(packetSkin.length > 0xFFFF) { + throw new IOException("Skin packet is too long: " + packetSkin.length); + } + profileDataToSend.put(protocolVersion >= 4 ? "skin_v2" : "skin_v1", packetSkin); + + byte[] packetCape = EaglerProfile.getCapePacket(); + if(packetCape.length > 0xFFFF) { + throw new IOException("Cape packet is too long: " + packetCape.length); + } + profileDataToSend.put("cape_v1", packetCape); + + byte[] packetSignatureData = UpdateService.getClientSignatureData(); + if(packetSignatureData != null) { + profileDataToSend.put("update_cert_v1", packetSignatureData); + } + + if(protocolVersion >= 4) { + List> toSend = new ArrayList<>(profileDataToSend.entrySet()); + while(!toSend.isEmpty()) { + int sendLen = 2; + bao.reset(); + d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); + d.writeByte(0); // will be replaced + int packetCount = 0; + while(!toSend.isEmpty() && packetCount < 255) { + Entry etr = toSend.get(toSend.size() - 1); + int i = 3 + etr.getKey().length() + etr.getValue().length; + if(sendLen + i < 0xFF00) { + String profileDataType = etr.getKey(); + d.writeByte(profileDataType.length()); + d.writeBytes(profileDataType); + byte[] data = etr.getValue(); + d.writeShort(data.length); + d.write(data); + toSend.remove(toSend.size() - 1); + ++packetCount; + }else { + break; + } + } + byte[] send = bao.toByteArray(); + send[1] = (byte)packetCount; + client.send(send); + } + }else { + for(Entry etr : profileDataToSend.entrySet()) { + bao.reset(); + d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); + String profileDataType = etr.getKey(); + d.writeByte(profileDataType.length()); + d.writeBytes(profileDataType); + byte[] data = etr.getValue(); + d.writeShort(data.length); + d.write(data); + client.send(bao.toByteArray()); + } + } + + bao.reset(); + d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN); + client.send(bao.toByteArray()); + + read = awaitNextPacket(client, baseTimeout); + if(read == null) { + logger.error("Read timed out while waiting for login confirmation response!"); + return false; + } + + di = new DataInputStream(new EaglerInputStream(read)); + type = di.read(); + if(type == HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN) { + return true; + }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { + showError(mc, client, connecting, ret, di, protocolVersion == protocolV2); + return false; + }else { + return false; + } + }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN) { + if(protocolVersion == protocolV2) { + msgLen = di.read(); + }else { + msgLen = di.readUnsignedShort(); + } + dat = new byte[msgLen]; + di.read(dat); + String errStr = new String(dat, StandardCharsets.UTF_8); + mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr))); + return false; + }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { + showError(mc, client, connecting, ret, di, protocolVersion == protocolV2); + return false; + }else { + return false; + } + }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { + showError(mc, client, connecting, ret, di, true); + return false; + }else { + return false; + } + }catch(Throwable t) { + logger.error("Exception in handshake"); + logger.error(t); + return false; + } + + } + + private static byte[] awaitNextPacket(IWebSocketClient client, long timeout) { + long millis = EagRuntime.steadyTimeMillis(); + IWebSocketFrame b; + while((b = client.getNextBinaryFrame()) == null) { + if(client.getState().isClosed()) { + return null; + } + try { + Thread.sleep(50l); + } catch (InterruptedException e) { + } + if(EagRuntime.steadyTimeMillis() - millis > timeout) { + client.close(); + return null; + } + } + return b.getByteArray(); + } + + private static void showError(Minecraft mc, IWebSocketClient client, GuiConnecting connecting, GuiScreen scr, DataInputStream err, boolean v2) throws IOException { + int errorCode = err.read(); + int msgLen = v2 ? err.read() : err.readUnsignedShort(); + + // workaround for bug in EaglerXBungee 1.2.7 and below + if(msgLen == 0) { + if(v2) { + if(err.available() == 256) { + msgLen = 256; + } + }else { + if(err.available() == 65536) { + msgLen = 65536; + } + } + } + + byte[] dat = new byte[msgLen]; + err.read(dat); + String errStr = new String(dat, StandardCharsets.UTF_8); + logger.info("Server Error Code {}: {}", errorCode, errStr); + if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_BLOCKED) { + RateLimitTracker.registerBlock(client.getCurrentURI()); + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr)); + }else if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) { + RateLimitTracker.registerLockOut(client.getCurrentURI()); + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr)); + }else if(errorCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) { + mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr))); + }else if(connecting != null && errorCode == HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED) { + mc.displayGuiScreen(new GuiAuthenticationScreen(connecting, scr, errStr)); + }else { + mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", new ChatComponentText("Server Error Code " + errorCode + "\n" + errStr))); + } + } + + public static GuiScreen displayAuthProtocolConfirm(int protocol, GuiScreen no, GuiScreen yes) { + if(protocol == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { + return new GuiHandshakeApprove("plaintext", no, yes); + }else if(protocol != HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256 && protocol != HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { + return new GuiHandshakeApprove("unsupportedAuth", no); + }else { + return null; + } + } + + private static final byte[] HEX = new byte[] { + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', + (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' + }; +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java index 0c28ed2..86959d9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java @@ -1,197 +1,89 @@ package net.lax1dude.eaglercraft.v1_8.socket; import java.io.IOException; -import java.util.List; - -import org.teavm.jso.JSBody; import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; import net.minecraft.network.EnumConnectionState; -import net.minecraft.network.EnumPacketDirection; import net.minecraft.network.INetHandler; import net.minecraft.network.Packet; import net.minecraft.network.PacketBuffer; -import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglercraftNetworkManager { - +public abstract class EaglercraftNetworkManager { + protected final String address; protected INetHandler nethandler = null; protected EnumConnectionState packetState = EnumConnectionState.HANDSHAKING; protected final PacketBuffer temporaryBuffer; protected int debugPacketCounter = 0; - + protected String pluginBrand = null; protected String pluginVersion = null; - + public static final Logger logger = LogManager.getLogger("NetworkManager"); public EaglercraftNetworkManager(String address) { this.address = address; this.temporaryBuffer = new PacketBuffer(Unpooled.buffer(0x1FFFF)); } - + public void setPluginInfo(String pluginBrand, String pluginVersion) { this.pluginBrand = pluginBrand; this.pluginVersion = pluginVersion; } - + public String getPluginBrand() { return pluginBrand; } - + public String getPluginVersion() { return pluginVersion; } - - public void connect() { - PlatformNetworking.startPlayConnection(address); + + public abstract void connect(); + + public abstract EnumEaglerConnectionState getConnectStatus(); + + public String getAddress() { + return address; } - - public EnumEaglerConnectionState getConnectStatus() { - return PlatformNetworking.playConnectionState(); - } - - @JSBody(params = {}, script = "window.onbeforeunload = null; location.reload();") - public static native void reloadPage(); - - public void closeChannel(IChatComponent reason) { - PlatformNetworking.playDisconnect(); - if (nethandler != null) { - nethandler.onDisconnect(reason); - } - clientDisconnected = true; - System.out.println("Closed channel: " + reason.getUnformattedText()); - if (reason.getUnformattedText().toLowerCase().contains("reload page")) { - EaglerProfile.updateUsernameCookieFromLocalStorage(); - reloadPage(); - } - } - + + public abstract void closeChannel(IChatComponent reason); + public void setConnectionState(EnumConnectionState state) { packetState = state; } + + public abstract void processReceivedPackets() throws IOException; - public void processReceivedPackets() throws IOException { - if (nethandler == null) - return; - List pkts = PlatformNetworking.readAllPacket(); - - if (pkts == null) { - return; - } - - for (int i = 0, l = pkts.size(); i < l; ++i) { - byte[] next = pkts.get(i); - ++debugPacketCounter; - try { - ByteBuf nettyBuffer = Unpooled.buffer(next, next.length); - nettyBuffer.writerIndex(next.length); - PacketBuffer input = new PacketBuffer(nettyBuffer); - int pktId = input.readVarIntFromBuffer(); - - Packet pkt; - try { - pkt = packetState.getPacket(EnumPacketDirection.CLIENTBOUND, pktId); - } catch (IllegalAccessException | InstantiationException ex) { - throw new IOException("Recieved a packet with type " + pktId + " which is invalid!"); - } - - if (pkt == null) { - throw new IOException( - "Recieved packet type " + pktId + " which is undefined in state " + packetState); - } - - try { - pkt.readPacketData(input); - } catch (Throwable t) { - throw new IOException("Failed to read packet type '" + pkt.getClass().getSimpleName() + "'", t); - } - - try { - pkt.processPacket(nethandler); - } catch (Throwable t) { - logger.error("Failed to process {}! It'll be skipped for debug purposes.", - pkt.getClass().getSimpleName()); - logger.error(t); - } - - } catch (Throwable t) { - logger.error("Failed to process websocket frame {}! It'll be skipped for debug purposes.", - debugPacketCounter); - logger.error(t); - } - } - } - - public void sendPacket(Packet pkt) { - if (!isChannelOpen()) { - logger.error("Packet was sent on a closed connection: {}", pkt.getClass().getSimpleName()); - return; - } - - int i; - try { - i = packetState.getPacketId(EnumPacketDirection.SERVERBOUND, pkt); - } catch (Throwable t) { - logger.error("Incorrect packet for state: {}", pkt.getClass().getSimpleName()); - return; - } - - temporaryBuffer.clear(); - temporaryBuffer.writeVarIntToBuffer(i); - try { - pkt.writePacketData(temporaryBuffer); - } catch (IOException ex) { - logger.error("Failed to write packet {}!", pkt.getClass().getSimpleName()); - return; - } - - int len = temporaryBuffer.writerIndex(); - byte[] bytes = new byte[len]; - temporaryBuffer.getBytes(0, bytes); - - PlatformNetworking.writePlayPacket(bytes); - } - + public abstract void sendPacket(Packet pkt); + public void setNetHandler(INetHandler nethandler) { this.nethandler = nethandler; } - + public boolean isLocalChannel() { return false; } - + public boolean isChannelOpen() { return getConnectStatus() == EnumEaglerConnectionState.CONNECTED; } @@ -204,28 +96,17 @@ public class EaglercraftNetworkManager { throw new CompressionNotSupportedException(); } - public boolean checkDisconnected() { - if (PlatformNetworking.playConnectionState().isClosed()) { - try { - processReceivedPackets(); // catch kick message - } catch (IOException e) { - } - doClientDisconnect(new ChatComponentTranslation("disconnect.endOfStream")); - return true; - } else { - return false; - } - } - + public abstract boolean checkDisconnected(); + protected boolean clientDisconnected = false; - + protected void doClientDisconnect(IChatComponent msg) { - if (!clientDisconnected) { + if(!clientDisconnected) { clientDisconnected = true; - if (nethandler != null) { + if(nethandler != null) { this.nethandler.onDisconnect(msg); } } } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java index bd8bcbe..19882b9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java @@ -8,24 +8,16 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.resources.I18n; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -54,19 +46,19 @@ public class GuiHandshakeApprove extends GuiScreen { public void initGui() { this.buttonList.clear(); titleString = I18n.format("handshakeApprove." + message + ".title"); - bodyLines = new ArrayList(); + bodyLines = new ArrayList<>(); int i = 0; boolean wasNull = true; - while (true) { + while(true) { String line = getI18nOrNull("handshakeApprove." + message + ".body." + (i++)); - if (line == null) { - if (wasNull) { + if(line == null) { + if(wasNull) { break; - } else { + }else { bodyLines.add(""); wasNull = true; } - } else { + }else { bodyLines.add(line); wasNull = false; } @@ -74,18 +66,18 @@ public class GuiHandshakeApprove extends GuiScreen { int totalHeight = 10 + 10 + bodyLines.size() * 10 + 10 + 20; bodyY = (height - totalHeight) / 2 - 15; int buttonY = bodyY + totalHeight - 20; - if (yes != null) { + if(yes != null) { this.buttonList.add(new GuiButton(0, width / 2 + 3, buttonY, 100, 20, I18n.format("gui.no"))); this.buttonList.add(new GuiButton(1, width / 2 - 103, buttonY, 100, 20, I18n.format("gui.yes"))); - } else { + }else { this.buttonList.add(new GuiButton(0, width / 2 - 100, buttonY, 200, 20, I18n.format("gui.back"))); } } protected void actionPerformed(GuiButton parGuiButton) { - if (parGuiButton.id == 0) { + if(parGuiButton.id == 0) { mc.displayGuiScreen(no); - } else if (parGuiButton.id == 1) { + }else if(parGuiButton.id == 1) { mc.displayGuiScreen(yes); } } @@ -93,20 +85,20 @@ public class GuiHandshakeApprove extends GuiScreen { public void drawScreen(int xx, int yy, float partialTicks) { drawBackground(0); drawCenteredString(fontRendererObj, titleString, width / 2, bodyY, 16777215); - for (int i = 0, l = bodyLines.size(); i < l; ++i) { + for(int i = 0, l = bodyLines.size(); i < l; ++i) { String s = bodyLines.get(i); - if (s.length() > 0) { + if(s.length() > 0) { drawCenteredString(fontRendererObj, s, width / 2, bodyY + 20 + i * 10, 16777215); } } - super.drawScreen(xx, yy, partialTicks); + super.drawScreen(xx, yy, partialTicks); } private String getI18nOrNull(String key) { String ret = I18n.format(key); - if (key.equals(ret)) { + if(key.equals(ret)) { return null; - } else { + }else { return ret; } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/RateLimitTracker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/RateLimitTracker.java index a2c70f0..97786ca 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/RateLimitTracker.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/RateLimitTracker.java @@ -4,25 +4,19 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -31,12 +25,12 @@ public class RateLimitTracker { private static long lastTickUpdate = 0l; - private static final Map blocks = new HashMap(); - private static final Map lockout = new HashMap(); + private static final Map blocks = new HashMap<>(); + private static final Map lockout = new HashMap<>(); public static boolean isLockedOut(String addr) { Long lockoutStatus = lockout.get(addr); - return lockoutStatus != null && System.currentTimeMillis() - lockoutStatus.longValue() < 300000l; + return lockoutStatus != null && EagRuntime.steadyTimeMillis() - lockoutStatus.longValue() < 300000l; } public static boolean isProbablyLockedOut(String addr) { @@ -44,28 +38,28 @@ public class RateLimitTracker { } public static void registerBlock(String addr) { - blocks.put(addr, System.currentTimeMillis()); + blocks.put(addr, EagRuntime.steadyTimeMillis()); } public static void registerLockOut(String addr) { - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); blocks.put(addr, millis); lockout.put(addr, millis); } public static void tick() { - long millis = System.currentTimeMillis(); - if (millis - lastTickUpdate > 5000l) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastTickUpdate > 5000l) { lastTickUpdate = millis; Iterator blocksItr = blocks.values().iterator(); - while (blocksItr.hasNext()) { - if (millis - blocksItr.next().longValue() > 900000l) { + while(blocksItr.hasNext()) { + if(millis - blocksItr.next().longValue() > 900000l) { blocksItr.remove(); } } blocksItr = lockout.values().iterator(); - while (blocksItr.hasNext()) { - if (millis - blocksItr.next().longValue() > 900000l) { + while(blocksItr.hasNext()) { + if(millis - blocksItr.next().longValue() > 900000l) { blocksItr.remove(); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryDispatch.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryDispatch.java index b091c0c..bfff0c4 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryDispatch.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryDispatch.java @@ -1,29 +1,22 @@ package net.lax1dude.eaglercraft.v1_8.socket; import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -34,7 +27,8 @@ public class ServerQueryDispatch { public static IServerQuery sendServerQuery(String uri, String accept) { logger.info("Sending {} query to: \"{}\"", accept, uri); - return PlatformNetworking.sendServerQuery(uri, accept); + IWebSocketClient sockClient = PlatformNetworking.openWebSocket(uri); + return sockClient != null ? new ServerQueryImpl(sockClient, accept) : null; } } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMServerQuery.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java similarity index 51% rename from src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMServerQuery.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java index ef4e616..19c3715 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMServerQuery.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java @@ -1,171 +1,139 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm; +package net.lax1dude.eaglercraft.v1_8.socket; import java.util.LinkedList; import java.util.List; import org.json.JSONObject; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.events.MessageEvent; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.websocket.WebSocket; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit; import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; /** - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ -public class TeaVMServerQuery implements IServerQuery { +class ServerQueryImpl implements IServerQuery { public static final Logger logger = LogManager.getLogger("WebSocketQuery"); - private final List queryResponses = new LinkedList(); - private final List queryResponsesBytes = new LinkedList(); + private final List queryResponses = new LinkedList<>(); + private final List queryResponsesBytes = new LinkedList<>(); + protected final IWebSocketClient websocketClient; protected final String uri; protected final String accept; - protected final WebSocket sock; + protected boolean hasSentAccept = false; protected boolean open = true; protected boolean alive = false; protected long pingStart = -1l; protected long pingTimer = -1l; private EnumServerRateLimit rateLimit = EnumServerRateLimit.OK; - public TeaVMServerQuery(String uri, String accept) { - this.uri = uri; + ServerQueryImpl(IWebSocketClient websocketClient, String accept) { + this.websocketClient = websocketClient; + this.uri = websocketClient.getCurrentURI(); this.accept = accept; - this.sock = WebSocket.create(uri); - initHandlers(); } - @JSBody(params = { "obj" }, script = "return typeof obj === \"string\";") - private static native boolean isString(JSObject obj); - - protected void initHandlers() { - sock.setBinaryType("arraybuffer"); - TeaVMUtils.addEventListener(sock, "open", new EventListener() { - @Override - public void handleEvent(Event evt) { - sock.send("Accept: " + accept); - } - }); - TeaVMUtils.addEventListener(sock, "close", new EventListener() { - @Override - public void handleEvent(Event evt) { - open = false; - } - }); - TeaVMUtils.addEventListener(sock, "message", new EventListener() { - @Override - public void handleEvent(MessageEvent evt) { + @Override + public void update() { + if(!hasSentAccept && websocketClient.getState() == EnumEaglerConnectionState.CONNECTED) { + hasSentAccept = true; + websocketClient.send("Accept: " + accept); + } + List lst = websocketClient.getNextFrames(); + if(lst != null) { + for(int i = 0, l = lst.size(); i < l; ++i) { + IWebSocketFrame frame = lst.get(i); alive = true; - if (pingTimer == -1) { - pingTimer = System.currentTimeMillis() - pingStart; - if (pingTimer < 1) { + if(pingTimer == -1) { + pingTimer = PlatformRuntime.steadyTimeMillis() - pingStart; + if(pingTimer < 1) { pingTimer = 1; } } - if (isString(evt.getData())) { - String str = evt.getDataAsString(); - if (str.equalsIgnoreCase("BLOCKED")) { + if(frame.isString()) { + String str = frame.getString(); + if(str.equalsIgnoreCase("BLOCKED")) { logger.error("Reached full IP ratelimit for {}!", uri); rateLimit = EnumServerRateLimit.BLOCKED; return; } - if (str.equalsIgnoreCase("LOCKED")) { + if(str.equalsIgnoreCase("LOCKED")) { logger.error("Reached full IP ratelimit lockout for {}!", uri); rateLimit = EnumServerRateLimit.LOCKED_OUT; return; } try { JSONObject obj = new JSONObject(str); - if ("blocked".equalsIgnoreCase(obj.optString("type", null))) { + if("blocked".equalsIgnoreCase(obj.optString("type", null))) { logger.error("Reached query ratelimit for {}!", uri); rateLimit = EnumServerRateLimit.BLOCKED; - } else if ("locked".equalsIgnoreCase(obj.optString("type", null))) { + }else if("locked".equalsIgnoreCase(obj.optString("type", null))) { logger.error("Reached query ratelimit lockout for {}!", uri); rateLimit = EnumServerRateLimit.LOCKED_OUT; - } else { - QueryResponse response = new QueryResponse(obj, pingTimer); - synchronized (queryResponses) { - queryResponses.add(response); - } + }else { + queryResponses.add(new QueryResponse(obj, pingTimer)); } - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Exception thrown parsing websocket query response from \"" + uri + "\"!"); logger.error(t); } - } else { - synchronized (queryResponsesBytes) { - queryResponsesBytes.add(TeaVMUtils.wrapByteArrayBuffer(evt.getDataAsArray())); - } + }else { + queryResponsesBytes.add(frame.getByteArray()); } } - }); - TeaVMUtils.addEventListener(sock, "error", new EventListener() { - @Override - public void handleEvent(Event evt) { - sock.close(); - open = false; - } - }); + } + if(websocketClient.isClosed()) { + open = false; + } } @Override public void send(String str) { - if (open) { - sock.send(str); + if(websocketClient.getState() == EnumEaglerConnectionState.CONNECTED) { + websocketClient.send(str); } } - @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") - private static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); - @Override public void send(byte[] bytes) { - if (open) { - nativeBinarySend(sock, TeaVMUtils.unwrapArrayBuffer(bytes)); + if(websocketClient.getState() == EnumEaglerConnectionState.CONNECTED) { + websocketClient.send(bytes); } } @Override public int responsesAvailable() { - synchronized (queryResponses) { + synchronized(queryResponses) { return queryResponses.size(); } } @Override public QueryResponse getResponse() { - synchronized (queryResponses) { - if (queryResponses.size() > 0) { + synchronized(queryResponses) { + if(queryResponses.size() > 0) { return queryResponses.remove(0); - } else { + }else { return null; } } @@ -173,17 +141,17 @@ public class TeaVMServerQuery implements IServerQuery { @Override public int binaryResponsesAvailable() { - synchronized (queryResponsesBytes) { + synchronized(queryResponsesBytes) { return queryResponsesBytes.size(); } } @Override public byte[] getBinaryResponse() { - synchronized (queryResponsesBytes) { - if (queryResponsesBytes.size() > 0) { + synchronized(queryResponsesBytes) { + if(queryResponsesBytes.size() > 0) { return queryResponsesBytes.remove(0); - } else { + }else { return null; } } @@ -197,9 +165,9 @@ public class TeaVMServerQuery implements IServerQuery { @Override public void close() { - if (open) { + if(open) { open = false; - sock.close(); + websocketClient.close(); } } @@ -207,4 +175,5 @@ public class TeaVMServerQuery implements IServerQuery { public EnumServerRateLimit getRateLimit() { return rateLimit; } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java new file mode 100755 index 0000000..8458de6 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java @@ -0,0 +1,152 @@ +package net.lax1dude.eaglercraft.v1_8.socket; + +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.minecraft.network.EnumPacketDirection; +import net.minecraft.network.Packet; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class WebSocketNetworkManager extends EaglercraftNetworkManager { + + protected final IWebSocketClient webSocketClient; + + public WebSocketNetworkManager(IWebSocketClient webSocketClient) { + super(webSocketClient.getCurrentURI()); + this.webSocketClient = webSocketClient; + } + + public void connect() { + } + + public EnumEaglerConnectionState getConnectStatus() { + return webSocketClient.getState(); + } + + public void closeChannel(IChatComponent reason) { + webSocketClient.close(); + if(nethandler != null) { + nethandler.onDisconnect(reason); + } + clientDisconnected = true; + } + + public void processReceivedPackets() throws IOException { + if(nethandler == null) return; + if(webSocketClient.availableStringFrames() > 0) { + logger.warn("discarding {} string frames recieved on a binary connection", webSocketClient.availableStringFrames()); + webSocketClient.clearStringFrames(); + } + List pkts = webSocketClient.getNextBinaryFrames(); + + if(pkts == null) { + return; + } + + for(int i = 0, l = pkts.size(); i < l; ++i) { + IWebSocketFrame next = pkts.get(i); + ++debugPacketCounter; + try { + byte[] asByteArray = next.getByteArray(); + ByteBuf nettyBuffer = Unpooled.buffer(asByteArray, asByteArray.length); + nettyBuffer.writerIndex(asByteArray.length); + PacketBuffer input = new PacketBuffer(nettyBuffer); + int pktId = input.readVarIntFromBuffer(); + + Packet pkt; + try { + pkt = packetState.getPacket(EnumPacketDirection.CLIENTBOUND, pktId); + }catch(IllegalAccessException | InstantiationException ex) { + throw new IOException("Recieved a packet with type " + pktId + " which is invalid!"); + } + + if(pkt == null) { + throw new IOException("Recieved packet type " + pktId + " which is undefined in state " + packetState); + } + + try { + pkt.readPacketData(input); + }catch(Throwable t) { + throw new IOException("Failed to read packet type '" + pkt.getClass().getSimpleName() + "'", t); + } + + try { + pkt.processPacket(nethandler); + }catch(Throwable t) { + logger.error("Failed to process {}! It'll be skipped for debug purposes.", pkt.getClass().getSimpleName()); + logger.error(t); + } + + }catch(Throwable t) { + logger.error("Failed to process websocket frame {}! It'll be skipped for debug purposes.", debugPacketCounter); + logger.error(t); + } + } + } + + public void sendPacket(Packet pkt) { + if(!isChannelOpen()) { + logger.error("Packet was sent on a closed connection: {}", pkt.getClass().getSimpleName()); + return; + } + + int i; + try { + i = packetState.getPacketId(EnumPacketDirection.SERVERBOUND, pkt); + }catch(Throwable t) { + logger.error("Incorrect packet for state: {}", pkt.getClass().getSimpleName()); + return; + } + + temporaryBuffer.clear(); + temporaryBuffer.writeVarIntToBuffer(i); + try { + pkt.writePacketData(temporaryBuffer); + }catch(IOException ex) { + logger.error("Failed to write packet {}!", pkt.getClass().getSimpleName()); + return; + } + + int len = temporaryBuffer.writerIndex(); + byte[] bytes = new byte[len]; + temporaryBuffer.getBytes(0, bytes); + + webSocketClient.send(bytes); + } + + public boolean checkDisconnected() { + if(webSocketClient.isClosed()) { + try { + processReceivedPackets(); // catch kick message + } catch (IOException e) { + } + doClientDisconnect(new ChatComponentTranslation("disconnect.endOfStream")); + return true; + }else { + return false; + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java new file mode 100755 index 0000000..1db2153 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java @@ -0,0 +1,130 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import net.minecraft.client.Minecraft; +import net.minecraft.client.network.NetHandlerPlayClient; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClientV3MessageHandler implements GameMessageHandler { + + private final NetHandlerPlayClient netHandler; + + public ClientV3MessageHandler(NetHandlerPlayClient netHandler) { + this.netHandler = netHandler; + } + + public void handleServer(SPacketEnableFNAWSkinsEAG packet) { + netHandler.currentFNAWSkinAllowedState = packet.enableSkins; + netHandler.currentFNAWSkinForcedState = packet.enableSkins; + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(netHandler.currentFNAWSkinForcedState + || (netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins)); + } + + public void handleServer(SPacketOtherCapeCustomEAG packet) { + netHandler.getCapeCache().cacheCapeCustom(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + packet.customCape); + } + + public void handleServer(SPacketOtherCapePresetEAG packet) { + netHandler.getCapeCache().cacheCapePreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + packet.presetCape); + } + + public void handleServer(SPacketOtherSkinCustomV3EAG packet) { + EaglercraftUUID responseUUID = new EaglercraftUUID(packet.uuidMost, packet.uuidLeast); + SkinModel modelId; + if(packet.modelID == (byte)0xFF) { + modelId = this.netHandler.getSkinCache().getRequestedSkinType(responseUUID); + }else { + modelId = SkinModel.getModelFromId(packet.modelID & 0x7F); + if((packet.modelID & 0x80) != 0 && modelId.sanitize) { + modelId = SkinModel.STEVE; + } + } + if(modelId.highPoly != null) { + modelId = SkinModel.STEVE; + } + this.netHandler.getSkinCache().cacheSkinCustom(responseUUID, packet.customSkin, modelId); + } + + public void handleServer(SPacketOtherSkinPresetEAG packet) { + this.netHandler.getSkinCache().cacheSkinPreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + packet.presetSkin); + } + + public void handleServer(SPacketUpdateCertEAG packet) { + if (EagRuntime.getConfiguration().allowUpdateSvc()) { + UpdateService.addCertificateToSet(packet.updateCert); + } + } + + public void handleServer(SPacketVoiceSignalAllowedEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeAllowed(packet.allowed, packet.iceServers); + } + } + + public void handleServer(SPacketVoiceSignalConnectV3EAG packet) { + if (VoiceClientController.isClientSupported()) { + if (packet.isAnnounceType) { + VoiceClientController.handleVoiceSignalPacketTypeConnectAnnounce( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } else { + VoiceClientController.handleVoiceSignalPacketTypeConnect( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.offer); + } + } + } + + public void handleServer(SPacketVoiceSignalDescEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeDescription( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + new String(packet.desc, StandardCharsets.UTF_8)); + } + } + + public void handleServer(SPacketVoiceSignalDisconnectPeerEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeDisconnect( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } + } + + public void handleServer(SPacketVoiceSignalGlobalEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeGlobalNew(packet.users); + } + } + + public void handleServer(SPacketVoiceSignalICEEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeICECandidate( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + new String(packet.ice, StandardCharsets.UTF_8)); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java new file mode 100755 index 0000000..efee75d --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java @@ -0,0 +1,222 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import net.lax1dude.eaglercraft.v1_8.webview.ServerInfoCache; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; +import net.minecraft.client.Minecraft; +import net.minecraft.client.network.NetHandlerPlayClient; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClientV4MessageHandler implements GameMessageHandler { + + private final NetHandlerPlayClient netHandler; + + public ClientV4MessageHandler(NetHandlerPlayClient netHandler) { + this.netHandler = netHandler; + } + + public void handleServer(SPacketEnableFNAWSkinsEAG packet) { + netHandler.currentFNAWSkinAllowedState = packet.enableSkins; + netHandler.currentFNAWSkinForcedState = packet.force; + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(netHandler.currentFNAWSkinForcedState + || (netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins)); + } + + public void handleServer(SPacketOtherCapeCustomEAG packet) { + netHandler.getCapeCache().cacheCapeCustom(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + packet.customCape); + } + + public void handleServer(SPacketOtherCapePresetEAG packet) { + netHandler.getCapeCache().cacheCapePreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + packet.presetCape); + } + + public void handleServer(SPacketOtherSkinCustomV4EAG packet) { + EaglercraftUUID responseUUID = new EaglercraftUUID(packet.uuidMost, packet.uuidLeast); + SkinModel modelId; + if(packet.modelID == (byte)0xFF) { + modelId = this.netHandler.getSkinCache().getRequestedSkinType(responseUUID); + }else { + modelId = SkinModel.getModelFromId(packet.modelID & 0x7F); + if((packet.modelID & 0x80) != 0 && modelId.sanitize) { + modelId = SkinModel.STEVE; + } + } + if(modelId.highPoly != null) { + modelId = SkinModel.STEVE; + } + this.netHandler.getSkinCache().cacheSkinCustom(responseUUID, SkinPacketVersionCache.convertToV3Raw(packet.customSkin), modelId); + } + + public void handleServer(SPacketOtherSkinPresetEAG packet) { + this.netHandler.getSkinCache().cacheSkinPreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.presetSkin); + } + + public void handleServer(SPacketUpdateCertEAG packet) { + if (EagRuntime.getConfiguration().allowUpdateSvc()) { + UpdateService.addCertificateToSet(packet.updateCert); + } + } + + public void handleServer(SPacketVoiceSignalAllowedEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeAllowed(packet.allowed, packet.iceServers); + } + } + + public void handleServer(SPacketVoiceSignalConnectV4EAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeConnect(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.offer); + } + } + + public void handleServer(SPacketVoiceSignalConnectAnnounceV4EAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeConnectAnnounce(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } + } + + public void handleServer(SPacketVoiceSignalDescEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeDescription( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + new String(packet.desc, StandardCharsets.UTF_8)); + } + } + + public void handleServer(SPacketVoiceSignalDisconnectPeerEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeDisconnect(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } + } + + public void handleServer(SPacketVoiceSignalGlobalEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeGlobalNew(packet.users); + } + } + + public void handleServer(SPacketVoiceSignalICEEAG packet) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacketTypeICECandidate( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + new String(packet.ice, StandardCharsets.UTF_8)); + } + } + + public void handleServer(SPacketForceClientSkinPresetV4EAG packet) { + EaglerProfile.handleForceSkinPreset(packet.presetSkin); + } + + public void handleServer(SPacketForceClientSkinCustomV4EAG packet) { + EaglerProfile.handleForceSkinCustom(packet.modelID, SkinPacketVersionCache.convertToV3Raw(packet.customSkin)); + } + + public void handleServer(SPacketSetServerCookieV4EAG packet) { + if(!netHandler.isClientInEaglerSingleplayerOrLAN() && Minecraft.getMinecraft().getCurrentServerData().enableCookies) { + ServerCookieDataStore.saveCookie(netHandler.getNetworkManager().getAddress(), packet.expires, packet.data, + packet.revokeQuerySupported, packet.saveCookieToDisk); + } + } + + public void handleServer(SPacketRedirectClientV4EAG packet) { + Minecraft.getMinecraft().handleReconnectPacket(packet.redirectURI); + } + + public void handleServer(SPacketOtherPlayerClientUUIDV4EAG packet) { + ClientUUIDLoadingCache.handleResponse(packet.requestId, new EaglercraftUUID(packet.clientUUIDMost, packet.clientUUIDLeast)); + } + + public void handleServer(SPacketForceClientCapePresetV4EAG packet) { + EaglerProfile.handleForceCapePreset(packet.presetCape); + } + + public void handleServer(SPacketForceClientCapeCustomV4EAG packet) { + EaglerProfile.handleForceCapeCustom(packet.customCape); + } + + public void handleServer(SPacketInvalidatePlayerCacheV4EAG packet) { + if(packet.players != null && packet.players.size() > 0) { + for(SPacketInvalidatePlayerCacheV4EAG.InvalidateRequest req : packet.players) { + EaglercraftUUID uuid = new EaglercraftUUID(req.uuidMost, req.uuidLeast); + if(req.invalidateSkin) { + this.netHandler.getSkinCache().handleInvalidate(uuid); + } + if(req.invalidateCape) { + this.netHandler.getCapeCache().handleInvalidate(uuid); + } + } + } + } + + public void handleServer(SPacketUnforceClientV4EAG packet) { + if(packet.resetSkin) { + EaglerProfile.isServerSkinOverride = false; + } + if(packet.resetCape) { + EaglerProfile.isServerCapeOverride = false; + } + if(packet.resetFNAW) { + netHandler.currentFNAWSkinForcedState = false; + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins( + netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins); + } + } + + public void handleServer(SPacketCustomizePauseMenuV4EAG packet) { + PauseMenuCustomizeState.loadPacket(packet); + } + + public void handleServer(SPacketServerInfoDataChunkV4EAG packet) { + ServerInfoCache.handleChunk(packet); + } + + public void handleServer(SPacketWebViewMessageV4EAG packet) { + WebViewOverlayController.handleMessagePacket(packet); + } + + public void handleServer(SPacketNotifIconsRegisterV4EAG packet) { + netHandler.getNotifManager().processPacketAddIcons(packet); + } + + public void handleServer(SPacketNotifIconsReleaseV4EAG packet) { + netHandler.getNotifManager().processPacketRemIcons(packet); + } + + public void handleServer(SPacketNotifBadgeShowV4EAG packet) { + netHandler.getNotifManager().processPacketShowBadge(packet); + } + + public void handleServer(SPacketNotifBadgeHideV4EAG packet) { + netHandler.getNotifManager().processPacketHideBadge(packet); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java new file mode 100755 index 0000000..11d2ff9 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java @@ -0,0 +1,199 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerV3MessageHandler; +import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerV4MessageHandler; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.PacketBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GameProtocolMessageController { + + private static final Logger logger = LogManager.getLogger("GameProtocolMessageController"); + + public final GamePluginMessageProtocol protocol; + public final int sendDirection; + public final int receiveDirection; + private final PacketBufferInputWrapper inputStream = new PacketBufferInputWrapper(null); + private final PacketBufferOutputWrapper outputStream = new PacketBufferOutputWrapper(null); + private final GameMessageHandler handler; + private final IPluginMessageSendFunction sendFunction; + private final List sendQueueV4; + private final boolean noDelay; + + public GameProtocolMessageController(GamePluginMessageProtocol protocol, int sendDirection, GameMessageHandler handler, + IPluginMessageSendFunction sendCallback) { + this.protocol = protocol; + this.sendDirection = sendDirection; + this.receiveDirection = GamePluginMessageConstants.oppositeDirection(sendDirection); + this.handler = handler; + this.sendFunction = sendCallback; + this.noDelay = protocol.ver < 4 || EagRuntime.getConfiguration().isEaglerNoDelay(); + this.sendQueueV4 = !noDelay ? new LinkedList<>() : null; + } + + public boolean handlePacket(String channel, PacketBuffer data) throws IOException { + GameMessagePacket pkt; + if(protocol.ver >= 4 && data.readableBytes() > 0 && data.getByte(data.readerIndex()) == (byte) 0xFF + && channel.equals(GamePluginMessageConstants.V4_CHANNEL)) { + data.readByte(); + inputStream.buffer = data; + int count = inputStream.readVarInt(); + for(int i = 0, j, k; i < count; ++i) { + j = data.readVarIntFromBuffer(); + k = data.readerIndex() + j; + if(j > data.readableBytes()) { + throw new IOException("Packet fragment is too long: " + j + " > " + data.readableBytes()); + } + pkt = protocol.readPacket(channel, receiveDirection, inputStream); + if(pkt != null) { + try { + pkt.handlePacket(handler); + }catch(Throwable t) { + logger.error("Failed to handle packet {} in direction {} using handler {}!", pkt.getClass().getSimpleName(), + GamePluginMessageConstants.getDirectionString(receiveDirection), handler); + logger.error(t); + } + }else { + logger.warn("Could not read packet fragment {} of {}, unknown packet", count, i); + } + if(data.readerIndex() != k) { + logger.warn("Packet fragment {} was the wrong length: {} != {}", + (pkt != null ? pkt.getClass().getSimpleName() : "unknown"), j + data.readerIndex() - k, j); + data.readerIndex(k); + } + } + if(data.readableBytes() > 0) { + logger.warn("Leftover data after reading multi-packet! ({} bytes)", data.readableBytes()); + } + inputStream.buffer = null; + return true; + } + inputStream.buffer = data; + pkt = protocol.readPacket(channel, receiveDirection, inputStream); + if(pkt != null && inputStream.available() > 0) { + logger.warn("Leftover data after reading packet {}! ({} bytes)", pkt.getClass().getSimpleName(), inputStream.available()); + } + inputStream.buffer = null; + if(pkt != null) { + try { + pkt.handlePacket(handler); + }catch(Throwable t) { + logger.error("Failed to handle packet {} in direction {} using handler {}!", pkt.getClass().getSimpleName(), + GamePluginMessageConstants.getDirectionString(receiveDirection), handler); + logger.error(t); + } + return true; + }else { + return false; + } + } + + public void sendPacket(GameMessagePacket packet) throws IOException { + int len = packet.length() + 1; + PacketBuffer buf = new PacketBuffer(len != 0 ? Unpooled.buffer(len) : Unpooled.buffer(64)); + outputStream.buffer = buf; + String chan = protocol.writePacket(sendDirection, outputStream, packet); + outputStream.buffer = null; + int j = buf.writerIndex(); + if(len != 0 && j != len && j + 1 != len) { + logger.warn("Packet {} was expected to be {} bytes but was serialized to {} bytes!", + packet.getClass().getSimpleName(), len, j); + } + if(sendQueueV4 != null && chan.equals(GamePluginMessageConstants.V4_CHANNEL)) { + sendQueueV4.add(buf); + }else { + sendFunction.sendPluginMessage(chan, buf); + } + } + + public void flush() { + if(sendQueueV4 != null) { + int queueLen = sendQueueV4.size(); + PacketBuffer pkt; + if(queueLen == 0) { + return; + }else if(queueLen == 1) { + pkt = sendQueueV4.remove(0); + sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, pkt); + }else { + int i, j, sendCount = 0, totalLen = 0; + PacketBuffer sendBuffer; + while(sendQueueV4.size() > 0) { + do { + i = sendQueueV4.get(sendCount++).readableBytes(); + totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i; + }while(totalLen < 32760 && sendCount < sendQueueV4.size()); + if(totalLen >= 32760) { + --sendCount; + } + if(sendCount <= 1) { + pkt = sendQueueV4.remove(0); + sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, pkt); + continue; + } + sendBuffer = new PacketBuffer(Unpooled.buffer(1 + totalLen + GamePacketOutputBuffer.getVarIntSize(sendCount))); + sendBuffer.writeByte(0xFF); + sendBuffer.writeVarIntToBuffer(sendCount); + for(j = 0; j < sendCount; ++j) { + pkt = sendQueueV4.remove(0); + sendBuffer.writeVarIntToBuffer(pkt.readableBytes()); + sendBuffer.writeBytes(pkt); + } + sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, sendBuffer); + } + } + } + } + + public static GameMessageHandler createClientHandler(int protocolVersion, NetHandlerPlayClient netHandler) { + switch(protocolVersion) { + case 2: + case 3: + return new ClientV3MessageHandler(netHandler); + case 4: + return new ClientV4MessageHandler(netHandler); + default: + throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion); + } + } + + public static GameMessageHandler createServerHandler(int protocolVersion, NetHandlerPlayServer netHandler) { + switch(protocolVersion) { + case 2: + case 3: + return new ServerV3MessageHandler(netHandler); + case 4: + return new ServerV4MessageHandler(netHandler); + default: + throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion); + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/IPluginMessageSendFunction.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/IPluginMessageSendFunction.java new file mode 100755 index 0000000..1065cf9 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/IPluginMessageSendFunction.java @@ -0,0 +1,24 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import net.minecraft.network.PacketBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IPluginMessageSendFunction { + + void sendPluginMessage(String channel, PacketBuffer contents); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferInputWrapper.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferInputWrapper.java new file mode 100755 index 0000000..b8dda71 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferInputWrapper.java @@ -0,0 +1,303 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import net.lax1dude.eaglercraft.v1_8.DecoderException; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.minecraft.network.PacketBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PacketBufferInputWrapper implements GamePacketInputBuffer { + + protected PacketBuffer buffer; + + public PacketBufferInputWrapper(PacketBuffer buffer) { + this.buffer = buffer; + } + + public PacketBuffer getBuffer() { + return buffer; + } + + public void setBuffer(PacketBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void readFully(byte[] b) throws IOException { + try { + buffer.readBytes(b); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + try { + buffer.readBytes(b, off, len); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public int skipBytes(int n) throws IOException { + int r = buffer.readableBytes(); + if(n > r) { + n = r; + } + buffer.readerIndex(buffer.readerIndex() + n); + return n; + } + + @Override + public boolean readBoolean() throws IOException { + try { + return buffer.readBoolean(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public byte readByte() throws IOException { + try { + return buffer.readByte(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public int readUnsignedByte() throws IOException { + try { + return buffer.readUnsignedByte(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public short readShort() throws IOException { + try { + return buffer.readShort(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public int readUnsignedShort() throws IOException { + try { + return buffer.readUnsignedShort(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public char readChar() throws IOException { + try { + return buffer.readChar(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public int readInt() throws IOException { + try { + return buffer.readInt(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public long readLong() throws IOException { + try { + return buffer.readLong(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public float readFloat() throws IOException { + try { + return buffer.readFloat(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public double readDouble() throws IOException { + try { + return buffer.readDouble(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public String readLine() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public String readUTF() throws IOException { + return DataInputStream.readUTF(this); + } + + @Override + public void skipAllBytes(int n) throws IOException { + if(buffer.readableBytes() < n) { + throw new EOFException(); + } + buffer.readerIndex(buffer.readerIndex() + n); + } + + @Override + public int readVarInt() throws IOException { + try { + return buffer.readVarIntFromBuffer(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public long readVarLong() throws IOException { + try { + return buffer.readVarLong(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public String readStringMC(int maxLen) throws IOException { + try { + return buffer.readStringFromBuffer(maxLen); + }catch(DecoderException ex) { + throw new IOException(ex.getMessage()); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public String readStringEaglerASCII8() throws IOException { + int len = readUnsignedByte(); + char[] ret = new char[len]; + for(int i = 0; i < len; ++i) { + ret[i] = (char)readByte(); + } + return new String(ret); + } + + @Override + public String readStringEaglerASCII16() throws IOException { + int len = readUnsignedShort(); + char[] ret = new char[len]; + for(int i = 0; i < len; ++i) { + ret[i] = (char)readByte(); + } + return new String(ret); + } + + @Override + public byte[] readByteArrayMC() throws IOException { + try { + return buffer.readByteArray(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + @Override + public int available() throws IOException { + return buffer.readableBytes(); + } + + @Override + public InputStream stream() { + return new InputStream() { + + @Override + public int read() throws IOException { + if(buffer.readableBytes() > 0) { + return buffer.readUnsignedShort(); + }else { + return -1; + } + } + + @Override + public int read(byte b[], int off, int len) throws IOException { + int avail = buffer.readableBytes(); + if(avail == 0) return -1; + len = avail > len ? avail : len; + buffer.readBytes(b, off, len); + return len; + } + + @Override + public long skip(long n) throws IOException { + return PacketBufferInputWrapper.this.skipBytes((int)n); + } + + @Override + public int available() throws IOException { + return buffer.readableBytes(); + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void mark(int readlimit) { + buffer.markReaderIndex(); + } + + @Override + public synchronized void reset() throws IOException { + try { + buffer.resetReaderIndex(); + }catch(IndexOutOfBoundsException ex) { + throw new EOFException(); + } + } + + }; + } + + @Override + public byte[] toByteArray() throws IOException { + byte[] ret = new byte[buffer.readableBytes()]; + buffer.readBytes(ret); + return ret; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferOutputWrapper.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferOutputWrapper.java new file mode 100755 index 0000000..c228f7f --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferOutputWrapper.java @@ -0,0 +1,316 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import java.io.IOException; +import java.io.OutputStream; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.minecraft.network.PacketBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PacketBufferOutputWrapper implements GamePacketOutputBuffer { + + protected PacketBuffer buffer; + + public PacketBufferOutputWrapper(PacketBuffer buffer) { + this.buffer = buffer; + } + + public PacketBuffer getBuffer() { + return buffer; + } + + public void setBuffer(PacketBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void write(int b) throws IOException { + try { + buffer.writeByte(b); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void write(byte[] b) throws IOException { + try { + buffer.writeBytes(b); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + try { + buffer.writeBytes(b, off, len); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeBoolean(boolean v) throws IOException { + try { + buffer.writeBoolean(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeByte(int v) throws IOException { + try { + buffer.writeByte(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeShort(int v) throws IOException { + try { + buffer.writeShort(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeChar(int v) throws IOException { + try { + buffer.writeChar(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeInt(int v) throws IOException { + try { + buffer.writeInt(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeLong(long v) throws IOException { + try { + buffer.writeLong(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeFloat(float v) throws IOException { + try { + buffer.writeFloat(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeDouble(double v) throws IOException { + try { + buffer.writeDouble(v); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeBytes(String s) throws IOException { + try { + int l = s.length(); + for(int i = 0; i < l; ++i) { + buffer.writeByte((int)s.charAt(i)); + } + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeChars(String s) throws IOException { + try { + int l = s.length(); + for(int i = 0; i < l; ++i) { + buffer.writeChar(s.charAt(i)); + } + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public final void writeUTF(String str) throws IOException { + long utfCount = countUTFBytes(str); + if (utfCount > 65535) { + throw new IOException("String is longer than 65535 bytes when encoded as UTF8!"); + } + byte[] arr = new byte[(int) utfCount + 2]; + int offset = 2; + arr[0] = (byte)(((int)utfCount >>> 8) & 0xFF); + arr[1] = (byte)((int)utfCount & 0xFF); + offset = writeUTFBytesToBuffer(str, arr, offset); + try { + buffer.writeBytes(arr, 0, offset); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + private static long countUTFBytes(String str) { + int utfCount = 0; + int length = str.length(); + for (int i = 0; i < length; i++) { + int charValue = str.charAt(i); + if (charValue > 0 && charValue <= 127) { + utfCount++; + } else if (charValue <= 2047) { + utfCount += 2; + } else { + utfCount += 3; + } + } + return utfCount; + } + + private static int writeUTFBytesToBuffer(String str, byte[] buffer, int offset) throws IOException { + int length = str.length(); + for (int i = 0; i < length; i++) { + int charValue = str.charAt(i); + if (charValue > 0 && charValue <= 127) { + buffer[offset++] = (byte) charValue; + } else if (charValue <= 2047) { + buffer[offset++] = (byte) (0xc0 | (0x1f & (charValue >> 6))); + buffer[offset++] = (byte) (0x80 | (0x3f & charValue)); + } else { + buffer[offset++] = (byte) (0xe0 | (0x0f & (charValue >> 12))); + buffer[offset++] = (byte) (0x80 | (0x3f & (charValue >> 6))); + buffer[offset++] = (byte) (0x80 | (0x3f & charValue)); + } + } + return offset; + } + + @Override + public void writeVarInt(int i) throws IOException { + try { + buffer.writeVarIntToBuffer(i); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeVarLong(long i) throws IOException { + try { + buffer.writeVarLong(i); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeStringMC(String str) throws IOException { + try { + buffer.writeString(str); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeStringEaglerASCII8(String str) throws IOException { + int len = str.length(); + if(len > 255) { + throw new IOException("String is longer than 255 chars! (" + len + ")"); + } + try { + buffer.writeByte(len); + for(int i = 0, j; i < len; ++i) { + j = (int)str.charAt(i); + if(j > 255) { + j = (int)'?'; + } + buffer.writeByte(j); + } + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeStringEaglerASCII16(String str) throws IOException { + int len = str.length(); + if(len > 65535) { + throw new IOException("String is longer than 65535 chars! (" + len + ")"); + } + try { + buffer.writeShort(len); + for(int i = 0, j; i < len; ++i) { + j = (int)str.charAt(i); + if(j > 255) { + j = (int)'?'; + } + buffer.writeByte(j); + } + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void writeByteArrayMC(byte[] bytes) throws IOException { + try { + buffer.writeByteArray(bytes); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public OutputStream stream() { + return new OutputStream() { + + @Override + public void write(int b) throws IOException { + try { + buffer.writeByte(b); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + try { + buffer.writeBytes(b, off, len); + }catch(IndexOutOfBoundsException ex) { + throw new IOException("Packet buffer overflowed!"); + } + } + + }; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java index 8a7abb8..ef490a2 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java @@ -1,620 +1,632 @@ -package net.lax1dude.eaglercraft.v1_8.sp; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; - -import org.apache.commons.lang3.StringUtils; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; -import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; -import net.lax1dude.eaglercraft.v1_8.sp.ipc.*; -import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; -import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; -import net.minecraft.client.Minecraft; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.IProgressUpdate; -import net.minecraft.util.StringTranslate; -import net.minecraft.world.WorldSettings; -import net.minecraft.world.storage.ISaveFormat; -import net.minecraft.world.storage.ISaveHandler; -import net.minecraft.world.storage.SaveFormatComparator; -import net.minecraft.world.storage.WorldInfo; - -/** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class SingleplayerServerController implements ISaveFormat { - - public static final String IPC_CHANNEL = "~!IPC"; - public static final String PLAYER_CHANNEL = "~!LOCAL_PLAYER"; - - private static int statusState = IntegratedServerState.WORLD_WORKER_NOT_RUNNING; - private static boolean loggingState = true; - private static String worldStatusString = ""; - private static float worldStatusProgress = 0.0f; - private static final LinkedList exceptions = new LinkedList(); - - public static final SingleplayerServerController instance = new SingleplayerServerController(); - public static final Logger logger = LogManager.getLogger("SingleplayerServerController"); - public static final List saveListCache = new ArrayList(); - public static final Map saveListMap = new HashMap(); - public static final List saveListNBT = new ArrayList(); - - private static boolean isPaused = false; - private static List integratedServerTPS = new ArrayList(); - private static long integratedServerLastTPSUpdate = 0; - public static final ClientIntegratedServerNetworkManager localPlayerNetworkManager = new ClientIntegratedServerNetworkManager( - PLAYER_CHANNEL); - private static final List openLANChannels = new ArrayList(); - - private static final IPCPacketManager packetManagerInstance = new IPCPacketManager(); - - private SingleplayerServerController() { - } - - public static void startIntegratedServerWorker() { - if (statusState == IntegratedServerState.WORLD_WORKER_NOT_RUNNING) { - exceptions.clear(); - statusState = IntegratedServerState.WORLD_WORKER_BOOTING; - loggingState = true; - ClientPlatformSingleplayer.startIntegratedServer(); - } - } - - public static boolean isIntegratedServerWorkerStarted() { - return statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING - && statusState != IntegratedServerState.WORLD_WORKER_BOOTING; - } - - public static boolean isIntegratedServerWorkerAlive() { - return statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING; - } - - public static boolean isRunningSingleThreadMode() { - return ClientPlatformSingleplayer.isRunningSingleThreadMode(); - } - - public static boolean isReady() { - return statusState == IntegratedServerState.WORLD_NONE; - } - - public static boolean isWorldNotLoaded() { - return statusState == IntegratedServerState.WORLD_NONE - || statusState == IntegratedServerState.WORLD_WORKER_NOT_RUNNING || - statusState == IntegratedServerState.WORLD_WORKER_BOOTING; - } - - public static boolean isWorldRunning() { - return statusState == IntegratedServerState.WORLD_LOADED || statusState == IntegratedServerState.WORLD_PAUSED || - statusState == IntegratedServerState.WORLD_LOADING || statusState == IntegratedServerState.WORLD_SAVING; - } - - public static boolean isWorldReady() { - return statusState == IntegratedServerState.WORLD_LOADED || statusState == IntegratedServerState.WORLD_PAUSED || - statusState == IntegratedServerState.WORLD_SAVING; - } - - public static int getStatusState() { - return statusState; - } - - public static boolean isChannelOpen(String ch) { - return openLANChannels.contains(ch); - } - - public static boolean isChannelNameAllowed(String ch) { - return !IPC_CHANNEL.equals(ch) && !PLAYER_CHANNEL.equals(ch); - } - - public static void openPlayerChannel(String ch) { - if (openLANChannels.contains(ch)) { - logger.error("Tried to open channel that already exists: \"{}\"", ch); - } else if (!isChannelNameAllowed(ch)) { - logger.error("Tried to open disallowed channel name: \"{}\"", ch); - } else { - openLANChannels.add(ch); - sendIPCPacket(new IPCPacket0CPlayerChannel(ch, true)); - PlatformWebRTC.serverLANCreatePeer(ch); - } - } - - public static void closePlayerChannel(String ch) { - if (!openLANChannels.remove(ch)) { - logger.error("Tried to close channel that doesn't exist: \"{}\"", ch); - } else { - sendIPCPacket(new IPCPacket0CPlayerChannel(ch, false)); - PlatformWebRTC.serverLANDisconnectPeer(ch); - } - } - - public static void openLocalPlayerChannel() { - localPlayerNetworkManager.isPlayerChannelOpen = true; - sendIPCPacket(new IPCPacket0CPlayerChannel(PLAYER_CHANNEL, true)); - } - - public static void closeLocalPlayerChannel() { - localPlayerNetworkManager.isPlayerChannelOpen = false; - sendIPCPacket(new IPCPacket0CPlayerChannel(PLAYER_CHANNEL, false)); - } - - private static void ensureReady() { - if (!isReady()) { - String msg = "Server is in state " + statusState + " '" + IntegratedServerState.getStateName(statusState) - + "' which is not the 'WORLD_NONE' state for the requested IPC operation"; - throw new IllegalStateException(msg); - } - } - - private static void ensureWorldReady() { - if (!isWorldReady()) { - String msg = "Server is in state " + statusState + " '" + IntegratedServerState.getStateName(statusState) - + "' which is not the 'WORLD_LOADED' state for the requested IPC operation"; - throw new IllegalStateException(msg); - } - } - - public static void launchEaglercraftServer(String folderName, int difficulty, int viewDistance, - WorldSettings settings) { - ensureReady(); - clearTPS(); - if (settings != null) { - sendIPCPacket(new IPCPacket02InitWorld(folderName, settings.getGameType().getID(), - settings.getTerrainType().getWorldTypeID(), settings.getWorldName(), settings.getSeed(), - settings.areCommandsAllowed(), settings.isMapFeaturesEnabled(), settings.isBonusChestEnabled(), - settings.getHardcoreEnabled())); - } - statusState = IntegratedServerState.WORLD_LOADING; - worldStatusProgress = 0.0f; - sendIPCPacket(new IPCPacket00StartServer(folderName, EaglerProfile.getName(), difficulty, viewDistance, - EagRuntime.getConfiguration().isDemo())); - } - - public static void clearTPS() { - integratedServerTPS.clear(); - integratedServerLastTPSUpdate = 0l; - } - - public static List getTPS() { - return integratedServerTPS; - } - - public static long getTPSAge() { - return System.currentTimeMillis() - integratedServerLastTPSUpdate; - } - - public static boolean hangupEaglercraftServer() { - LANServerController.closeLAN(); - if (isWorldRunning()) { - logger.error("Shutting down integrated server due to unexpected client hangup, this is a memleak"); - statusState = IntegratedServerState.WORLD_UNLOADING; - sendIPCPacket(new IPCPacket01StopServer()); - return true; - } else { - return false; - } - } - - public static boolean shutdownEaglercraftServer() { - LANServerController.closeLAN(); - if (isWorldRunning()) { - logger.info("Shutting down integrated server"); - statusState = IntegratedServerState.WORLD_UNLOADING; - sendIPCPacket(new IPCPacket01StopServer()); - return true; - } else { - return false; - } - } - - public static void autoSave() { - if (!isPaused) { - statusState = IntegratedServerState.WORLD_SAVING; - sendIPCPacket(new IPCPacket19Autosave()); - } - } - - public static void setPaused(boolean pause) { - if (statusState != IntegratedServerState.WORLD_LOADED && statusState != IntegratedServerState.WORLD_PAUSED - && statusState != IntegratedServerState.WORLD_SAVING) { - return; - } - if (isPaused != pause) { - sendIPCPacket(new IPCPacket0BPause(pause)); - isPaused = pause; - } - } - - public static void runTick() { - List pktList = ClientPlatformSingleplayer.recieveAllPacket(); - if (pktList != null) { - IPCPacketData packetData; - for (int i = 0, l = pktList.size(); i < l; ++i) { - packetData = pktList.get(i); - if (packetData.channel.equals(SingleplayerServerController.IPC_CHANNEL)) { - IPCPacketBase ipc; - try { - ipc = packetManagerInstance.IPCDeserialize(packetData.contents); - } catch (IOException ex) { - throw new RuntimeException("Failed to deserialize IPC packet", ex); - } - handleIPCPacket(ipc); - } else if (packetData.channel.equals(SingleplayerServerController.PLAYER_CHANNEL)) { - if (localPlayerNetworkManager.getConnectStatus() != EnumEaglerConnectionState.CLOSED) { - localPlayerNetworkManager.addRecievedPacket(packetData.contents); - } else { - logger.warn("Recieved {} byte packet on closed local player connection", - packetData.contents.length); - } - } else { - PlatformWebRTC.serverLANWritePacket(packetData.channel, packetData.contents); - } - } - } - - boolean logWindowState = PlatformApplication.isShowingDebugConsole(); - if (loggingState != logWindowState) { - loggingState = logWindowState; - sendIPCPacket(new IPCPacket21EnableLogging(logWindowState)); - } - - LANServerController.updateLANServer(); - } - - private static void handleIPCPacket(IPCPacketBase ipc) { - switch (ipc.id()) { - case IPCPacketFFProcessKeepAlive.ID: { - IPCPacketFFProcessKeepAlive pkt = (IPCPacketFFProcessKeepAlive) ipc; - IntegratedServerState.assertState(pkt.ack, statusState); - switch (pkt.ack) { - case 0xFF: - logger.info("Integrated server signaled a successful boot"); - sendIPCPacket(new IPCPacket14StringList(IPCPacket14StringList.LOCALE, StringTranslate.dump())); - statusState = IntegratedServerState.WORLD_NONE; - break; - case IPCPacket00StartServer.ID: - statusState = IntegratedServerState.WORLD_LOADED; - isPaused = false; - break; - case IPCPacket0BPause.ID: - case IPCPacket19Autosave.ID: - if (statusState != IntegratedServerState.WORLD_UNLOADING) { - statusState = isPaused ? IntegratedServerState.WORLD_PAUSED - : IntegratedServerState.WORLD_LOADED; - } - break; - case IPCPacketFFProcessKeepAlive.FAILURE: - logger.error("Server signaled 'FAILURE' response in state '{}'", - IntegratedServerState.getStateName(statusState)); - statusState = IntegratedServerState.WORLD_NONE; - callFailed = true; - break; - case IPCPacket01StopServer.ID: - LANServerController.closeLAN(); - localPlayerNetworkManager.isPlayerChannelOpen = false; - statusState = IntegratedServerState.WORLD_NONE; - break; - case IPCPacket06RenameWorldNBT.ID: - statusState = IntegratedServerState.WORLD_NONE; - break; - case IPCPacket03DeleteWorld.ID: - case IPCPacket07ImportWorld.ID: - case IPCPacket12FileWrite.ID: - case IPCPacket13FileCopyMove.ID: - case IPCPacket18ClearPlayers.ID: - statusState = IntegratedServerState.WORLD_NONE; - break; - case IPCPacketFFProcessKeepAlive.EXITED: - logger.error("Server signaled 'EXITED' response in state '{}'", - IntegratedServerState.getStateName(statusState)); - if (ClientPlatformSingleplayer.canKillWorker()) { - ClientPlatformSingleplayer.killWorker(); - } - LANServerController.closeLAN(); - localPlayerNetworkManager.isPlayerChannelOpen = false; - statusState = IntegratedServerState.WORLD_WORKER_NOT_RUNNING; - callFailed = true; - break; - default: - logger.error("IPC acknowledge packet type 0x{} was not handled", Integer.toHexString(pkt.ack)); - break; - } - break; - } - case IPCPacket09RequestResponse.ID: { - IPCPacket09RequestResponse pkt = (IPCPacket09RequestResponse) ipc; - if (statusState == IntegratedServerState.WORLD_EXPORTING) { - statusState = IntegratedServerState.WORLD_NONE; - exportResponse = pkt.response; - } else { - logger.error( - "IPCPacket09RequestResponse was recieved but statusState was '{}' instead of 'WORLD_EXPORTING'", - IntegratedServerState.getStateName(statusState)); - } - break; - } - case IPCPacket0DProgressUpdate.ID: { - IPCPacket0DProgressUpdate pkt = (IPCPacket0DProgressUpdate) ipc; - worldStatusString = pkt.updateMessage; - worldStatusProgress = pkt.updateProgress; - break; - } - case IPCPacket15Crashed.ID: { - exceptions.add((IPCPacket15Crashed) ipc); - if (exceptions.size() > 64) { - exceptions.remove(0); - } - break; - } - case IPCPacket16NBTList.ID: { - IPCPacket16NBTList pkt = (IPCPacket16NBTList) ipc; - if (pkt.opCode == IPCPacket16NBTList.WORLD_LIST && statusState == IntegratedServerState.WORLD_LISTING) { - statusState = IntegratedServerState.WORLD_NONE; - saveListNBT.clear(); - saveListNBT.addAll(pkt.nbtTagList); - loadSaveComparators(); - } else { - logger.error("IPC packet type 0x{} class '{}' contained invalid opCode {} in state {} '{}'", - Integer.toHexString(ipc.id()), ipc.getClass().getSimpleName(), pkt.opCode, statusState, - IntegratedServerState.getStateName(statusState)); - } - break; - } - case IPCPacket0CPlayerChannel.ID: { - IPCPacket0CPlayerChannel pkt = (IPCPacket0CPlayerChannel) ipc; - if (!pkt.open) { - if (pkt.channel.equals(PLAYER_CHANNEL)) { - LANServerController.closeLAN(); - localPlayerNetworkManager.isPlayerChannelOpen = false; - logger.error("Local player channel was closed"); - } else { - PlatformWebRTC.serverLANDisconnectPeer(pkt.channel); - } - } - break; - } - case IPCPacket14StringList.ID: { - IPCPacket14StringList pkt = (IPCPacket14StringList) ipc; - if (pkt.opCode == IPCPacket14StringList.SERVER_TPS) { - integratedServerTPS.clear(); - integratedServerTPS.addAll(pkt.stringList); - integratedServerLastTPSUpdate = System.currentTimeMillis(); - } else { - logger.warn("Strange string list type {} recieved!", pkt.opCode); - } - break; - } - case IPCPacket20LoggerMessage.ID: { - IPCPacket20LoggerMessage pkt = (IPCPacket20LoggerMessage) ipc; - PlatformApplication.addLogMessage(pkt.logMessage, pkt.isError); - break; - } - default: - throw new RuntimeException("Unexpected IPC packet type recieved on client: " + ipc.id()); - } - } - - public static void sendIPCPacket(IPCPacketBase ipc) { - byte[] pkt; - try { - pkt = packetManagerInstance.IPCSerialize(ipc); - } catch (IOException ex) { - throw new RuntimeException("Failed to serialize IPC packet", ex); - } - ClientPlatformSingleplayer.sendPacket(new IPCPacketData(IPC_CHANNEL, pkt)); - } - - private static boolean callFailed = false; - - public static boolean didLastCallFail() { - boolean c = callFailed; - callFailed = false; - return c; - } - - public static void importWorld(String name, byte[] data, int format, byte gameRules) { - ensureReady(); - statusState = IntegratedServerState.WORLD_IMPORTING; - sendIPCPacket(new IPCPacket07ImportWorld(name, data, (byte) format, gameRules)); - } - - public static void exportWorld(String name, int format) { - ensureReady(); - statusState = IntegratedServerState.WORLD_EXPORTING; - if (format == IPCPacket05RequestData.REQUEST_LEVEL_EAG) { - name = name + (new String(new char[] { (char) 253, (char) 233, (char) 233 })) + EaglerProfile.getName(); - } - sendIPCPacket(new IPCPacket05RequestData(name, (byte) format)); - } - - private static byte[] exportResponse = null; - - public static byte[] getExportResponse() { - byte[] dat = exportResponse; - exportResponse = null; - return dat; - } - - public static String worldStatusString() { - return worldStatusString; - } - - public static float worldStatusProgress() { - return worldStatusProgress; - } - - public static IPCPacket15Crashed worldStatusError() { - return exceptions.size() > 0 ? exceptions.remove(0) : null; - } - - public static IPCPacket15Crashed[] worldStatusErrors() { - int l = exceptions.size(); - if (l == 0) { - return null; - } - IPCPacket15Crashed[] pkts = exceptions.toArray(new IPCPacket15Crashed[l]); - exceptions.clear(); - return pkts; - } - - public static void clearPlayerData(String worldName) { - ensureReady(); - statusState = IntegratedServerState.WORLD_CLEAR_PLAYERS; - sendIPCPacket(new IPCPacket18ClearPlayers(worldName)); - } - - private static void loadSaveComparators() { - saveListMap.clear(); - saveListCache.clear(); - for (int j = 0, l = saveListNBT.size(); j < l; ++j) { - NBTTagCompound nbt = saveListNBT.get(j); - String folderName = nbt.getString("folderNameEagler"); - if (!StringUtils.isEmpty(folderName)) { - WorldInfo worldinfo = new WorldInfo(nbt.getCompoundTag("Data")); - saveListMap.put(folderName, worldinfo); - String s1 = worldinfo.getWorldName(); - if (StringUtils.isEmpty(s1)) { - s1 = folderName; - } - - long i = 0L; - saveListCache.add(new SaveFormatComparator(folderName, s1, worldinfo.getLastTimePlayed(), i, - worldinfo.getGameType(), false, worldinfo.isHardcoreModeEnabled(), - worldinfo.areCommandsAllowed(), nbt)); - } - } - } - - @Override - public String getName() { - return "eaglercraft"; - } - - @Override - public ISaveHandler getSaveLoader(String var1, boolean var2) { - return new SingleplayerSaveHandler(saveListMap.get(var1)); - } - - @Override - public List getSaveList() { - return saveListCache; - } - - @Override - public void flushCache() { - sendIPCPacket(new IPCPacket0EListWorlds()); - statusState = IntegratedServerState.WORLD_LISTING; - } - - @Override - public WorldInfo getWorldInfo(String var1) { - return saveListMap.get(var1); - } - - @Override - public boolean func_154335_d(String var1) { - return false; - } - - @Override - public boolean deleteWorldDirectory(String var1) { - sendIPCPacket(new IPCPacket03DeleteWorld(var1)); - statusState = IntegratedServerState.WORLD_DELETING; - return false; - } - - @Override - public boolean renameWorld(String var1, String var2) { - sendIPCPacket(new IPCPacket06RenameWorldNBT(var1, var2, false)); - statusState = IntegratedServerState.WORLD_RENAMING; - return true; - } - - public static void duplicateWorld(String var1, String var2) { - sendIPCPacket(new IPCPacket06RenameWorldNBT(var1, var2, true)); - statusState = IntegratedServerState.WORLD_DUPLICATING; - } - - @Override - public boolean func_154334_a(String var1) { - return false; - } - - @Override - public boolean isOldMapFormat(String var1) { - return false; - } - - @Override - public boolean convertMapFormat(String var1, IProgressUpdate var2) { - return false; - } - - @Override - public boolean canLoadWorld(String var1) { - return saveListMap.containsKey(var1); - } - - public static boolean canKillWorker() { - return ClientPlatformSingleplayer.canKillWorker(); - } - - public static void killWorker() { - statusState = IntegratedServerState.WORLD_WORKER_NOT_RUNNING; - ClientPlatformSingleplayer.killWorker(); - LANServerController.closeLAN(); - } - - public static void updateLocale(List dump) { - if (statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING) { - sendIPCPacket(new IPCPacket14StringList(IPCPacket14StringList.LOCALE, dump)); - } - } - - public static void setDifficulty(int difficultyId) { - if (isWorldRunning()) { - sendIPCPacket(new IPCPacket0ASetWorldDifficulty((byte) difficultyId)); - } - } - - public static void configureLAN(net.minecraft.world.WorldSettings.GameType enumGameType, boolean allowCommands) { - sendIPCPacket(new IPCPacket17ConfigureLAN(enumGameType.getID(), allowCommands, - LANServerController.currentICEServers)); - } - - public static boolean isClientInEaglerSingleplayerOrLAN() { - Minecraft mc = Minecraft.getMinecraft(); - return mc != null && mc.thePlayer != null && mc.thePlayer.sendQueue.isClientInEaglerSingleplayerOrLAN(); - } -} +package net.lax1dude.eaglercraft.v1_8.sp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; + +import org.apache.commons.lang3.StringUtils; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.*; +import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; +import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.IProgressUpdate; +import net.minecraft.util.StringTranslate; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.storage.ISaveFormat; +import net.minecraft.world.storage.ISaveHandler; +import net.minecraft.world.storage.SaveFormatComparator; +import net.minecraft.world.storage.WorldInfo; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SingleplayerServerController implements ISaveFormat { + + public static final String IPC_CHANNEL = "~!IPC"; + public static final String PLAYER_CHANNEL = "~!LOCAL_PLAYER"; + + private static int statusState = IntegratedServerState.WORLD_WORKER_NOT_RUNNING; + private static boolean loggingState = true; + private static String worldStatusString = ""; + private static float worldStatusProgress = 0.0f; + private static final LinkedList exceptions = new LinkedList<>(); + private static final Set issuesDetected = new HashSet<>(); + + public static final SingleplayerServerController instance = new SingleplayerServerController(); + public static final Logger logger = LogManager.getLogger("SingleplayerServerController"); + public static final List saveListCache = new ArrayList<>(); + public static final Map saveListMap = new HashMap<>(); + public static final List saveListNBT = new ArrayList<>(); + + private static boolean isPaused = false; + private static List integratedServerTPS = new ArrayList<>(); + private static long integratedServerLastTPSUpdate = 0; + public static final ClientIntegratedServerNetworkManager localPlayerNetworkManager = new ClientIntegratedServerNetworkManager(PLAYER_CHANNEL); + private static final List openLANChannels = new ArrayList<>(); + + private static final IPCPacketManager packetManagerInstance = new IPCPacketManager(); + + private SingleplayerServerController() { + } + + public static void startIntegratedServerWorker(boolean forceSingleThread) { + if(statusState == IntegratedServerState.WORLD_WORKER_NOT_RUNNING) { + exceptions.clear(); + issuesDetected.clear(); + statusState = IntegratedServerState.WORLD_WORKER_BOOTING; + loggingState = true; + boolean singleThreadSupport = ClientPlatformSingleplayer.isSingleThreadModeSupported(); + if(!singleThreadSupport && forceSingleThread) { + throw new UnsupportedOperationException("Single thread mode is not supported!"); + } + if(forceSingleThread || !singleThreadSupport) { + ClientPlatformSingleplayer.startIntegratedServer(forceSingleThread); + }else { + try { + ClientPlatformSingleplayer.startIntegratedServer(forceSingleThread); + }catch(Throwable t) { + logger.error("Failed to start integrated server worker"); + logger.error(t); + logger.error("Attempting to use single thread mode"); + exceptions.clear(); + issuesDetected.clear(); + statusState = IntegratedServerState.WORLD_WORKER_BOOTING; + loggingState = true; + ClientPlatformSingleplayer.startIntegratedServer(true); + } + } + } + } + + public static boolean isIssueDetected(int issue) { + return issuesDetected.contains(issue); + } + + public static boolean isIntegratedServerWorkerStarted() { + return statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING && statusState != IntegratedServerState.WORLD_WORKER_BOOTING; + } + + public static boolean isIntegratedServerWorkerAlive() { + return statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING; + } + + public static boolean isRunningSingleThreadMode() { + return ClientPlatformSingleplayer.isRunningSingleThreadMode(); + } + + public static boolean isReady() { + return statusState == IntegratedServerState.WORLD_NONE; + } + + public static boolean isWorldNotLoaded() { + return statusState == IntegratedServerState.WORLD_NONE || statusState == IntegratedServerState.WORLD_WORKER_NOT_RUNNING || + statusState == IntegratedServerState.WORLD_WORKER_BOOTING; + } + + public static boolean isWorldRunning() { + return statusState == IntegratedServerState.WORLD_LOADED || statusState == IntegratedServerState.WORLD_PAUSED || + statusState == IntegratedServerState.WORLD_LOADING || statusState == IntegratedServerState.WORLD_SAVING; + } + + public static boolean isWorldReady() { + return statusState == IntegratedServerState.WORLD_LOADED || statusState == IntegratedServerState.WORLD_PAUSED || + statusState == IntegratedServerState.WORLD_SAVING; + } + + public static int getStatusState() { + return statusState; + } + + public static boolean isChannelOpen(String ch) { + return openLANChannels.contains(ch); + } + + public static boolean isChannelNameAllowed(String ch) { + return !IPC_CHANNEL.equals(ch) && !PLAYER_CHANNEL.equals(ch); + } + + public static void openPlayerChannel(String ch) { + if(openLANChannels.contains(ch)) { + logger.error("Tried to open channel that already exists: \"{}\"", ch); + } else if (!isChannelNameAllowed(ch)) { + logger.error("Tried to open disallowed channel name: \"{}\"", ch); + }else { + openLANChannels.add(ch); + sendIPCPacket(new IPCPacket0CPlayerChannel(ch, true)); + PlatformWebRTC.serverLANCreatePeer(ch); + } + } + + public static void closePlayerChannel(String ch) { + if(!openLANChannels.remove(ch)) { + logger.error("Tried to close channel that doesn't exist: \"{}\"", ch); + }else { + sendIPCPacket(new IPCPacket0CPlayerChannel(ch, false)); + PlatformWebRTC.serverLANDisconnectPeer(ch); + } + } + + public static void openLocalPlayerChannel() { + localPlayerNetworkManager.isPlayerChannelOpen = true; + sendIPCPacket(new IPCPacket0CPlayerChannel(PLAYER_CHANNEL, true)); + } + + public static void closeLocalPlayerChannel() { + localPlayerNetworkManager.isPlayerChannelOpen = false; + sendIPCPacket(new IPCPacket0CPlayerChannel(PLAYER_CHANNEL, false)); + } + + private static void ensureReady() { + if(!isReady()) { + String msg = "Server is in state " + statusState + " '" + IntegratedServerState.getStateName(statusState) + "' which is not the 'WORLD_NONE' state for the requested IPC operation"; + throw new IllegalStateException(msg); + } + } + + private static void ensureWorldReady() { + if(!isWorldReady()) { + String msg = "Server is in state " + statusState + " '" + IntegratedServerState.getStateName(statusState) + "' which is not the 'WORLD_LOADED' state for the requested IPC operation"; + throw new IllegalStateException(msg); + } + } + + public static void launchEaglercraftServer(String folderName, int difficulty, int viewDistance, WorldSettings settings) { + ensureReady(); + clearTPS(); + if(settings != null) { + sendIPCPacket(new IPCPacket02InitWorld(folderName, settings.getGameType().getID(), + settings.getTerrainType().getWorldTypeID(), settings.getWorldName(), settings.getSeed(), + settings.areCommandsAllowed(), settings.isMapFeaturesEnabled(), settings.isBonusChestEnabled(), + settings.getHardcoreEnabled())); + } + statusState = IntegratedServerState.WORLD_LOADING; + worldStatusProgress = 0.0f; + sendIPCPacket(new IPCPacket00StartServer(folderName, EaglerProfile.getName(), difficulty, viewDistance, EagRuntime.getConfiguration().isDemo())); + } + + public static void clearTPS() { + integratedServerTPS.clear(); + integratedServerLastTPSUpdate = 0l; + } + + public static List getTPS() { + return integratedServerTPS; + } + + public static long getTPSAge() { + return EagRuntime.steadyTimeMillis() - integratedServerLastTPSUpdate; + } + + public static boolean hangupEaglercraftServer() { + LANServerController.closeLAN(); + if(isWorldRunning()) { + logger.error("Shutting down integrated server due to unexpected client hangup, this is a memleak"); + statusState = IntegratedServerState.WORLD_UNLOADING; + sendIPCPacket(new IPCPacket01StopServer()); + return true; + }else { + return false; + } + } + + public static boolean shutdownEaglercraftServer() { + LANServerController.closeLAN(); + if(isWorldRunning()) { + logger.info("Shutting down integrated server"); + statusState = IntegratedServerState.WORLD_UNLOADING; + sendIPCPacket(new IPCPacket01StopServer()); + return true; + }else { + return false; + } + } + + public static void autoSave() { + if(!isPaused) { + statusState = IntegratedServerState.WORLD_SAVING; + sendIPCPacket(new IPCPacket19Autosave()); + } + } + + public static void setPaused(boolean pause) { + if(statusState != IntegratedServerState.WORLD_LOADED && statusState != IntegratedServerState.WORLD_PAUSED && statusState != IntegratedServerState.WORLD_SAVING) { + return; + } + if(isPaused != pause) { + sendIPCPacket(new IPCPacket0BPause(pause)); + isPaused = pause; + } + } + + public static void runTick() { + List pktList = ClientPlatformSingleplayer.recieveAllPacket(); + if(pktList != null) { + IPCPacketData packetData; + for(int i = 0, l = pktList.size(); i < l; ++i) { + packetData = pktList.get(i); + if(packetData.channel.equals(SingleplayerServerController.IPC_CHANNEL)) { + IPCPacketBase ipc; + try { + ipc = packetManagerInstance.IPCDeserialize(packetData.contents); + }catch(IOException ex) { + throw new RuntimeException("Failed to deserialize IPC packet", ex); + } + handleIPCPacket(ipc); + }else if(packetData.channel.equals(SingleplayerServerController.PLAYER_CHANNEL)) { + if(localPlayerNetworkManager.getConnectStatus() != EnumEaglerConnectionState.CLOSED) { + localPlayerNetworkManager.addRecievedPacket(packetData.contents); + }else { + logger.warn("Recieved {} byte packet on closed local player connection", packetData.contents.length); + } + }else { + PlatformWebRTC.serverLANWritePacket(packetData.channel, packetData.contents); + } + } + } + + boolean logWindowState = PlatformApplication.isShowingDebugConsole(); + if(loggingState != logWindowState) { + loggingState = logWindowState; + sendIPCPacket(new IPCPacket1BEnableLogging(logWindowState)); + } + + if(ClientPlatformSingleplayer.isRunningSingleThreadMode()) { + ClientPlatformSingleplayer.updateSingleThreadMode(); + } + + LANServerController.updateLANServer(); + } + + private static void handleIPCPacket(IPCPacketBase ipc) { + switch(ipc.id()) { + case IPCPacketFFProcessKeepAlive.ID: { + IPCPacketFFProcessKeepAlive pkt = (IPCPacketFFProcessKeepAlive)ipc; + IntegratedServerState.assertState(pkt.ack, statusState); + switch(pkt.ack) { + case 0xFF: + logger.info("Integrated server signaled a successful boot"); + sendIPCPacket(new IPCPacket14StringList(IPCPacket14StringList.LOCALE, StringTranslate.dump())); + statusState = IntegratedServerState.WORLD_NONE; + break; + case IPCPacket00StartServer.ID: + statusState = IntegratedServerState.WORLD_LOADED; + isPaused = false; + break; + case IPCPacket0BPause.ID: + case IPCPacket19Autosave.ID: + if(statusState != IntegratedServerState.WORLD_UNLOADING) { + statusState = isPaused ? IntegratedServerState.WORLD_PAUSED : IntegratedServerState.WORLD_LOADED; + } + break; + case IPCPacketFFProcessKeepAlive.FAILURE: + logger.error("Server signaled 'FAILURE' response in state '{}'", IntegratedServerState.getStateName(statusState)); + statusState = IntegratedServerState.WORLD_NONE; + callFailed = true; + break; + case IPCPacket01StopServer.ID: + LANServerController.closeLAN(); + localPlayerNetworkManager.isPlayerChannelOpen = false; + statusState = IntegratedServerState.WORLD_NONE; + break; + case IPCPacket06RenameWorldNBT.ID: + statusState = IntegratedServerState.WORLD_NONE; + break; + case IPCPacket03DeleteWorld.ID: + case IPCPacket07ImportWorld.ID: + case IPCPacket12FileWrite.ID: + case IPCPacket13FileCopyMove.ID: + case IPCPacket18ClearPlayers.ID: + statusState = IntegratedServerState.WORLD_NONE; + break; + case IPCPacketFFProcessKeepAlive.EXITED: + logger.error("Server signaled 'EXITED' response in state '{}'", IntegratedServerState.getStateName(statusState)); + if(ClientPlatformSingleplayer.canKillWorker()) { + ClientPlatformSingleplayer.killWorker(); + } + LANServerController.closeLAN(); + localPlayerNetworkManager.isPlayerChannelOpen = false; + statusState = IntegratedServerState.WORLD_WORKER_NOT_RUNNING; + callFailed = true; + break; + default: + logger.error("IPC acknowledge packet type 0x{} was not handled", Integer.toHexString(pkt.ack)); + break; + } + break; + } + case IPCPacket09RequestResponse.ID: { + IPCPacket09RequestResponse pkt = (IPCPacket09RequestResponse)ipc; + if(statusState == IntegratedServerState.WORLD_EXPORTING) { + statusState = IntegratedServerState.WORLD_NONE; + exportResponse = pkt.response; + }else { + logger.error("IPCPacket09RequestResponse was recieved but statusState was '{}' instead of 'WORLD_EXPORTING'", IntegratedServerState.getStateName(statusState)); + } + break; + } + case IPCPacket0DProgressUpdate.ID: { + IPCPacket0DProgressUpdate pkt = (IPCPacket0DProgressUpdate)ipc; + worldStatusString = pkt.updateMessage; + worldStatusProgress = pkt.updateProgress; + break; + } + case IPCPacket15Crashed.ID: { + exceptions.add((IPCPacket15Crashed)ipc); + if(exceptions.size() > 64) { + exceptions.remove(0); + } + break; + } + case IPCPacket16NBTList.ID: { + IPCPacket16NBTList pkt = (IPCPacket16NBTList)ipc; + if(pkt.opCode == IPCPacket16NBTList.WORLD_LIST && statusState == IntegratedServerState.WORLD_LISTING) { + statusState = IntegratedServerState.WORLD_NONE; + saveListNBT.clear(); + saveListNBT.addAll(pkt.nbtTagList); + loadSaveComparators(); + }else { + logger.error("IPC packet type 0x{} class '{}' contained invalid opCode {} in state {} '{}'", Integer.toHexString(ipc.id()), ipc.getClass().getSimpleName(), pkt.opCode, statusState, IntegratedServerState.getStateName(statusState)); + } + break; + } + case IPCPacket0CPlayerChannel.ID: { + IPCPacket0CPlayerChannel pkt = (IPCPacket0CPlayerChannel)ipc; + if(!pkt.open) { + if(pkt.channel.equals(PLAYER_CHANNEL)) { + LANServerController.closeLAN(); + localPlayerNetworkManager.isPlayerChannelOpen = false; + logger.error("Local player channel was closed"); + }else { + PlatformWebRTC.serverLANDisconnectPeer(pkt.channel); + } + } + break; + } + case IPCPacket14StringList.ID: { + IPCPacket14StringList pkt = (IPCPacket14StringList)ipc; + if(pkt.opCode == IPCPacket14StringList.SERVER_TPS) { + integratedServerTPS.clear(); + integratedServerTPS.addAll(pkt.stringList); + integratedServerLastTPSUpdate = EagRuntime.steadyTimeMillis(); + }else { + logger.warn("Strange string list type {} recieved!", pkt.opCode); + } + break; + } + case IPCPacket1ALoggerMessage.ID: { + IPCPacket1ALoggerMessage pkt = (IPCPacket1ALoggerMessage)ipc; + PlatformApplication.addLogMessage(pkt.logMessage, pkt.isError); + break; + } + case IPCPacket1CIssueDetected.ID: { + IPCPacket1CIssueDetected pkt = (IPCPacket1CIssueDetected)ipc; + issuesDetected.add(pkt.issueID); + break; + } + default: + throw new RuntimeException("Unexpected IPC packet type recieved on client: " + ipc.id()); + } + } + + public static void sendIPCPacket(IPCPacketBase ipc) { + byte[] pkt; + try { + pkt = packetManagerInstance.IPCSerialize(ipc); + }catch (IOException ex) { + throw new RuntimeException("Failed to serialize IPC packet", ex); + } + ClientPlatformSingleplayer.sendPacket(new IPCPacketData(IPC_CHANNEL, pkt)); + } + + + private static boolean callFailed = false; + + public static boolean didLastCallFail() { + boolean c = callFailed; + callFailed = false; + return c; + } + + public static void importWorld(String name, byte[] data, int format, byte gameRules) { + ensureReady(); + statusState = IntegratedServerState.WORLD_IMPORTING; + sendIPCPacket(new IPCPacket07ImportWorld(name, data, (byte)format, gameRules)); + } + + public static void exportWorld(String name, int format) { + ensureReady(); + statusState = IntegratedServerState.WORLD_EXPORTING; + if(format == IPCPacket05RequestData.REQUEST_LEVEL_EAG) { + name = name + (new String(new char[] { (char)253, (char)233, (char)233 })) + EaglerProfile.getName(); + } + sendIPCPacket(new IPCPacket05RequestData(name, (byte)format)); + } + + private static byte[] exportResponse = null; + + public static byte[] getExportResponse() { + byte[] dat = exportResponse; + exportResponse = null; + return dat; + } + + public static String worldStatusString() { + return worldStatusString; + } + + public static float worldStatusProgress() { + return worldStatusProgress; + } + + public static IPCPacket15Crashed worldStatusError() { + return exceptions.size() > 0 ? exceptions.remove(0) : null; + } + + public static IPCPacket15Crashed[] worldStatusErrors() { + int l = exceptions.size(); + if(l == 0) { + return null; + } + IPCPacket15Crashed[] pkts = exceptions.toArray(new IPCPacket15Crashed[l]); + exceptions.clear(); + return pkts; + } + + public static void clearPlayerData(String worldName) { + ensureReady(); + statusState = IntegratedServerState.WORLD_CLEAR_PLAYERS; + sendIPCPacket(new IPCPacket18ClearPlayers(worldName)); + } + + private static void loadSaveComparators() { + saveListMap.clear(); + saveListCache.clear(); + for(int j = 0, l = saveListNBT.size(); j < l; ++j) { + NBTTagCompound nbt = saveListNBT.get(j); + String folderName = nbt.getString("folderNameEagler"); + if(!StringUtils.isEmpty(folderName)) { + WorldInfo worldinfo = new WorldInfo(nbt.getCompoundTag("Data")); + saveListMap.put(folderName, worldinfo); + String s1 = worldinfo.getWorldName(); + if (StringUtils.isEmpty(s1)) { + s1 = folderName; + } + + long i = 0L; + saveListCache.add(new SaveFormatComparator(folderName, s1, worldinfo.getLastTimePlayed(), i, + worldinfo.getGameType(), false, worldinfo.isHardcoreModeEnabled(), + worldinfo.areCommandsAllowed(), nbt)); + } + } + } + + @Override + public String getName() { + return "eaglercraft"; + } + + @Override + public ISaveHandler getSaveLoader(String var1, boolean var2) { + return new SingleplayerSaveHandler(saveListMap.get(var1)); + } + + @Override + public List getSaveList() { + return saveListCache; + } + + @Override + public void flushCache() { + sendIPCPacket(new IPCPacket0EListWorlds()); + statusState = IntegratedServerState.WORLD_LISTING; + } + + @Override + public WorldInfo getWorldInfo(String var1) { + return saveListMap.get(var1); + } + + @Override + public boolean func_154335_d(String var1) { + return false; + } + + @Override + public boolean deleteWorldDirectory(String var1) { + sendIPCPacket(new IPCPacket03DeleteWorld(var1)); + statusState = IntegratedServerState.WORLD_DELETING; + return false; + } + + @Override + public boolean renameWorld(String var1, String var2) { + sendIPCPacket(new IPCPacket06RenameWorldNBT(var1, var2, false)); + statusState = IntegratedServerState.WORLD_RENAMING; + return true; + } + + public static void duplicateWorld(String var1, String var2) { + sendIPCPacket(new IPCPacket06RenameWorldNBT(var1, var2, true)); + statusState = IntegratedServerState.WORLD_DUPLICATING; + } + + @Override + public boolean func_154334_a(String var1) { + return false; + } + + @Override + public boolean isOldMapFormat(String var1) { + return false; + } + + @Override + public boolean convertMapFormat(String var1, IProgressUpdate var2) { + return false; + } + + @Override + public boolean canLoadWorld(String var1) { + return saveListMap.containsKey(var1); + } + + public static boolean canKillWorker() { + return ClientPlatformSingleplayer.canKillWorker(); + } + + public static void killWorker() { + statusState = IntegratedServerState.WORLD_WORKER_NOT_RUNNING; + ClientPlatformSingleplayer.killWorker(); + LANServerController.closeLAN(); + } + + public static void updateLocale(List dump) { + if(statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING) { + sendIPCPacket(new IPCPacket14StringList(IPCPacket14StringList.LOCALE, dump)); + } + } + + public static void setDifficulty(int difficultyId) { + if(isWorldRunning()) { + sendIPCPacket(new IPCPacket0ASetWorldDifficulty((byte)difficultyId)); + } + } + + public static void configureLAN(net.minecraft.world.WorldSettings.GameType enumGameType, boolean allowCommands) { + sendIPCPacket(new IPCPacket17ConfigureLAN(enumGameType.getID(), allowCommands, LANServerController.currentICEServers)); + } + + public static boolean isClientInEaglerSingleplayerOrLAN() { + Minecraft mc = Minecraft.getMinecraft(); + return mc != null && mc.thePlayer != null && mc.thePlayer.sendQueue.isClientInEaglerSingleplayerOrLAN(); + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SkullCommand.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SkullCommand.java index 5d2bf32..1d2aa1d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SkullCommand.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SkullCommand.java @@ -3,9 +3,8 @@ package net.lax1dude.eaglercraft.v1_8.sp; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; -import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketInstallSkinSPEAG; import net.minecraft.client.Minecraft; -import net.minecraft.network.play.client.C17PacketCustomPayload; import net.minecraft.util.ChatComponentTranslation; /** @@ -44,9 +43,9 @@ public class SkullCommand { if(fr == null || mc.thePlayer == null || mc.thePlayer.sendQueue == null) { return; } - ImageData loaded = ImageData.loadImageFile(fr.fileData); + ImageData loaded = ImageData.loadImageFile(fr.fileData, ImageData.getMimeFromType(fr.fileName)); if(loaded == null) { - mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentTranslation("command.skull.error.invalid.png")); + mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentTranslation("command.skull.error.invalid.format")); return; } if(loaded.width != 64 || loaded.height > 64) { @@ -62,7 +61,7 @@ public class SkullCommand { rawSkin[j + 2] = (byte)(k >>> 8); rawSkin[j + 3] = (byte)(k & 0xFF); } - mc.thePlayer.sendQueue.addToSendQueue(new C17PacketCustomPayload("EAG|Skins-1.8", SkinPackets.writeCreateCustomSkull(rawSkin))); + mc.thePlayer.sendQueue.sendEaglerMessage(new CPacketInstallSkinSPEAG(rawSkin)); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiIntegratedServerStartup.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiIntegratedServerStartup.java deleted file mode 100644 index 9e5349d..0000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiIntegratedServerStartup.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.GuiSelectWorld; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiIntegratedServerStartup extends GuiScreen { - - private final GuiScreen backScreen; - private static final String[] dotDotDot = new String[] { "", ".", "..", "..." }; - - private int counter = 0; - - public GuiIntegratedServerStartup(GuiScreen backScreen) { - this.backScreen = backScreen; - } - - protected void keyTyped(char parChar1, int parInt1) { - } - - public void initGui() { - this.buttonList.clear(); - } - - public void updateScreen() { - ++counter; - if(counter > 1 && SingleplayerServerController.isIntegratedServerWorkerStarted()) { - mc.displayGuiScreen(new GuiSelectWorld(backScreen)); - }else if(counter == 2) { - SingleplayerServerController.startIntegratedServerWorker(); - } - } - - public void drawScreen(int i, int j, float f) { - this.drawBackground(0); - String txt = I18n.format("singleplayer.integratedStartup"); - int w = this.fontRendererObj.getStringWidth(txt); - this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((System.currentTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215); - super.drawScreen(i, j, f); - } - -} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java index 99b8a95..8bba261 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java @@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; @@ -9,24 +10,16 @@ import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.resources.I18n; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -60,18 +53,14 @@ public class GuiScreenAddRelay extends GuiScreen { this.parentGui.addNewAddr = ""; this.parentGui.addNewPrimary = RelayManager.relayManager.count() == 0; int sslOff = EagRuntime.requireSSL() ? 36 : 0; - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12 + sslOff, - I18n.format("addRelay.add"))); - this.buttonList.add( - new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12 + sslOff, I18n.format("gui.cancel"))); - this.buttonList.add(new GuiButton(2, this.width / 2 - 100, 142, I18n.format("addRelay.primary") + ": " - + (this.parentGui.addNewPrimary ? I18n.format("gui.yes") : I18n.format("gui.no")))); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12 + sslOff, I18n.format("addRelay.add"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12 + sslOff, I18n.format("gui.cancel"))); + this.buttonList.add(new GuiButton(2, this.width / 2 - 100, 142, I18n.format("addRelay.primary") + ": " + (this.parentGui.addNewPrimary ? I18n.format("gui.yes") : I18n.format("gui.no")))); this.serverName = new GuiTextField(3, this.fontRendererObj, this.width / 2 - 100, 106, 200, 20); this.serverAddress = new GuiTextField(4, this.fontRendererObj, this.width / 2 - 100, 66, 200, 20); this.serverAddress.setMaxStringLength(128); this.serverAddress.setFocused(true); - ((GuiButton) this.buttonList.get(0)).enabled = this.serverAddress.getText().length() > 0 - && this.serverAddress.getText().split(":").length > 0 && this.serverName.getText().length() > 0; + ((GuiButton) this.buttonList.get(0)).enabled = this.serverAddress.getText().length() > 0 && this.serverAddress.getText().split(":").length > 0 && this.serverName.getText().length() > 0; this.serverName.setText(this.parentGui.addNewName); } @@ -96,8 +85,7 @@ public class GuiScreenAddRelay extends GuiScreen { this.parentGui.confirmClicked(true, 0); } else if (par1GuiButton.id == 2) { this.parentGui.addNewPrimary = !this.parentGui.addNewPrimary; - ((GuiButton) this.buttonList.get(2)).displayString = I18n.format("addRelay.primary") + ": " - + (this.parentGui.addNewPrimary ? I18n.format("gui.yes") : I18n.format("gui.no")); + ((GuiButton) this.buttonList.get(2)).displayString = I18n.format("addRelay.primary") + ": " + (this.parentGui.addNewPrimary ? I18n.format("gui.yes") : I18n.format("gui.no")); } } } @@ -124,8 +112,7 @@ public class GuiScreenAddRelay extends GuiScreen { this.actionPerformed((GuiButton) this.buttonList.get(0)); } - ((GuiButton) this.buttonList.get(0)).enabled = this.serverAddress.getText().length() > 0 - && this.serverAddress.getText().split(":").length > 0 && this.serverName.getText().length() > 0; + ((GuiButton) this.buttonList.get(0)).enabled = this.serverAddress.getText().length() > 0 && this.serverAddress.getText().split(":").length > 0 && this.serverName.getText().length() > 0; } /** @@ -145,11 +132,9 @@ public class GuiScreenAddRelay extends GuiScreen { this.drawCenteredString(this.fontRendererObj, I18n.format("addRelay.title"), this.width / 2, 17, 16777215); this.drawString(this.fontRendererObj, I18n.format("addRelay.address"), this.width / 2 - 100, 53, 10526880); this.drawString(this.fontRendererObj, I18n.format("addRelay.name"), this.width / 2 - 100, 94, 10526880); - if (EagRuntime.requireSSL()) { - this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn1"), this.width / 2, 169, - 0xccccff); - this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn2"), this.width / 2, 181, - 0xccccff); + if(EagRuntime.requireSSL()) { + this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn1"), this.width / 2, 169, 0xccccff); + this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn2"), this.width / 2, 181, 0xccccff); } this.serverName.drawTextBox(); this.serverAddress.drawTextBox(); @@ -159,4 +144,16 @@ public class GuiScreenAddRelay extends GuiScreen { public boolean blockPTTKey() { return this.serverName.isFocused() || this.serverAddress.isFocused(); } + + @Override + public boolean showCopyPasteButtons() { + return this.serverName.isFocused() || this.serverAddress.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + this.serverName.fireInputEvent(event, param); + this.serverAddress.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenDemoIntegratedServerStartup.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenDemoIntegratedServerStartup.java index 4a351d5..6d98ef6 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenDemoIntegratedServerStartup.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenDemoIntegratedServerStartup.java @@ -1,69 +1,104 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.lax1dude.eaglercraft.v1_8.sp.WorkerStartupFailedException; -import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiScreenDemoIntegratedServerStartup extends GuiScreen { - - private final GuiScreen contScreen; - private static final String[] dotDotDot = new String[] { "", ".", "..", "..." }; - - private int counter = 0; - - public GuiScreenDemoIntegratedServerStartup(GuiScreen contScreen) { - this.contScreen = contScreen; - } - - protected void keyTyped(char parChar1, int parInt1) { - } - - public void initGui() { - this.buttonList.clear(); - } - - public void updateScreen() { - ++counter; - if(counter == 2) { - try { - SingleplayerServerController.startIntegratedServerWorker(); - }catch(WorkerStartupFailedException ex) { - mc.displayGuiScreen(new GuiScreenIntegratedServerFailed(ex.getMessage(), new GuiScreenDemoIntegratedServerFailed())); - return; - } - }else if(counter > 2) { - IPCPacket15Crashed[] crashReport = SingleplayerServerController.worldStatusErrors(); - if(crashReport != null) { - mc.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiScreenDemoIntegratedServerFailed(), "singleplayer.failed.notStarted", crashReport)); - }else if(SingleplayerServerController.isIntegratedServerWorkerStarted()) { - mc.displayGuiScreen(contScreen); - } - } - } - - public void drawScreen(int i, int j, float f) { - this.drawBackground(0); - String txt = I18n.format("singleplayer.integratedStartup"); - int w = this.fontRendererObj.getStringWidth(txt); - this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((System.currentTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215); - super.drawScreen(i, j, f); - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.WorkerStartupFailedException; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket1CIssueDetected; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSelectWorld; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenDemoIntegratedServerStartup extends GuiScreen { + + private final GuiScreen contScreen; + private final boolean singleThread; + private static final String[] dotDotDot = new String[] { "", ".", "..", "..." }; + + private int counter = 0; + + private GuiButton cancelButton; + + public GuiScreenDemoIntegratedServerStartup(GuiScreen contScreen) { + this.contScreen = contScreen; + this.singleThread = false; + } + + public GuiScreenDemoIntegratedServerStartup(GuiScreen contScreen, boolean singleThread) { + this.contScreen = contScreen; + this.singleThread = singleThread; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(cancelButton = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask"))); + cancelButton.visible = false; + } + + public void updateScreen() { + ++counter; + if(counter == 2) { + try { + SingleplayerServerController.startIntegratedServerWorker(singleThread); + }catch(WorkerStartupFailedException ex) { + mc.displayGuiScreen(new GuiScreenIntegratedServerFailed(ex.getMessage(), new GuiScreenDemoIntegratedServerFailed())); + return; + } + }else if(counter > 2) { + if(counter > 100 && SingleplayerServerController.canKillWorker() && !singleThread) { + cancelButton.visible = true; + } + IPCPacket15Crashed[] crashReport = SingleplayerServerController.worldStatusErrors(); + if(crashReport != null) { + mc.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiScreenDemoIntegratedServerFailed(), "singleplayer.failed.notStarted", crashReport)); + }else if(SingleplayerServerController.isIntegratedServerWorkerStarted()) { + GuiScreen cont = contScreen; + if(SingleplayerServerController.isRunningSingleThreadMode()) { + cont = new GuiScreenIntegratedServerFailed("singleplayer.failed.singleThreadWarning.1", "singleplayer.failed.singleThreadWarning.2", cont); + } else if (!EagRuntime.getConfiguration().isRamdiskMode() + && SingleplayerServerController.isIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE) + && SingleplayerServerController.canKillWorker()) { + cont = new GuiScreenRAMDiskModeDetected(cont); + } + mc.displayGuiScreen(cont); + } + } + } + + protected void actionPerformed(GuiButton parGuiButton) { + if(parGuiButton.id == 0) { + SingleplayerServerController.killWorker(); + mc.displayGuiScreen(new GuiScreenDemoIntegratedServerStartup(contScreen, true)); + } + } + + public void drawScreen(int i, int j, float f) { + this.drawBackground(0); + String txt = I18n.format("singleplayer.integratedStartup"); + int w = this.fontRendererObj.getStringWidth(txt); + this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((EagRuntime.steadyTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215); + super.drawScreen(i, j, f); + } + + public boolean canCloseGui() { + return false; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerBusy.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerBusy.java index 53b8287..7caf337 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerBusy.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerBusy.java @@ -1,167 +1,171 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import java.util.function.BiConsumer; -import java.util.function.BooleanSupplier; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiScreenIntegratedServerBusy extends GuiScreen { - - public final GuiScreen menu; - private GuiButton killTask; - public final String failMessage; - private BooleanSupplier checkTaskComplete; - private Runnable taskKill; - private String lastStatus; - private String currentStatus; - private BiConsumer onException; - private int areYouSure; - - private long startStartTime; - - private static final Runnable defaultTerminateAction = () -> { - if(SingleplayerServerController.canKillWorker()) { - SingleplayerServerController.killWorker(); - Minecraft.getMinecraft().displayGuiScreen(new GuiScreenIntegratedServerFailed("singleplayer.failed.killed", new GuiMainMenu())); - }else { - EagRuntime.showPopup("Cannot kill worker tasks on desktop runtime!"); - } - }; - - public static GuiScreen createException(GuiScreen ok, String msg, IPCPacket15Crashed[] exceptions) { - ok = new GuiScreenIntegratedServerFailed(msg, ok); - if(exceptions != null) { - for(int i = exceptions.length - 1; i >= 0; --i) { - ok = new GuiScreenIntegratedServerCrashed(ok, exceptions[i].crashReport); - } - } - return ok; - } - - private static final BiConsumer defaultExceptionAction = (t, u) -> { - GuiScreenIntegratedServerBusy tt = (GuiScreenIntegratedServerBusy) t; - Minecraft.getMinecraft().displayGuiScreen(createException(tt.menu, tt.failMessage, u)); - }; - - public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete) { - this(menu, progressMessage, failMessage, checkTaskComplete, defaultExceptionAction, defaultTerminateAction); - } - - public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete, BiConsumer exceptionAction) { - this(menu, progressMessage, failMessage, checkTaskComplete, exceptionAction, defaultTerminateAction); - } - - public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete, Runnable onTerminate) { - this(menu, progressMessage, failMessage, checkTaskComplete, defaultExceptionAction, onTerminate); - } - - public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete, BiConsumer onException, Runnable onTerminate) { - this.menu = menu; - this.failMessage = failMessage; - this.checkTaskComplete = checkTaskComplete; - this.onException = onException; - this.taskKill = onTerminate; - this.lastStatus = SingleplayerServerController.worldStatusString(); - this.currentStatus = progressMessage; - } - - public void initGui() { - if(startStartTime == 0) this.startStartTime = System.currentTimeMillis(); - areYouSure = 0; - this.buttonList.add(killTask = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask"))); - killTask.enabled = false; - } - - public boolean doesGuiPauseGame() { - return false; - } - - public void drawScreen(int par1, int par2, float par3) { - this.drawDefaultBackground(); - int top = this.height / 3; - - long millis = System.currentTimeMillis(); - - String str = I18n.format(currentStatus); - - long dots = (millis / 500l) % 4l; - this.drawString(fontRendererObj, str + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(str)) / 2, top + 10, 0xFFFFFF); - - if(areYouSure > 0) { - this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.busy.cancelWarning"), this.width / 2, top + 25, 0xFF8888); - }else { - float prog = SingleplayerServerController.worldStatusProgress(); - if(this.currentStatus.equals(this.lastStatus) && prog > 0.01f) { - this.drawCenteredString(fontRendererObj, (prog > 1.0f ? ("(" + (prog > 1000000.0f ? "" + (int)(prog / 1000000.0f) + "MB" : - (prog > 1000.0f ? "" + (int)(prog / 1000.0f) + "kB" : "" + (int)prog + "B")) + ")") : "" + (int)(prog * 100.0f) + "%"), this.width / 2, top + 25, 0xFFFFFF); - }else { - long elapsed = (millis - startStartTime) / 1000l; - if(elapsed > 3) { - this.drawCenteredString(fontRendererObj, "(" + elapsed + "s)", this.width / 2, top + 25, 0xFFFFFF); - } - } - } - - super.drawScreen(par1, par2, par3); - } - - public void updateScreen() { - long millis = System.currentTimeMillis(); - if(millis - startStartTime > 6000l && SingleplayerServerController.canKillWorker()) { - killTask.enabled = true; - } - if(SingleplayerServerController.didLastCallFail() || !SingleplayerServerController.isIntegratedServerWorkerAlive()) { - onException.accept(this, SingleplayerServerController.worldStatusErrors()); - return; - } - if(checkTaskComplete.getAsBoolean()) { - this.mc.displayGuiScreen(menu); - } - String str = SingleplayerServerController.worldStatusString(); - if(!lastStatus.equals(str)) { - lastStatus = str; - currentStatus = str; - } - killTask.displayString = I18n.format(areYouSure > 0 ? "singleplayer.busy.confirmCancel" : "singleplayer.busy.killTask"); - if(areYouSure > 0) { - --areYouSure; - } - } - - protected void actionPerformed(GuiButton par1GuiButton) { - if(par1GuiButton.id == 0) { - if(areYouSure <= 0) { - areYouSure = 80; - }else if(areYouSure <= 65) { - taskKill.run(); - } - } - } - - public boolean shouldHangupIntegratedServer() { - return false; - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenIntegratedServerBusy extends GuiScreen { + + public final GuiScreen menu; + private GuiButton killTask; + public final String failMessage; + private BooleanSupplier checkTaskComplete; + private Runnable taskKill; + private String lastStatus; + private String currentStatus; + private BiConsumer onException; + private int areYouSure; + + private long startStartTime; + + private static final Runnable defaultTerminateAction = () -> { + if(SingleplayerServerController.canKillWorker()) { + SingleplayerServerController.killWorker(); + Minecraft.getMinecraft().displayGuiScreen(new GuiScreenIntegratedServerFailed("singleplayer.failed.killed", new GuiMainMenu())); + }else { + EagRuntime.showPopup("Cannot kill worker tasks on desktop runtime!"); + } + }; + + public static GuiScreen createException(GuiScreen ok, String msg, IPCPacket15Crashed[] exceptions) { + ok = new GuiScreenIntegratedServerFailed(msg, ok); + if(exceptions != null) { + for(int i = exceptions.length - 1; i >= 0; --i) { + ok = new GuiScreenIntegratedServerCrashed(ok, exceptions[i].crashReport); + } + } + return ok; + } + + private static final BiConsumer defaultExceptionAction = (t, u) -> { + GuiScreenIntegratedServerBusy tt = (GuiScreenIntegratedServerBusy) t; + Minecraft.getMinecraft().displayGuiScreen(createException(tt.menu, tt.failMessage, u)); + }; + + public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete) { + this(menu, progressMessage, failMessage, checkTaskComplete, defaultExceptionAction, defaultTerminateAction); + } + + public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete, BiConsumer exceptionAction) { + this(menu, progressMessage, failMessage, checkTaskComplete, exceptionAction, defaultTerminateAction); + } + + public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete, Runnable onTerminate) { + this(menu, progressMessage, failMessage, checkTaskComplete, defaultExceptionAction, onTerminate); + } + + public GuiScreenIntegratedServerBusy(GuiScreen menu, String progressMessage, String failMessage, BooleanSupplier checkTaskComplete, BiConsumer onException, Runnable onTerminate) { + this.menu = menu; + this.failMessage = failMessage; + this.checkTaskComplete = checkTaskComplete; + this.onException = onException; + this.taskKill = onTerminate; + this.lastStatus = SingleplayerServerController.worldStatusString(); + this.currentStatus = progressMessage; + } + + public void initGui() { + if(startStartTime == 0) this.startStartTime = EagRuntime.steadyTimeMillis(); + areYouSure = 0; + this.buttonList.add(killTask = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask"))); + killTask.enabled = false; + } + + public boolean doesGuiPauseGame() { + return false; + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + int top = this.height / 3; + + long millis = EagRuntime.steadyTimeMillis(); + + String str = I18n.format(currentStatus); + + long dots = (millis / 500l) % 4l; + this.drawString(fontRendererObj, str + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(str)) / 2, top + 10, 0xFFFFFF); + + if(areYouSure > 0) { + this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.busy.cancelWarning"), this.width / 2, top + 25, 0xFF8888); + }else { + float prog = SingleplayerServerController.worldStatusProgress(); + if(this.currentStatus.equals(this.lastStatus) && prog > 0.01f) { + this.drawCenteredString(fontRendererObj, (prog > 1.0f ? ("(" + (prog > 1000000.0f ? "" + (int)(prog / 1000000.0f) + "MB" : + (prog > 1000.0f ? "" + (int)(prog / 1000.0f) + "kB" : "" + (int)prog + "B")) + ")") : "" + (int)(prog * 100.0f) + "%"), this.width / 2, top + 25, 0xFFFFFF); + }else { + long elapsed = (millis - startStartTime) / 1000l; + if(elapsed > 3) { + this.drawCenteredString(fontRendererObj, "(" + elapsed + "s)", this.width / 2, top + 25, 0xFFFFFF); + } + } + } + + super.drawScreen(par1, par2, par3); + } + + public void updateScreen() { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - startStartTime > 6000l && SingleplayerServerController.canKillWorker()) { + killTask.enabled = true; + } + if(SingleplayerServerController.didLastCallFail() || !SingleplayerServerController.isIntegratedServerWorkerAlive()) { + onException.accept(this, SingleplayerServerController.worldStatusErrors()); + return; + } + if(checkTaskComplete.getAsBoolean()) { + this.mc.displayGuiScreen(menu); + } + String str = SingleplayerServerController.worldStatusString(); + if(!lastStatus.equals(str)) { + lastStatus = str; + currentStatus = str; + } + killTask.displayString = I18n.format(areYouSure > 0 ? "singleplayer.busy.confirmCancel" : "singleplayer.busy.killTask"); + if(areYouSure > 0) { + --areYouSure; + } + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + if(areYouSure <= 0) { + areYouSure = 80; + }else if(areYouSure <= 65) { + taskKill.run(); + } + } + } + + public boolean shouldHangupIntegratedServer() { + return false; + } + + public boolean canCloseGui() { + return false; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerCrashed.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerCrashed.java index fb97ea4..aba7497 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerCrashed.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerCrashed.java @@ -2,7 +2,6 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.resources.I18n; /** @@ -33,8 +32,7 @@ public class GuiScreenIntegratedServerCrashed extends GuiScreen { public void initGui() { this.buttonList.clear(); this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height - 50, I18n.format("singleplayer.crashed.continue"))); - ScaledResolution res = new ScaledResolution(mc); - int i = res.getScaleFactor(); + int i = mc.scaledResolution.getScaleFactor(); CrashScreen.showCrashReportOverlay(crashReport, 90 * i, 60 * i, (width - 180) * i, (height - 130) * i); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerFailed.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerFailed.java index 2e6a88b..ac180cc 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerFailed.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerFailed.java @@ -1,57 +1,69 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiScreenIntegratedServerFailed extends GuiScreen { - - private String str1; - private String str2; - private GuiScreen cont; - - public GuiScreenIntegratedServerFailed(String str1, String str2, GuiScreen cont) { - this.str1 = I18n.format(str1); - this.str2 = I18n.format(str2); - this.cont = cont; - } - - public GuiScreenIntegratedServerFailed(String str2, GuiScreen cont) { - this.str1 = I18n.format("singleplayer.failed.title"); - this.str2 = I18n.format(str2); - this.cont = cont; - } - - public void initGui() { - this.buttonList.clear(); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("singleplayer.crashed.continue"))); - } - - public void drawScreen(int par1, int par2, float par3) { - this.drawDefaultBackground(); - this.drawCenteredString(fontRendererObj, str1, this.width / 2, 70, 11184810); - this.drawCenteredString(fontRendererObj, str2, this.width / 2, 90, 16777215); - super.drawScreen(par1, par2, par3); - } - - protected void actionPerformed(GuiButton par1GuiButton) { - if(par1GuiButton.id == 0) { - this.mc.displayGuiScreen(cont); - } - } -} +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenIntegratedServerFailed extends GuiScreen { + + private String str1; + private String str2; + private GuiScreen cont; + + public GuiScreenIntegratedServerFailed(String str1, String str2, GuiScreen cont) { + this.str1 = I18n.format(str1); + this.str2 = I18n.format(str2); + this.cont = cont; + } + + public GuiScreenIntegratedServerFailed(String str2, GuiScreen cont) { + this.str1 = I18n.format("singleplayer.failed.title"); + this.str2 = I18n.format(str2); + this.cont = cont; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("singleplayer.crashed.continue"))); + if(!ClientPlatformSingleplayer.isRunningSingleThreadMode() && ClientPlatformSingleplayer.isSingleThreadModeSupported()) { + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 126, I18n.format("singleplayer.crashed.singleThreadCont"))); + } + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, str1, this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, str2, this.width / 2, 90, 16777215); + super.drawScreen(par1, par2, par3); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + this.mc.displayGuiScreen(cont); + }else if(par1GuiButton.id == 1) { + if(SingleplayerServerController.canKillWorker()) { + SingleplayerServerController.killWorker(); + } + this.mc.displayGuiScreen(new GuiScreenIntegratedServerStartup(new GuiMainMenu(), true)); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerStartup.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerStartup.java index 5b8e5c6..d805c6a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerStartup.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenIntegratedServerStartup.java @@ -1,75 +1,104 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.lax1dude.eaglercraft.v1_8.sp.WorkerStartupFailedException; -import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.GuiSelectWorld; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiScreenIntegratedServerStartup extends GuiScreen { - - private final GuiScreen backScreen; - private static final String[] dotDotDot = new String[] { "", ".", "..", "..." }; - - private int counter = 0; - - public GuiScreenIntegratedServerStartup(GuiScreen backScreen) { - this.backScreen = backScreen; - } - - protected void keyTyped(char parChar1, int parInt1) { - } - - public void initGui() { - this.buttonList.clear(); - } - - public void updateScreen() { - ++counter; - if(counter == 2) { - try { - SingleplayerServerController.startIntegratedServerWorker(); - }catch(WorkerStartupFailedException ex) { - mc.displayGuiScreen(new GuiScreenIntegratedServerFailed(ex.getMessage(), new GuiMainMenu())); - return; - } - }else if(counter > 2) { - IPCPacket15Crashed[] crashReport = SingleplayerServerController.worldStatusErrors(); - if(crashReport != null) { - mc.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiMainMenu(), "singleplayer.failed.notStarted", crashReport)); - }else if(SingleplayerServerController.isIntegratedServerWorkerStarted()) { - GuiScreen cont = new GuiSelectWorld(backScreen); - if(SingleplayerServerController.isRunningSingleThreadMode()) { - cont = new GuiScreenIntegratedServerFailed("singleplayer.failed.singleThreadWarning.1", "singleplayer.failed.singleThreadWarning.2", cont); - } - mc.displayGuiScreen(cont); - } - } - } - - public void drawScreen(int i, int j, float f) { - this.drawBackground(0); - String txt = I18n.format("singleplayer.integratedStartup"); - int w = this.fontRendererObj.getStringWidth(txt); - this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((System.currentTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215); - super.drawScreen(i, j, f); - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.WorkerStartupFailedException; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed; +import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket1CIssueDetected; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSelectWorld; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenIntegratedServerStartup extends GuiScreen { + + private final GuiScreen backScreen; + private final boolean singleThread; + private static final String[] dotDotDot = new String[] { "", ".", "..", "..." }; + + private int counter = 0; + + private GuiButton cancelButton; + + public GuiScreenIntegratedServerStartup(GuiScreen backScreen) { + this.backScreen = backScreen; + this.singleThread = false; + } + + public GuiScreenIntegratedServerStartup(GuiScreen backScreen, boolean singleThread) { + this.backScreen = backScreen; + this.singleThread = singleThread; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(cancelButton = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask"))); + cancelButton.visible = false; + } + + public void updateScreen() { + ++counter; + if(counter == 2) { + try { + SingleplayerServerController.startIntegratedServerWorker(singleThread); + }catch(WorkerStartupFailedException ex) { + mc.displayGuiScreen(new GuiScreenIntegratedServerFailed(ex.getMessage(), new GuiMainMenu())); + return; + } + }else if(counter > 2) { + if(counter > 100 && SingleplayerServerController.canKillWorker() && !singleThread) { + cancelButton.visible = true; + } + IPCPacket15Crashed[] crashReport = SingleplayerServerController.worldStatusErrors(); + if(crashReport != null) { + mc.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiMainMenu(), "singleplayer.failed.notStarted", crashReport)); + }else if(SingleplayerServerController.isIntegratedServerWorkerStarted()) { + GuiScreen cont = new GuiSelectWorld(backScreen); + if(SingleplayerServerController.isRunningSingleThreadMode()) { + cont = new GuiScreenIntegratedServerFailed("singleplayer.failed.singleThreadWarning.1", "singleplayer.failed.singleThreadWarning.2", cont); + } else if (!EagRuntime.getConfiguration().isRamdiskMode() + && SingleplayerServerController.isIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE) + && SingleplayerServerController.canKillWorker()) { + cont = new GuiScreenRAMDiskModeDetected(cont); + } + mc.displayGuiScreen(cont); + } + } + } + + protected void actionPerformed(GuiButton parGuiButton) { + if(parGuiButton.id == 0) { + SingleplayerServerController.killWorker(); + mc.displayGuiScreen(new GuiScreenIntegratedServerStartup(new GuiMainMenu(), true)); + } + } + + public void drawScreen(int i, int j, float f) { + this.drawBackground(0); + String txt = I18n.format("singleplayer.integratedStartup"); + int w = this.fontRendererObj.getStringWidth(txt); + this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((EagRuntime.steadyTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215); + super.drawScreen(i, j, f); + } + + public boolean canCloseGui() { + return false; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnect.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnect.java index 1cf5af3..7853f45 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnect.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnect.java @@ -1,30 +1,23 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.resources.I18n; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -45,12 +38,9 @@ public class GuiScreenLANConnect extends GuiScreen { public void initGui() { Keyboard.enableRepeatEvents(true); this.buttonList.clear(); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12, - I18n.format("directConnect.lanWorldJoin"))); - this.buttonList - .add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12, I18n.format("gui.cancel"))); - this.codeTextField = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 27, 200, - 20); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12, I18n.format("directConnect.lanWorldJoin"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12, I18n.format("gui.cancel"))); + this.codeTextField = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 27, 200, 20); this.codeTextField.setMaxStringLength(48); this.codeTextField.setFocused(true); this.codeTextField.setText(lastCode); @@ -82,25 +72,31 @@ public class GuiScreenLANConnect extends GuiScreen { public void drawScreen(int xx, int yy, float pt) { this.drawDefaultBackground(); - this.drawCenteredString(this.fontRendererObj, I18n.format("selectServer.direct"), this.width / 2, - this.height / 4 - 60 + 20, 16777215); - this.drawString(this.fontRendererObj, I18n.format("directConnect.lanWorldCode"), this.width / 2 - 100, - this.height / 4 + 12, 10526880); - this.drawCenteredString(this.fontRendererObj, I18n.format("directConnect.networkSettingsNote"), this.width / 2, - this.height / 4 + 63, 10526880); - this.drawCenteredString(this.fontRendererObj, I18n.format("directConnect.ipGrabNote"), this.width / 2, - this.height / 4 + 77, 10526880); + this.drawCenteredString(this.fontRendererObj, I18n.format("selectServer.direct"), this.width / 2, this.height / 4 - 60 + 20, 16777215); + this.drawString(this.fontRendererObj, I18n.format("directConnect.lanWorldCode"), this.width / 2 - 100, this.height / 4 + 12, 10526880); + this.drawCenteredString(this.fontRendererObj, I18n.format("directConnect.networkSettingsNote"), this.width / 2, this.height / 4 + 63, 10526880); + this.drawCenteredString(this.fontRendererObj, I18n.format("directConnect.ipGrabNote"), this.width / 2, this.height / 4 + 77, 10526880); this.codeTextField.drawTextBox(); super.drawScreen(xx, yy, pt); this.relaysButton.drawScreen(xx, yy); } protected void actionPerformed(GuiButton par1GuiButton) { - if (par1GuiButton.id == 1) { + if(par1GuiButton.id == 1) { mc.displayGuiScreen(parent); - } else if (par1GuiButton.id == 0) { + }else if(par1GuiButton.id == 0) { mc.displayGuiScreen(new GuiScreenLANConnecting(parent, this.codeTextField.getText().trim())); } } + @Override + public boolean showCopyPasteButtons() { + return codeTextField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + codeTextField.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java index 1c6f530..26e4cba 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java @@ -1,7 +1,9 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServer; @@ -20,24 +22,16 @@ import net.minecraft.util.ChatComponentText; import java.io.IOException; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -72,7 +66,7 @@ public class GuiScreenLANConnecting extends GuiScreen { } public void updateScreen() { - if (networkManager != null) { + if(networkManager != null) { if (networkManager.isChannelOpen()) { try { networkManager.processReceivedPackets(); @@ -83,8 +77,7 @@ public class GuiScreenLANConnecting extends GuiScreen { this.mc.getSession().reset(); if (mc.currentScreen == this) { mc.loadWorld(null); - mc.displayGuiScreen(new GuiDisconnected(parent, "connect.failed", - new ChatComponentText("LAN Connection Refused"))); + mc.displayGuiScreen(new GuiDisconnected(parent, "connect.failed", new ChatComponentText("LAN Connection Refused"))); } } } @@ -93,39 +86,33 @@ public class GuiScreenLANConnecting extends GuiScreen { public void drawScreen(int par1, int par2, float par3) { this.drawDefaultBackground(); - if (completed) { + if(completed) { String message = I18n.format("connect.authorizing"); - this.drawString(fontRendererObj, message, (this.width - this.fontRendererObj.getStringWidth(message)) / 2, - this.height / 3 + 10, 0xFFFFFF); - } else { + this.drawString(fontRendererObj, message, (this.width - this.fontRendererObj.getStringWidth(message)) / 2, this.height / 3 + 10, 0xFFFFFF); + }else { LoadingScreenRenderer ls = mc.loadingScreen; String message = I18n.format("lanServer.pleaseWait"); - this.drawString(fontRendererObj, message, (this.width - this.fontRendererObj.getStringWidth(message)) / 2, - this.height / 3 + 10, 0xFFFFFF); + this.drawString(fontRendererObj, message, (this.width - this.fontRendererObj.getStringWidth(message)) / 2, this.height / 3 + 10, 0xFFFFFF); PlatformWebRTC.startRTCLANClient(); - if (++renderCount > 1) { + if(++renderCount > 1) { RelayServerSocket sock; - if (relay == null) { - sock = RelayManager.relayManager - .getWorkingRelay((str) -> ls.resetProgressAndMessage("Connecting: " + str), 0x02, code); - } else { + if(relay == null) { + sock = RelayManager.relayManager.getWorkingRelay((str) -> ls.resetProgressAndMessage("Connecting: " + str), 0x02, code); + }else { sock = RelayManager.relayManager.connectHandshake(relay, 0x02, code); } - if (sock == null) { - this.mc.displayGuiScreen( - new GuiScreenNoRelays(parent, I18n.format("noRelay.worldNotFound1").replace("$code$", code), - I18n.format("noRelay.worldNotFound2").replace("$code$", code), - I18n.format("noRelay.worldNotFound3"))); + if(sock == null) { + this.mc.displayGuiScreen(new GuiScreenNoRelays(parent, I18n.format("noRelay.worldNotFound1").replace("$code$", code), + I18n.format("noRelay.worldNotFound2").replace("$code$", code), I18n.format("noRelay.worldNotFound3"))); return; } networkManager = LANClientNetworkManager.connectToWorld(sock, code, sock.getURI()); - if (networkManager == null) { - this.mc.displayGuiScreen(new GuiDisconnected(parent, "connect.failed", - new ChatComponentText(I18n.format("noRelay.worldFail").replace("$code$", code)))); + if(networkManager == null) { + this.mc.displayGuiScreen(new GuiDisconnected(parent, "connect.failed", new ChatComponentText(I18n.format("noRelay.worldFail").replace("$code$", code)))); return; } @@ -136,9 +123,14 @@ public class GuiScreenLANConnecting extends GuiScreen { networkManager.setConnectionState(EnumConnectionState.LOGIN); networkManager.setNetHandler(new NetHandlerSingleplayerLogin(networkManager, mc, parent)); networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), - EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket())); + EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(), + ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID)); } } } + public boolean canCloseGui() { + return false; + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenNameWorldImport.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenNameWorldImport.java index 2c0a3d4..ff8c54d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenNameWorldImport.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenNameWorldImport.java @@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiCreateWorld; @@ -13,21 +14,14 @@ import net.minecraft.client.resources.I18n; /** * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -51,7 +45,7 @@ public class GuiScreenNameWorldImport extends GuiScreen { this.importFormat = format; this.world = world; this.name = world.fileName; - if (name.length() > 4 && (name.endsWith(".epk") || name.endsWith(".zip"))) { + if(name.length() > 4 && (name.endsWith(".epk") || name.endsWith(".zip"))) { name = name.substring(0, name.length() - 4); } } @@ -60,17 +54,13 @@ public class GuiScreenNameWorldImport extends GuiScreen { * Called from the main game loop to update the screen. */ public void updateScreen() { - if (!timeToImport) { + if(!timeToImport) { this.theGuiTextField.updateCursorCounter(); } - if (definetlyTimeToImport && !isImporting) { + if(definetlyTimeToImport && !isImporting) { isImporting = true; - SingleplayerServerController.importWorld( - GuiCreateWorld.func_146317_a(mc.getSaveLoader(), this.theGuiTextField.getText().trim()), - world.fileData, importFormat, (byte) ((loadSpawnChunks ? 2 : 0) | (enhancedGameRules ? 1 : 0))); - mc.displayGuiScreen(new GuiScreenIntegratedServerBusy(parentGuiScreen, - "singleplayer.busy.importing." + (importFormat + 1), - "singleplayer.failed.importing." + (importFormat + 1), SingleplayerServerController::isReady)); + SingleplayerServerController.importWorld(GuiCreateWorld.func_146317_a(mc.getSaveLoader(), this.theGuiTextField.getText().trim()), world.fileData, importFormat, (byte) ((loadSpawnChunks ? 2 : 0) | (enhancedGameRules ? 1 : 0))); + mc.displayGuiScreen(new GuiScreenIntegratedServerBusy(parentGuiScreen, "singleplayer.busy.importing." + (importFormat + 1), "singleplayer.failed.importing." + (importFormat + 1), SingleplayerServerController::isReady)); } } @@ -78,23 +68,16 @@ public class GuiScreenNameWorldImport extends GuiScreen { * Adds the buttons (and other controls) to the screen in question. */ public void initGui() { - if (!timeToImport) { + if(!timeToImport) { Keyboard.enableRepeatEvents(true); this.buttonList.clear(); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12, - I18n.format("singleplayer.import.continue"))); - this.buttonList - .add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12, I18n.format("gui.cancel"))); - this.theGuiTextField = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 3, - 200, 20); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12, I18n.format("singleplayer.import.continue"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12, I18n.format("gui.cancel"))); + this.theGuiTextField = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 3, 200, 20); this.theGuiTextField.setFocused(true); this.theGuiTextField.setText(name); - this.buttonList.add(loadSpawnChunksBtn = new GuiButton(2, this.width / 2 - 100, this.height / 4 + 24 + 12, - I18n.format("singleplayer.import.loadSpawnChunks", - loadSpawnChunks ? I18n.format("gui.yes") : I18n.format("gui.no")))); - this.buttonList.add(enhancedGameRulesBtn = new GuiButton(3, this.width / 2 - 100, this.height / 4 + 48 + 12, - I18n.format("singleplayer.import.enhancedGameRules", - enhancedGameRules ? I18n.format("gui.yes") : I18n.format("gui.no")))); + this.buttonList.add(loadSpawnChunksBtn = new GuiButton(2, this.width / 2 - 100, this.height / 4 + 24 + 12, I18n.format("singleplayer.import.loadSpawnChunks", loadSpawnChunks ? I18n.format("gui.yes") : I18n.format("gui.no")))); + this.buttonList.add(enhancedGameRulesBtn = new GuiButton(3, this.width / 2 - 100, this.height / 4 + 48 + 12, I18n.format("singleplayer.import.enhancedGameRules", enhancedGameRules ? I18n.format("gui.yes") : I18n.format("gui.no")))); } } @@ -119,12 +102,10 @@ public class GuiScreenNameWorldImport extends GuiScreen { timeToImport = true; } else if (par1GuiButton.id == 2) { loadSpawnChunks = !loadSpawnChunks; - loadSpawnChunksBtn.displayString = I18n.format("singleplayer.import.loadSpawnChunks", - loadSpawnChunks ? I18n.format("gui.yes") : I18n.format("gui.no")); + loadSpawnChunksBtn.displayString = I18n.format("singleplayer.import.loadSpawnChunks", loadSpawnChunks ? I18n.format("gui.yes") : I18n.format("gui.no")); } else if (par1GuiButton.id == 3) { enhancedGameRules = !enhancedGameRules; - enhancedGameRulesBtn.displayString = I18n.format("singleplayer.import.enhancedGameRules", - enhancedGameRules ? I18n.format("gui.yes") : I18n.format("gui.no")); + enhancedGameRulesBtn.displayString = I18n.format("singleplayer.import.enhancedGameRules", enhancedGameRules ? I18n.format("gui.yes") : I18n.format("gui.no")); } } } @@ -147,7 +128,7 @@ public class GuiScreenNameWorldImport extends GuiScreen { */ protected void mouseClicked(int par1, int par2, int par3) { super.mouseClicked(par1, par2, par3); - if (!timeToImport) { + if(!timeToImport) { this.theGuiTextField.mouseClicked(par1, par2, par3); } } @@ -157,22 +138,28 @@ public class GuiScreenNameWorldImport extends GuiScreen { */ public void drawScreen(int par1, int par2, float par3) { this.drawDefaultBackground(); - if (!timeToImport) { - this.drawCenteredString(this.fontRendererObj, I18n.format("singleplayer.import.title"), this.width / 2, - this.height / 4 - 60 + 20, 16777215); - this.drawString(this.fontRendererObj, I18n.format("singleplayer.import.enterName"), this.width / 2 - 100, - this.height / 4 - 60 + 50, 10526880); - this.drawCenteredString(this.fontRendererObj, I18n.format("createWorld.seedNote"), this.width / 2, - this.height / 4 + 90, -6250336); + if(!timeToImport) { + this.drawCenteredString(this.fontRendererObj, I18n.format("singleplayer.import.title"), this.width / 2, this.height / 4 - 60 + 20, 16777215); + this.drawString(this.fontRendererObj, I18n.format("singleplayer.import.enterName"), this.width / 2 - 100, this.height / 4 - 60 + 50, 10526880); + this.drawCenteredString(this.fontRendererObj, I18n.format("createWorld.seedNote"), this.width / 2, this.height / 4 + 90, -6250336); this.theGuiTextField.drawTextBox(); - } else { + }else { definetlyTimeToImport = true; - long dots = (System.currentTimeMillis() / 500l) % 4l; + long dots = (EagRuntime.steadyTimeMillis() / 500l) % 4l; String str = I18n.format("singleplayer.import.reading", world.fileName); - this.drawString(fontRendererObj, - str + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), - (this.width - this.fontRendererObj.getStringWidth(str)) / 2, this.height / 3 + 10, 0xFFFFFF); + this.drawString(fontRendererObj, str + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(str)) / 2, this.height / 3 + 10, 0xFFFFFF); } super.drawScreen(par1, par2, par3); } + + @Override + public boolean showCopyPasteButtons() { + return theGuiTextField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + theGuiTextField.fireInputEvent(event, param); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRAMDiskModeDetected.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRAMDiskModeDetected.java new file mode 100755 index 0000000..85eef64 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRAMDiskModeDetected.java @@ -0,0 +1,55 @@ +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenRAMDiskModeDetected extends GuiScreen { + + private GuiScreen cont; + + public GuiScreenRAMDiskModeDetected(GuiScreen cont) { + this.cont = cont; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 106, I18n.format("singleplayer.ramdiskdetected.continue"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 136, I18n.format("singleplayer.ramdiskdetected.singleThreadCont"))); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.ramdiskdetected.title"), this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.ramdiskdetected.text0"), this.width / 2, 90, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.ramdiskdetected.text1"), this.width / 2, 105, 16777215); + super.drawScreen(par1, par2, par3); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + this.mc.displayGuiScreen(cont); + }else if(par1GuiButton.id == 1) { + SingleplayerServerController.killWorker(); + mc.displayGuiScreen(new GuiScreenIntegratedServerStartup(new GuiMainMenu(), true)); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRelay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRelay.java index 373d359..8f10ec1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRelay.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenRelay.java @@ -16,24 +16,16 @@ import net.minecraft.util.ResourceLocation; import java.io.IOException; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -62,72 +54,66 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback { selected = -1; buttonList.clear(); buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 28, 100, 20, I18n.format("gui.done"))); - buttonList.add( - new GuiButton(1, this.width / 2 - 154, this.height - 52, 100, 20, I18n.format("networkSettings.add"))); - buttonList.add(deleteRelay = new GuiButton(2, this.width / 2 - 50, this.height - 52, 100, 20, - I18n.format("networkSettings.delete"))); - buttonList.add(setPrimary = new GuiButton(3, this.width / 2 + 54, this.height - 52, 100, 20, - I18n.format("networkSettings.default"))); - buttonList.add(new GuiButton(4, this.width / 2 - 50, this.height - 28, 100, 20, - I18n.format("networkSettings.refresh"))); - buttonList.add(new GuiButton(5, this.width / 2 - 154, this.height - 28, 100, 20, - I18n.format("networkSettings.loadDefaults"))); + buttonList.add(new GuiButton(1, this.width / 2 - 154, this.height - 52, 100, 20, I18n.format("networkSettings.add"))); + buttonList.add(deleteRelay = new GuiButton(2, this.width / 2 - 50, this.height - 52, 100, 20, I18n.format("networkSettings.delete"))); + buttonList.add(setPrimary = new GuiButton(3, this.width / 2 + 54, this.height - 52, 100, 20, I18n.format("networkSettings.default"))); + buttonList.add(new GuiButton(4, this.width / 2 - 50, this.height - 28, 100, 20, I18n.format("networkSettings.refresh"))); + buttonList.add(new GuiButton(5, this.width / 2 - 154, this.height - 28, 100, 20, I18n.format("networkSettings.loadDefaults"))); buttonList.add(new GuiButton(6, this.width - 100, 0, 100, 20, I18n.format("networkSettings.downloadRelay"))); updateButtons(); this.slots = new GuiSlotRelay(this); - if (!hasPinged) { + if(!hasPinged) { hasPinged = true; slots.relayManager.ping(); } } void updateButtons() { - if (selected < 0) { + if(selected < 0) { deleteRelay.enabled = false; setPrimary.enabled = false; - } else { + }else { deleteRelay.enabled = true; setPrimary.enabled = true; } } public void actionPerformed(GuiButton btn) { - if (btn.id == 0) { + if(btn.id == 0) { RelayManager.relayManager.save(); mc.displayGuiScreen(screen); - } else if (btn.id == 1) { + } else if(btn.id == 1) { addingNew = true; mc.displayGuiScreen(new GuiScreenAddRelay(this)); - } else if (btn.id == 2) { - if (selected >= 0) { + } else if(btn.id == 2) { + if(selected >= 0) { RelayServer srv = RelayManager.relayManager.get(selected); - mc.displayGuiScreen( - new GuiYesNo(this, I18n.format("networkSettings.delete"), I18n.format("addRelay.removeText1") + - EnumChatFormatting.GRAY + " '" + srv.comment + "' (" + srv.address + ")", selected)); + mc.displayGuiScreen(new GuiYesNo(this, I18n.format("networkSettings.delete"), I18n.format("addRelay.removeText1") + + EnumChatFormatting.GRAY + " '" + srv.comment + "' (" + srv.address + ")", selected)); deleting = true; } - } else if (btn.id == 3) { - if (selected >= 0) { + } else if(btn.id == 3) { + if(selected >= 0) { slots.relayManager.setPrimary(selected); selected = 0; } - } else if (btn.id == 4) { - long millis = System.currentTimeMillis(); - if (millis - lastRefresh > 700l) { + } else if(btn.id == 4) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastRefresh > 700l) { lastRefresh = millis; slots.relayManager.ping(); } lastRefresh += 60l; - } else if (btn.id == 5) { + } else if(btn.id == 5) { slots.relayManager.loadDefaults(); - long millis = System.currentTimeMillis(); - if (millis - lastRefresh > 700l) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastRefresh > 700l) { lastRefresh = millis; slots.relayManager.ping(); } lastRefresh += 60l; - } else if (btn.id == 6) { - EagRuntime.downloadFileWithName("EaglerSPRelay.zip", EagRuntime.getResourceBytes("relay_download.zip")); + } else if(btn.id == 6) { + EagRuntime.downloadFileWithName("EaglerSPRelay.zip", EagRuntime.getRequiredResourceBytes("relay_download.zip")); } } @@ -151,7 +137,7 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback { my = par2; slots.drawScreen(par1, par2, par3); - if (tooltipString != null) { + if(tooltipString != null) { int ww = mc.fontRendererObj.getStringWidth(tooltipString); Gui.drawRect(par1 + 1, par2 - 14, par1 + ww + 7, par2 - 2, 0xC0000000); screen.drawString(mc.fontRendererObj, tooltipString, par1 + 4, par2 - 12, 0xFF999999); @@ -170,11 +156,8 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback { str = EnumChatFormatting.UNDERLINE + I18n.format("networkSettings.relayTimeoutChange"); int w2 = fontRendererObj.getStringWidth(str); boolean b = par1 > w + 5 && par1 < w + 7 + w2 * 3 / 4 && par2 > 3 && par2 < 11; - if (b) - Mouse.showCursor(EnumCursorType.HAND); - this.drawString(fontRendererObj, - EnumChatFormatting.UNDERLINE + I18n.format("networkSettings.relayTimeoutChange"), 0, 0, - b ? 0xCCCCCC : 0x999999); + if(b) Mouse.showCursor(EnumCursorType.HAND); + this.drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + I18n.format("networkSettings.relayTimeoutChange"), 0, 0, b ? 0xCCCCCC : 0x999999); GlStateManager.popMatrix(); super.drawScreen(par1, par2, par3); @@ -182,15 +165,14 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback { protected void mouseClicked(int par1, int par2, int par3) { super.mouseClicked(par1, par2, par3); - if (par3 == 0) { + if(par3 == 0) { String str = I18n.format("networkSettings.relayTimeout") + " " + mc.gameSettings.relayTimeout; int w = fontRendererObj.getStringWidth(str); str = I18n.format("networkSettings.relayTimeoutChange"); int w2 = fontRendererObj.getStringWidth(str); - if (par1 > w + 5 && par1 < w + 7 + w2 * 3 / 4 && par2 > 3 && par2 < 11) { + if(par1 > w + 5 && par1 < w + 7 + w2 * 3 / 4 && par2 > 3 && par2 < 11) { this.mc.displayGuiScreen(new GuiScreenChangeRelayTimeout(this)); - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); } } } @@ -204,15 +186,15 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback { boolean addNewPrimary; public void confirmClicked(boolean par1, int par2) { - if (par1) { - if (addingNew) { + if(par1) { + if(addingNew) { RelayManager.relayManager.addNew(addNewAddr, addNewName, addNewPrimary); addNewAddr = null; addNewName = null; addNewPrimary = false; selected = -1; updateButtons(); - } else if (deleting) { + }else if(deleting) { RelayManager.relayManager.remove(par2); selected = -1; updateButtons(); @@ -233,4 +215,10 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback { this.slots.handleMouseInput(); } + @Override + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + this.slots.handleTouchInput(); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java index fb38a61..b54d823 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java @@ -1,127 +1,137 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; -import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; -import net.lax1dude.eaglercraft.v1_8.sp.socket.NetHandlerSingleplayerLogin; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiDisconnected; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.client.resources.I18n; -import net.minecraft.network.EnumConnectionState; -import net.minecraft.network.login.client.C00PacketLoginStart; -import net.minecraft.util.ChatComponentText; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiScreenSingleplayerConnecting extends GuiScreen { - - private GuiScreen menu; - private String message; - private GuiButton killTask; - private ClientIntegratedServerNetworkManager networkManager = null; - private int timer = 0; - - private long startStartTime; - private boolean hasOpened = false; - - public GuiScreenSingleplayerConnecting(GuiScreen menu, String message) { - this.menu = menu; - this.message = message; - } - - public void initGui() { - if(startStartTime == 0) this.startStartTime = System.currentTimeMillis(); - this.buttonList.add(killTask = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask"))); - killTask.enabled = false; - } - - public void drawScreen(int par1, int par2, float par3) { - this.drawDefaultBackground(); - float f = 2.0f; - int top = this.height / 3; - - long millis = System.currentTimeMillis(); - - long dots = (millis / 500l) % 4l; - this.drawString(fontRendererObj, message + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(message)) / 2, top + 10, 0xFFFFFF); - - long elapsed = (millis - startStartTime) / 1000l; - if(elapsed > 3) { - this.drawCenteredString(fontRendererObj, "(" + elapsed + "s)", this.width / 2, top + 25, 0xFFFFFF); - } - - super.drawScreen(par1, par2, par3); - } - - public boolean doesGuiPauseGame() { - return false; - } - - public void updateScreen() { - ++timer; - if (timer > 1) { - if (this.networkManager == null) { - this.networkManager = SingleplayerServerController.localPlayerNetworkManager; - this.networkManager.connect(); - } else { - if (this.networkManager.isChannelOpen()) { - if (!hasOpened) { - hasOpened = true; - this.mc.getSession().setLAN(); - this.mc.clearTitles(); - this.networkManager.setConnectionState(EnumConnectionState.LOGIN); - this.networkManager.setNetHandler(new NetHandlerSingleplayerLogin(this.networkManager, this.mc, this.menu)); - this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket())); - } - try { - this.networkManager.processReceivedPackets(); - } catch (IOException ex) { - } - } else { - if (this.networkManager.checkDisconnected()) { - this.mc.getSession().reset(); - if (mc.currentScreen == this) { - mc.loadWorld(null); - mc.displayGuiScreen(new GuiDisconnected(menu, "connect.failed", new ChatComponentText("Worker Connection Refused"))); - } - } - } - } - } - - long millis = System.currentTimeMillis(); - if(millis - startStartTime > 6000l && SingleplayerServerController.canKillWorker()) { - killTask.enabled = true; - } - } - - protected void actionPerformed(GuiButton par1GuiButton) { - if(par1GuiButton.id == 0) { - SingleplayerServerController.killWorker(); - this.mc.loadWorld((WorldClient)null); - this.mc.getSession().reset(); - this.mc.displayGuiScreen(menu); - } - } - - public boolean shouldHangupIntegratedServer() { - return false; - } -} +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; +import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; +import net.lax1dude.eaglercraft.v1_8.sp.socket.NetHandlerSingleplayerLogin; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiDisconnected; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.client.resources.I18n; +import net.minecraft.network.EnumConnectionState; +import net.minecraft.network.login.client.C00PacketLoginStart; +import net.minecraft.util.ChatComponentText; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenSingleplayerConnecting extends GuiScreen { + + private GuiScreen menu; + private String message; + private GuiButton killTask; + private ClientIntegratedServerNetworkManager networkManager = null; + private int timer = 0; + + private long startStartTime; + private boolean hasOpened = false; + + public GuiScreenSingleplayerConnecting(GuiScreen menu, String message) { + this.menu = menu; + this.message = message; + } + + public void initGui() { + if(startStartTime == 0) this.startStartTime = EagRuntime.steadyTimeMillis(); + this.buttonList.add(killTask = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask"))); + killTask.enabled = false; + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + float f = 2.0f; + int top = this.height / 3; + + long millis = EagRuntime.steadyTimeMillis(); + + long dots = (millis / 500l) % 4l; + this.drawString(fontRendererObj, message + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(message)) / 2, top + 10, 0xFFFFFF); + + long elapsed = (millis - startStartTime) / 1000l; + if(elapsed > 3) { + this.drawCenteredString(fontRendererObj, "(" + elapsed + "s)", this.width / 2, top + 25, 0xFFFFFF); + } + + super.drawScreen(par1, par2, par3); + } + + public boolean doesGuiPauseGame() { + return false; + } + + public void updateScreen() { + ++timer; + if (timer > 1) { + if (this.networkManager == null) { + this.networkManager = SingleplayerServerController.localPlayerNetworkManager; + this.networkManager.connect(); + } else { + if (this.networkManager.isChannelOpen()) { + if (!hasOpened) { + hasOpened = true; + this.mc.getSession().setLAN(); + this.mc.clearTitles(); + this.networkManager.setConnectionState(EnumConnectionState.LOGIN); + this.networkManager.setNetHandler(new NetHandlerSingleplayerLogin(this.networkManager, this.mc, this.menu)); + this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), + EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(), + ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID)); + } + try { + this.networkManager.processReceivedPackets(); + } catch (IOException ex) { + } + } else { + if (this.networkManager.checkDisconnected()) { + this.mc.getSession().reset(); + if (mc.currentScreen == this) { + mc.loadWorld(null); + mc.displayGuiScreen(new GuiDisconnected(menu, "connect.failed", new ChatComponentText("Worker Connection Refused"))); + } + } + } + } + } + + long millis = EagRuntime.steadyTimeMillis(); + if(millis - startStartTime > 6000l && SingleplayerServerController.canKillWorker()) { + killTask.enabled = true; + } + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + SingleplayerServerController.killWorker(); + this.mc.loadWorld((WorldClient)null); + this.mc.getSession().reset(); + this.mc.displayGuiScreen(menu); + } + } + + public boolean shouldHangupIntegratedServer() { + return false; + } + + public boolean canCloseGui() { + return false; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java index f1b98cd..107013f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java @@ -1,6 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.minecraft.client.LoadingScreenRenderer; @@ -12,24 +13,16 @@ import net.minecraft.util.ChatComponentText; import net.minecraft.world.WorldSettings; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -123,7 +116,7 @@ public class GuiShareToLan extends GuiScreen { if (par1GuiButton.id == 102) { this.mc.displayGuiScreen(this.parentScreen); } else if (par1GuiButton.id == 104) { - if (!mc.isDemo()) { + if(!mc.isDemo()) { if (this.gameMode.equals("survival")) { this.gameMode = "creative"; } else if (this.gameMode.equals("creative")) { @@ -133,11 +126,11 @@ public class GuiShareToLan extends GuiScreen { } else { this.gameMode = "survival"; } - + this.func_74088_g(); } } else if (par1GuiButton.id == 103) { - if (!mc.isDemo()) { + if(!mc.isDemo()) { this.allowCommands = !this.allowCommands; this.func_74088_g(); } @@ -160,8 +153,7 @@ public class GuiShareToLan extends GuiScreen { LoadingScreenRenderer ls = mc.loadingScreen; String code = LANServerController.shareToLAN(ls::resetProgressAndMessage, worldName, hiddenToggle); if (code != null) { - SingleplayerServerController.configureLAN(WorldSettings.GameType.getByName(this.gameMode), - this.allowCommands); + SingleplayerServerController.configureLAN(WorldSettings.GameType.getByName(this.gameMode), this.allowCommands); this.mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(I18n.format("lanServer.opened") .replace("$relay$", LANServerController.getCurrentURI()).replace("$code$", code))); } else { @@ -207,4 +199,15 @@ public class GuiShareToLan extends GuiScreen { public boolean blockPTTKey() { return this.codeTextField.isFocused(); } + + @Override + public boolean showCopyPasteButtons() { + return this.codeTextField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + this.codeTextField.fireInputEvent(event, param); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiSlider2.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiSlider2.java index 95f7647..2c63698 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiSlider2.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiSlider2.java @@ -1,113 +1,127 @@ -package net.lax1dude.eaglercraft.v1_8.sp.gui; - -import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; - -/** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiSlider2 extends GuiButton { - /** The value of this slider control. */ - public float sliderValue = 1.0F; - public float sliderMax = 1.0F; - - /** Is this slider control being dragged. */ - public boolean dragging = false; - - public GuiSlider2(int par1, int par2, int par3, int par4, int par5, float par6, float par7) { - super(par1, par2, par3, par4, par5, (int) (par6 * par7 * 100.0F) + "%"); - this.sliderValue = par6; - this.sliderMax = par7; - } - - /** - * Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this - * button and 2 if it IS hovering over this button. - */ - protected int getHoverState(boolean par1) { - return 0; - } - - /** - * Fired when the mouse button is dragged. Equivalent of - * MouseListener.mouseDragged(MouseEvent e). - */ - protected void mouseDragged(Minecraft par1Minecraft, int par2, int par3) { - if (this.visible) { - if (this.dragging) { - this.sliderValue = (float) (par2 - (this.xPosition + 4)) / (float) (this.width - 8); - - if (this.sliderValue < 0.0F) { - this.sliderValue = 0.0F; - } - - if (this.sliderValue > 1.0F) { - this.sliderValue = 1.0F; - } - - this.displayString = (int) (this.sliderValue * this.sliderMax * 100.0F) + "%"; - } - - if (this.enabled) { - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.drawTexturedModalRect(this.xPosition + (int) (this.sliderValue * (float) (this.width - 8)), - this.yPosition, 0, 66, 4, 20); - this.drawTexturedModalRect(this.xPosition + (int) (this.sliderValue * (float) (this.width - 8)) + 4, - this.yPosition, 196, 66, 4, 20); - } - } - } - - /** - * Returns true if the mouse has been pressed on this control. Equivalent of - * MouseListener.mousePressed(MouseEvent e). - */ - public boolean mousePressed(Minecraft par1Minecraft, int par2, int par3) { - if (super.mousePressed(par1Minecraft, par2, par3)) { - this.sliderValue = (float) (par2 - (this.xPosition + 4)) / (float) (this.width - 8); - - if (this.sliderValue < 0.0F) { - this.sliderValue = 0.0F; - } - - if (this.sliderValue > 1.0F) { - this.sliderValue = 1.0F; - } - - this.displayString = (int) (this.sliderValue * this.sliderMax * 100.0F) + "%"; - this.dragging = true; - return true; - } else { - return false; - } - } - - /** - * Fired when the mouse button is released. Equivalent of - * MouseListener.mouseReleased(MouseEvent e). - */ - public void mouseReleased(int par1, int par2) { - this.dragging = false; - } +package net.lax1dude.eaglercraft.v1_8.sp.gui; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiSlider2 extends GuiButton { + /** The value of this slider control. */ + public float sliderValue = 1.0F; + public float sliderMax = 1.0F; + + /** Is this slider control being dragged. */ + public boolean dragging = false; + + public GuiSlider2(int buttonId, int x, int y, int widthIn, int heightIn, float sliderValue, float sliderMax) { + super(buttonId, x, y, widthIn, heightIn, null); + this.sliderValue = sliderValue; + this.sliderMax = sliderMax; + this.displayString = updateDisplayString(); + } + + /** + * Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this + * button and 2 if it IS hovering over this button. + */ + protected int getHoverState(boolean par1) { + return 0; + } + + /** + * Fired when the mouse button is dragged. Equivalent of + * MouseListener.mouseDragged(MouseEvent e). + */ + protected void mouseDragged(Minecraft par1Minecraft, int par2, int par3) { + if (this.visible) { + if (this.dragging) { + float oldValue = sliderValue; + this.sliderValue = (float) (par2 - (this.xPosition + 4)) / (float) (this.width - 8); + + if (this.sliderValue < 0.0F) { + this.sliderValue = 0.0F; + } + + if (this.sliderValue > 1.0F) { + this.sliderValue = 1.0F; + } + + if(oldValue != sliderValue) { + onChange(); + } + + this.displayString = updateDisplayString(); + } + + if(this.enabled) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.drawTexturedModalRect(this.xPosition + (int) (this.sliderValue * (float) (this.width - 8)), this.yPosition, 0, 66, 4, 20); + this.drawTexturedModalRect(this.xPosition + (int) (this.sliderValue * (float) (this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20); + } + } + } + + /** + * Returns true if the mouse has been pressed on this control. Equivalent of + * MouseListener.mousePressed(MouseEvent e). + */ + public boolean mousePressed(Minecraft par1Minecraft, int par2, int par3) { + if (super.mousePressed(par1Minecraft, par2, par3)) { + float oldValue = sliderValue; + this.sliderValue = (float) (par2 - (this.xPosition + 4)) / (float) (this.width - 8); + + if (this.sliderValue < 0.0F) { + this.sliderValue = 0.0F; + } + + if (this.sliderValue > 1.0F) { + this.sliderValue = 1.0F; + } + + if(oldValue != sliderValue) { + onChange(); + } + + this.displayString = updateDisplayString(); + this.dragging = true; + return true; + } else { + return false; + } + } + + /** + * Fired when the mouse button is released. Equivalent of + * MouseListener.mouseReleased(MouseEvent e). + */ + public void mouseReleased(int par1, int par2) { + this.dragging = false; + } + + protected String updateDisplayString() { + return (int)(this.sliderValue * this.sliderMax * 100.0F) + "%"; + } + + protected void onChange() { + + } + + public boolean isSliderTouchEvents() { + return true; + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket14StringList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket14StringList.java index 62c5644..90d3df6 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket14StringList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket14StringList.java @@ -34,11 +34,11 @@ public class IPCPacket14StringList implements IPCPacketBase { public final List stringList; public IPCPacket14StringList() { - stringList = new ArrayList(); + stringList = new ArrayList<>(); } public IPCPacket14StringList(int opcode, String[] list) { - stringList = new ArrayList(); + stringList = new ArrayList<>(list.length); for(int i = 0; i < list.length; ++i) { String s = list[i].trim(); if(s.length() > 0) { @@ -49,7 +49,7 @@ public class IPCPacket14StringList implements IPCPacketBase { } public IPCPacket14StringList(int opcode, List list) { - stringList = new ArrayList(); + stringList = new ArrayList<>(list.size()); for(int i = 0, l = list.size(); i < l; ++i) { String s = list.get(i).trim(); if(s.length() > 0) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket16NBTList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket16NBTList.java index bb9d464..d065a08 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket16NBTList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket16NBTList.java @@ -40,8 +40,8 @@ public class IPCPacket16NBTList implements IPCPacketBase { public final List nbtTagList; public IPCPacket16NBTList() { - tagList = new LinkedList(); - nbtTagList = new LinkedList(); + tagList = new LinkedList<>(); + nbtTagList = new LinkedList<>(); } public IPCPacket16NBTList(int opcode, NBTTagCompound[] list) { @@ -49,7 +49,7 @@ public class IPCPacket16NBTList implements IPCPacketBase { } public IPCPacket16NBTList(int opcode, List list) { - tagList = new LinkedList(); + tagList = new LinkedList<>(); nbtTagList = list; for(int i = 0, size = list.size(); i < size; ++i) { NBTTagCompound tag = list.get(i); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket17ConfigureLAN.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket17ConfigureLAN.java index 272dbc2..21167e5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket17ConfigureLAN.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket17ConfigureLAN.java @@ -30,7 +30,7 @@ public class IPCPacket17ConfigureLAN implements IPCPacketBase { public final List iceServers; public IPCPacket17ConfigureLAN() { - iceServers = new ArrayList(); + iceServers = new ArrayList<>(); } public IPCPacket17ConfigureLAN(int gamemode, boolean cheats, List iceServers) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket20LoggerMessage.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1ALoggerMessage.java similarity index 86% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket20LoggerMessage.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1ALoggerMessage.java index 9babf0b..7404478 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket20LoggerMessage.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1ALoggerMessage.java @@ -19,22 +19,22 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPCPacket20LoggerMessage implements IPCPacketBase { +public class IPCPacket1ALoggerMessage implements IPCPacketBase { - public static final int ID = 0x20; + public static final int ID = 0x1A; public String logMessage; public boolean isError; - public IPCPacket20LoggerMessage() { + public IPCPacket1ALoggerMessage() { } - public IPCPacket20LoggerMessage(String logMessage, boolean isError) { + public IPCPacket1ALoggerMessage(String logMessage, boolean isError) { this.logMessage = logMessage; this.isError = isError; } - public IPCPacket20LoggerMessage(String logMessage) { + public IPCPacket1ALoggerMessage(String logMessage) { this.logMessage = logMessage; this.isError = false; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket21EnableLogging.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1BEnableLogging.java similarity index 87% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket21EnableLogging.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1BEnableLogging.java index 7c9423d..a3e5afd 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket21EnableLogging.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1BEnableLogging.java @@ -19,16 +19,16 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPCPacket21EnableLogging implements IPCPacketBase { +public class IPCPacket1BEnableLogging implements IPCPacketBase { - public static final int ID = 0x21; + public static final int ID = 0x1B; public boolean enable; - public IPCPacket21EnableLogging() { + public IPCPacket1BEnableLogging() { } - public IPCPacket21EnableLogging(boolean enable) { + public IPCPacket1BEnableLogging(boolean enable) { this.enable = enable; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1CIssueDetected.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1CIssueDetected.java new file mode 100755 index 0000000..f4ca120 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacket1CIssueDetected.java @@ -0,0 +1,57 @@ +package net.lax1dude.eaglercraft.v1_8.sp.ipc; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class IPCPacket1CIssueDetected implements IPCPacketBase { + + public static final int ID = 0x1C; + + public static final int ISSUE_RAMDISK_MODE = 0x01; + + public int issueID; + + public IPCPacket1CIssueDetected() { + } + + public IPCPacket1CIssueDetected(int issueID) { + this.issueID = issueID; + } + + @Override + public void deserialize(DataInput bin) throws IOException { + issueID = bin.readUnsignedByte(); + } + + @Override + public void serialize(DataOutput bin) throws IOException { + bin.writeByte(issueID); + } + + @Override + public int id() { + return ID; + } + + @Override + public int size() { + return 1; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java index ea61173..6363efa 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java @@ -9,35 +9,28 @@ import java.util.function.Supplier; /** * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class IPCPacketManager { - - public static final HashMap> mappings = new HashMap(); + + public static final HashMap> mappings = new HashMap<>(); public final IPCInputStream IPC_INPUT_STREAM = new IPCInputStream(); public final IPCOutputStream IPC_OUTPUT_STREAM = new IPCOutputStream(); public final DataInputStream IPC_DATA_INPUT_STREAM = new DataInputStream(IPC_INPUT_STREAM); public final DataOutputStream IPC_DATA_OUTPUT_STREAM = new DataOutputStream(IPC_OUTPUT_STREAM); - + static { mappings.put(IPCPacket00StartServer.ID, IPCPacket00StartServer::new); mappings.put(IPCPacket01StopServer.ID, IPCPacket01StopServer::new); @@ -62,43 +55,43 @@ public class IPCPacketManager { mappings.put(IPCPacket17ConfigureLAN.ID, IPCPacket17ConfigureLAN::new); mappings.put(IPCPacket18ClearPlayers.ID, IPCPacket18ClearPlayers::new); mappings.put(IPCPacket19Autosave.ID, IPCPacket19Autosave::new); - mappings.put(IPCPacket20LoggerMessage.ID, IPCPacket20LoggerMessage::new); - mappings.put(IPCPacket21EnableLogging.ID, IPCPacket21EnableLogging::new); + mappings.put(IPCPacket1ALoggerMessage.ID, IPCPacket1ALoggerMessage::new); + mappings.put(IPCPacket1BEnableLogging.ID, IPCPacket1BEnableLogging::new); + mappings.put(IPCPacket1CIssueDetected.ID, IPCPacket1CIssueDetected::new); mappings.put(IPCPacketFFProcessKeepAlive.ID, IPCPacketFFProcessKeepAlive::new); } - + public byte[] IPCSerialize(IPCPacketBase pkt) throws IOException { - + IPC_OUTPUT_STREAM.feedBuffer(new byte[pkt.size() + 1], pkt.getClass().getSimpleName()); IPC_OUTPUT_STREAM.write(pkt.id()); pkt.serialize(IPC_DATA_OUTPUT_STREAM); - + return IPC_OUTPUT_STREAM.returnBuffer(); } - + public IPCPacketBase IPCDeserialize(byte[] pkt) throws IOException { - + IPC_INPUT_STREAM.feedBuffer(pkt); int i = IPC_INPUT_STREAM.read(); - + Supplier pk = mappings.get(Integer.valueOf(i)); - if (pk == null) { + if(pk == null) { throw new IOException("Packet type 0x" + Integer.toHexString(i) + " doesn't exist"); } - + IPCPacketBase p = pk.get(); - + IPC_INPUT_STREAM.nameBuffer(p.getClass().getSimpleName()); - + p.deserialize(IPC_DATA_INPUT_STREAM); - + int lo = IPC_INPUT_STREAM.getLeftoverCount(); - if (lo > 0) { - System.err.println("Packet type 0x" + Integer.toHexString(i) + " class '" + p.getClass().getSimpleName() - + "' was size " + (pkt.length - 1) + " but only " + (pkt.length - 1 - lo) + " bytes were read"); + if(lo > 0) { + System.err.println("Packet type 0x" + Integer.toHexString(i) + " class '" + p.getClass().getSimpleName() + "' was size " + (pkt.length - 1) + " but only " + (pkt.length - 1 - lo) + " bytes were read"); } - + return p; } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java index 69a1d1d..15a1054 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java @@ -1,5 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.sp.lan; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; @@ -27,21 +28,14 @@ import java.util.List; /** * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -54,8 +48,7 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { public static final int fragmentSize = 0xFF00; - private static final String[] initStateNames = new String[] { "PRE", "INIT", "SENT_ICE_CANDIDATE", - "SENT_DESCRIPTION" }; + private static final String[] initStateNames = new String[] { "PRE", "INIT", "SENT_ICE_CANDIDATE", "SENT_DESCRIPTION" }; public final String displayCode; public final String displayRelay; @@ -80,41 +73,39 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { return clientDisconnected ? EnumEaglerConnectionState.CLOSED : EnumEaglerConnectionState.CONNECTED; } - public static LANClientNetworkManager connectToWorld(RelayServerSocket sock, String displayCode, - String displayRelay) { + public static LANClientNetworkManager connectToWorld(RelayServerSocket sock, String displayCode, String displayRelay) { PlatformWebRTC.clearLANClientState(); int connectState = PRE; - IPacket pkt; - mainLoop: while (!sock.isClosed()) { - if ((pkt = sock.readPacket()) != null) { - if (pkt instanceof IPacket00Handshake) { - if (connectState == PRE) { + RelayPacket pkt; + mainLoop: while(!sock.isClosed()) { + PlatformWebRTC.runScheduledTasks(); + sock.update(); + if((pkt = sock.readPacket()) != null) { + if(pkt instanceof RelayPacket00Handshake) { + if(connectState == PRE) { - // %%%%%% Process IPacket00Handshake %%%%%% + // %%%%%% Process IPacket00Handshake %%%%%% - logger.info("Relay [{}|{}] recieved handshake, client id: {}", displayRelay, displayCode, - ((IPacket00Handshake) pkt).connectionCode); + logger.info("Relay [{}|{}] recieved handshake, client id: {}", displayRelay, displayCode, ((RelayPacket00Handshake)pkt).connectionCode); connectState = INIT; - } else { + }else { sock.close(); - logger.error("Relay [{}|{}] unexpected packet: IPacket00Handshake in state {}", displayRelay, - displayCode, initStateNames[connectState]); + logger.error("Relay [{}|{}] unexpected packet: IPacket00Handshake in state {}", displayRelay, displayCode, initStateNames[connectState]); return null; } - } else if (pkt instanceof IPacket01ICEServers) { - if (connectState == INIT) { + }else if(pkt instanceof RelayPacket01ICEServers) { + if(connectState == INIT) { - // %%%%%% Process IPacket01ICEServers %%%%%% + // %%%%%% Process IPacket01ICEServers %%%%%% - IPacket01ICEServers ipkt = (IPacket01ICEServers) pkt; + RelayPacket01ICEServers ipkt = (RelayPacket01ICEServers) pkt; // print servers logger.info("Relay [{}|{}] provided ICE servers:", displayRelay, displayCode); - List servers = new ArrayList(); - for (ICEServerSet.RelayServer srv : ipkt.servers) { - logger.info("Relay [{}|{}] {}: {}", displayRelay, displayCode, srv.type.name(), - srv.address); + List servers = new ArrayList<>(); + for(RelayPacket01ICEServers.RelayServer srv : ipkt.servers) { + logger.info("Relay [{}|{}] {}: {}", displayRelay, displayCode, srv.type.name(), srv.address); servers.add(srv.getICEString()); } @@ -122,128 +113,126 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { PlatformWebRTC.clientLANSetICEServersAndConnect(servers.toArray(new String[servers.size()])); // await result - long lm = System.currentTimeMillis(); + long lm = EagRuntime.steadyTimeMillis(); do { + PlatformWebRTC.runScheduledTasks(); String c = PlatformWebRTC.clientLANAwaitDescription(); - if (c != null) { + if(c != null) { logger.info("Relay [{}|{}] client sent description", displayRelay, displayCode); // 'this.descriptionHandler' was called, send result: - sock.writePacket(new IPacket04Description("", c)); + sock.writePacket(new RelayPacket04Description("", c)); connectState = SENT_DESCRIPTION; continue mainLoop; } EagUtils.sleep(20l); - } while (System.currentTimeMillis() - lm < 5000l); + }while(EagRuntime.steadyTimeMillis() - lm < 5000l); // no description was sent sock.close(); logger.error("Relay [{}|{}] client provide description timeout", displayRelay, displayCode); return null; - } else { + }else { sock.close(); - logger.error("Relay [{}|{}] unexpected packet: IPacket01ICEServers in state {}", displayRelay, - displayCode, initStateNames[connectState]); + logger.error("Relay [{}|{}] unexpected packet: IPacket01ICEServers in state {}", displayRelay, displayCode, initStateNames[connectState]); return null; } - } else if (pkt instanceof IPacket03ICECandidate) { - if (connectState == SENT_ICE_CANDIDATE) { + }else if(pkt instanceof RelayPacket03ICECandidate) { + if(connectState == SENT_ICE_CANDIDATE) { - // %%%%%% Process IPacket03ICECandidate %%%%%% + // %%%%%% Process IPacket03ICECandidate %%%%%% - IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt; + RelayPacket03ICECandidate ipkt = (RelayPacket03ICECandidate) pkt; // process logger.info("Relay [{}|{}] recieved server ICE candidate", displayRelay, displayCode); - PlatformWebRTC.clientLANSetICECandidate(ipkt.candidate); + PlatformWebRTC.clientLANSetICECandidate(ipkt.getCandidateString()); // await result - long lm = System.currentTimeMillis(); + long lm = EagRuntime.steadyTimeMillis(); do { - if (PlatformWebRTC.clientLANAwaitChannel()) { + PlatformWebRTC.runScheduledTasks(); + if(PlatformWebRTC.clientLANAwaitChannel()) { logger.info("Relay [{}|{}] client opened data channel", displayRelay, displayCode); // 'this.remoteDataChannelHandler' was called, success - sock.writePacket(new IPacket05ClientSuccess(ipkt.peerId)); + sock.writePacket(new RelayPacket05ClientSuccess(ipkt.peerId)); sock.close(); return new LANClientNetworkManager(displayCode, displayRelay); } EagUtils.sleep(20l); - } while (System.currentTimeMillis() - lm < 5000l); + }while(EagRuntime.steadyTimeMillis() - lm < 5000l); // no channel was opened - sock.writePacket(new IPacket06ClientFailure(ipkt.peerId)); + sock.writePacket(new RelayPacket06ClientFailure(ipkt.peerId)); sock.close(); logger.error("Relay [{}|{}] client open data channel timeout", displayRelay, displayCode); return null; - } else { + }else { sock.close(); - logger.error("Relay [{}|{}] unexpected packet: IPacket03ICECandidate in state {}", displayRelay, - displayCode, initStateNames[connectState]); + logger.error("Relay [{}|{}] unexpected packet: IPacket03ICECandidate in state {}", displayRelay, displayCode, initStateNames[connectState]); return null; } - } else if (pkt instanceof IPacket04Description) { - if (connectState == SENT_DESCRIPTION) { + }else if(pkt instanceof RelayPacket04Description) { + if(connectState == SENT_DESCRIPTION) { - // %%%%%% Process IPacket04Description %%%%%% + // %%%%%% Process IPacket04Description %%%%%% - IPacket04Description ipkt = (IPacket04Description) pkt; + RelayPacket04Description ipkt = (RelayPacket04Description) pkt; // process logger.info("Relay [{}|{}] recieved server description", displayRelay, displayCode); - PlatformWebRTC.clientLANSetDescription(ipkt.description); + PlatformWebRTC.clientLANSetDescription(ipkt.getDescriptionString()); // await result - long lm = System.currentTimeMillis(); + long lm = EagRuntime.steadyTimeMillis(); do { + PlatformWebRTC.runScheduledTasks(); String c = PlatformWebRTC.clientLANAwaitICECandidate(); - if (c != null) { + if(c != null) { logger.info("Relay [{}|{}] client sent ICE candidate", displayRelay, displayCode); // 'this.iceCandidateHandler' was called, send result: - sock.writePacket(new IPacket03ICECandidate("", c)); + sock.writePacket(new RelayPacket03ICECandidate("", c)); connectState = SENT_ICE_CANDIDATE; continue mainLoop; } EagUtils.sleep(20l); - } while (System.currentTimeMillis() - lm < 5000l); + }while(EagRuntime.steadyTimeMillis() - lm < 5000l); // no ice candidates were sent sock.close(); logger.error("Relay [{}|{}] client provide ICE candidate timeout", displayRelay, displayCode); return null; - } else { + }else { sock.close(); - logger.error("Relay [{}|{}] unexpected packet: IPacket04Description in state {}", displayRelay, - displayCode, initStateNames[connectState]); + logger.error("Relay [{}|{}] unexpected packet: IPacket04Description in state {}", displayRelay, displayCode, initStateNames[connectState]); return null; } - } else if (pkt instanceof IPacketFFErrorCode) { + }else if(pkt instanceof RelayPacketFFErrorCode) { - // %%%%%% Process IPacketFFErrorCode %%%%%% + // %%%%%% Process IPacketFFErrorCode %%%%%% - IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; - logger.error("Relay [{}|{}] connection failed: {}({}): {}", displayRelay, displayCode, - IPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc); + RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode) pkt; + logger.error("Relay [{}|{}] connection failed: {}({}): {}", displayRelay, displayCode, RelayPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc); Throwable t; - while ((t = sock.getException()) != null) { + while((t = sock.getException()) != null) { logger.error(t); } sock.close(); return null; - } else { + }else { - // %%%%%% Unexpected Packet %%%%%% + // %%%%%% Unexpected Packet %%%%%% - logger.error("Relay [{}|{}] unexpected packet: {}", displayRelay, displayCode, - pkt.getClass().getSimpleName()); + logger.error("Relay [{}|{}] unexpected packet: {}", displayRelay, displayCode, pkt.getClass().getSimpleName()); sock.close(); return null; } @@ -255,7 +244,7 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { @Override public void sendPacket(Packet pkt) { - if (!isChannelOpen()) { + if(!isChannelOpen()) { logger.error("Packet was sent on a closed connection: {}", pkt.getClass().getSimpleName()); return; } @@ -263,7 +252,7 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { int i; try { i = packetState.getPacketId(EnumPacketDirection.SERVERBOUND, pkt); - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Incorrect packet for state: {}", pkt.getClass().getSimpleName()); return; } @@ -272,22 +261,22 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { temporaryBuffer.writeVarIntToBuffer(i); try { pkt.writePacketData(temporaryBuffer); - } catch (IOException ex) { + }catch(IOException ex) { logger.error("Failed to write packet {}!", pkt.getClass().getSimpleName()); return; } int len = temporaryBuffer.readableBytes(); int fragmentSizeN1 = fragmentSize - 1; - if (len > fragmentSizeN1) { + if(len > fragmentSizeN1) { do { int readLen = len > fragmentSizeN1 ? fragmentSizeN1 : len; byte[] frag = new byte[readLen + 1]; temporaryBuffer.readBytes(frag, 1, readLen); - frag[0] = temporaryBuffer.readableBytes() == 0 ? (byte) 0 : (byte) 1; + frag[0] = temporaryBuffer.readableBytes() == 0 ? (byte)0 : (byte)1; PlatformWebRTC.clientLANSendPacket(frag); - } while ((len = temporaryBuffer.readableBytes()) > 0); - } else { + }while((len = temporaryBuffer.readableBytes()) > 0); + }else { byte[] bytes = new byte[len + 1]; bytes[0] = 0; temporaryBuffer.readBytes(bytes, 1, len); @@ -308,34 +297,34 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { return !clientDisconnected; } - private List fragmentedPacket = new ArrayList(); + private List fragmentedPacket = new ArrayList<>(); @Override public void processReceivedPackets() throws IOException { - if (this.nethandler != null) { + if(this.nethandler != null) { List packets = PlatformWebRTC.clientLANReadAllPacket(); - if (packets == null) { + if(packets == null) { return; } - for (int k = 0, l = packets.size(); k < l; ++k) { + for(int k = 0, l = packets.size(); k < l; ++k) { byte[] data = packets.get(k); byte[] fullData; boolean compressed = false; if (data[0] == 0 || data[0] == 2) { - if (fragmentedPacket.isEmpty()) { + if(fragmentedPacket.isEmpty()) { fullData = new byte[data.length - 1]; System.arraycopy(data, 1, fullData, 0, fullData.length); - } else { + }else { fragmentedPacket.add(data); int len = 0; int fragCount = fragmentedPacket.size(); - for (int i = 0; i < fragCount; ++i) { + for(int i = 0; i < fragCount; ++i) { len += fragmentedPacket.get(i).length - 1; } fullData = new byte[len]; len = 0; - for (int i = 0; i < fragCount; ++i) { + for(int i = 0; i < fragCount; ++i) { byte[] f = fragmentedPacket.get(i); System.arraycopy(f, 1, fullData, len, f.length - 1); len += f.length - 1; @@ -347,19 +336,19 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { fragmentedPacket.add(data); continue; } else { - logger.error("Recieved {} byte fragment of unknown type: {}", data.length, ((int) data[0] & 0xFF)); + logger.error("Recieved {} byte fragment of unknown type: {}", data.length, ((int)data[0] & 0xFF)); continue; } - if (compressed) { - if (fullData.length < 4) { + if(compressed) { + if(fullData.length < 4) { throw new IOException("Recieved invalid " + fullData.length + " byte compressed packet"); } EaglerInputStream bi = new EaglerInputStream(fullData); int i = (bi.read() << 24) | (bi.read() << 16) | (bi.read() << 8) | bi.read(); fullData = new byte[i]; int r; - try (InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi)) { + try(InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi)) { r = IOUtils.readFully(inflaterInputStream, fullData); } if (i != r) { @@ -367,10 +356,9 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { } } - if (firstPacket) { + if(firstPacket) { // 1.5 kick packet - if (fullData.length == 31 && fullData[0] == (byte) 0xFF && fullData[1] == (byte) 0x00 - && fullData[2] == (byte) 0x0E) { + if(fullData.length == 31 && fullData[0] == (byte)0xFF && fullData[1] == (byte)0x00 && fullData[2] == (byte)0x0E) { logger.error("Detected a 1.5 LAN server!"); this.closeChannel(new ChatComponentTranslation("singleplayer.outdatedLANServerKick")); firstPacket = false; @@ -387,26 +375,24 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { Packet pkt; try { pkt = packetState.getPacket(EnumPacketDirection.CLIENTBOUND, pktId); - } catch (IllegalAccessException | InstantiationException ex) { + }catch(IllegalAccessException | InstantiationException ex) { throw new IOException("Recieved a packet with type " + pktId + " which is invalid!"); } - if (pkt == null) { - throw new IOException( - "Recieved packet type " + pktId + " which is undefined in state " + packetState); + if(pkt == null) { + throw new IOException("Recieved packet type " + pktId + " which is undefined in state " + packetState); } try { pkt.readPacketData(input); - } catch (Throwable t) { + }catch(Throwable t) { throw new IOException("Failed to read packet type '" + pkt.getClass().getSimpleName() + "'", t); } try { pkt.processPacket(nethandler); - } catch (Throwable t) { - logger.error("Failed to process {}! It'll be skipped for debug purposes.", - pkt.getClass().getSimpleName()); + }catch(Throwable t) { + logger.error("Failed to process {}! It'll be skipped for debug purposes.", pkt.getClass().getSimpleName()); logger.error(t); } } @@ -415,10 +401,10 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { @Override public void closeChannel(IChatComponent reason) { - if (!PlatformWebRTC.clientLANClosed()) { + if(!PlatformWebRTC.clientLANClosed()) { PlatformWebRTC.clientLANCloseConnection(); } - if (nethandler != null) { + if(nethandler != null) { nethandler.onDisconnect(reason); } clientDisconnected = true; @@ -426,7 +412,7 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { @Override public boolean checkDisconnected() { - if (PlatformWebRTC.clientLANClosed()) { + if(PlatformWebRTC.clientLANClosed()) { clientDisconnected = false; try { processReceivedPackets(); // catch kick message diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java index 3e11308..817bc4c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java @@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.lan; import java.util.Iterator; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; @@ -10,28 +11,20 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket03ICECandidate; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket04Description; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket03ICECandidate; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket04Description; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -53,123 +46,115 @@ class LANClientPeer { } protected void handleICECandidates(String candidates) { - if (state == SENT_DESCRIPTION) { + if(state == SENT_DESCRIPTION) { PlatformWebRTC.serverLANPeerICECandidates(clientId, candidates); - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); do { LANPeerEvent evt; - if ((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null) { - if (evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { - LANServerController.lanRelaySocket.writePacket(new IPacket03ICECandidate(clientId, - ((LANPeerEvent.LANPeerICECandidateEvent) evt).candidates)); + if((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null) { + if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { + LANServerController.lanRelaySocket.writePacket(new RelayPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates)); state = SENT_ICE_CANDIDATE; return; - } else if (evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { logger.error("LAN client '{}' disconnected while waiting for server ICE candidates", clientId); - } else { + }else { logger.error("LAN client '{}' had an accident: {}", clientId, evt.getClass().getSimpleName()); } disconnect(); return; } EagUtils.sleep(20l); - } while (System.currentTimeMillis() - millis < 5000l); + }while(EagRuntime.steadyTimeMillis() - millis < 5000l); logger.error("Getting server ICE candidates for '{}' timed out!", clientId); disconnect(); - } else { - logger.error("Relay [{}] unexpected IPacket03ICECandidate for '{}'", - LANServerController.lanRelaySocket.getURI(), clientId); + }else { + logger.error("Relay [{}] unexpected IPacket03ICECandidate for '{}'", LANServerController.lanRelaySocket.getURI(), clientId); } } protected void handleDescription(String description) { - if (state == PRE) { + if(state == PRE) { PlatformWebRTC.serverLANPeerDescription(clientId, description); - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); do { LANPeerEvent evt; - if ((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null) { - if (evt instanceof LANPeerEvent.LANPeerDescriptionEvent) { - LANServerController.lanRelaySocket.writePacket(new IPacket04Description(clientId, - ((LANPeerEvent.LANPeerDescriptionEvent) evt).description)); + if((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null) { + if(evt instanceof LANPeerEvent.LANPeerDescriptionEvent) { + LANServerController.lanRelaySocket.writePacket(new RelayPacket04Description(clientId, ((LANPeerEvent.LANPeerDescriptionEvent)evt).description)); state = SENT_DESCRIPTION; return; - } else if (evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { logger.error("LAN client '{}' disconnected while waiting for server description", clientId); - } else { + }else { logger.error("LAN client '{}' had an accident: {}", clientId, evt.getClass().getSimpleName()); } disconnect(); return; } EagUtils.sleep(20l); - } while (System.currentTimeMillis() - millis < 5000l); + }while(EagRuntime.steadyTimeMillis() - millis < 5000l); logger.error("Getting server description for '{}' timed out!", clientId); disconnect(); - } else { - logger.error("Relay [{}] unexpected IPacket04Description for '{}'", - LANServerController.lanRelaySocket.getURI(), clientId); + }else { + logger.error("Relay [{}] unexpected IPacket04Description for '{}'", LANServerController.lanRelaySocket.getURI(), clientId); } } protected void handleSuccess() { - if (state == SENT_ICE_CANDIDATE) { - long millis = System.currentTimeMillis(); + if(state == SENT_ICE_CANDIDATE) { + long millis = EagRuntime.steadyTimeMillis(); do { LANPeerEvent evt; - while ((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null - && evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { + while((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null && evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { // skip ice candidates } - if (evt != null) { - if (evt instanceof LANPeerEvent.LANPeerDataChannelEvent) { + if(evt != null) { + if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) { SingleplayerServerController.openPlayerChannel(clientId); state = CONNECTED; return; - } else if (evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { logger.error("LAN client '{}' disconnected while waiting for connection", clientId); - } else { + }else { logger.error("LAN client '{}' had an accident: {}", clientId, evt.getClass().getSimpleName()); } disconnect(); return; } EagUtils.sleep(20l); - } while (System.currentTimeMillis() - millis < 5000l); + }while(EagRuntime.steadyTimeMillis() - millis < 5000l); logger.error("Getting server description for '{}' timed out!", clientId); disconnect(); - } else { - logger.error("Relay [{}] unexpected IPacket05ClientSuccess for '{}'", - LANServerController.lanRelaySocket.getURI(), clientId); + }else { + logger.error("Relay [{}] unexpected IPacket05ClientSuccess for '{}'", LANServerController.lanRelaySocket.getURI(), clientId); } } protected void handleFailure() { - if (state == SENT_ICE_CANDIDATE) { + if(state == SENT_ICE_CANDIDATE) { logger.error("Client '{}' failed to connect", clientId); disconnect(); - } else { - logger.error("Relay [{}] unexpected IPacket06ClientFailure for '{}'", - LANServerController.lanRelaySocket.getURI(), clientId); + }else { + logger.error("Relay [{}] unexpected IPacket06ClientFailure for '{}'", LANServerController.lanRelaySocket.getURI(), clientId); } } protected void update() { - if (state == CONNECTED) { + if(state == CONNECTED) { List l = PlatformWebRTC.serverLANGetAllEvent(clientId); - if (l == null) { + if(l == null) { return; } Iterator itr = l.iterator(); - while (state == CONNECTED && itr.hasNext()) { + while(state == CONNECTED && itr.hasNext()) { LANPeerEvent evt = itr.next(); - if (evt instanceof LANPeerEvent.LANPeerPacketEvent) { - ClientPlatformSingleplayer - .sendPacket(new IPCPacketData(clientId, ((LANPeerEvent.LANPeerPacketEvent) evt).payload)); - } else if (evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + if(evt instanceof LANPeerEvent.LANPeerPacketEvent) { + ClientPlatformSingleplayer.sendPacket(new IPCPacketData(clientId, ((LANPeerEvent.LANPeerPacketEvent)evt).payload)); + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { logger.info("LAN client '{}' disconnected", clientId); disconnect(); - } else { + }else { logger.error("LAN client '{}' had an accident: {}", clientId, evt.getClass().getSimpleName()); disconnect(); } @@ -178,8 +163,8 @@ class LANClientPeer { } protected void disconnect() { - if (!dead) { - if (state == CONNECTED) { + if(!dead) { + if(state == CONNECTED) { SingleplayerServerController.closePlayerChannel(clientId); } state = CLOSED; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java index fd81cbb..c743baf 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -16,24 +17,16 @@ import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket; import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.*; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -42,7 +35,7 @@ public class LANServerController { public static final Logger logger = LogManager.getLogger("LANServerController"); - public static final List currentICEServers = new ArrayList(); + public static final List currentICEServers = new ArrayList<>(); static RelayServerSocket lanRelaySocket = null; @@ -50,47 +43,46 @@ public class LANServerController { public static String shareToLAN(Consumer progressCallback, String worldName, boolean worldHidden) { currentCode = null; - RelayServerSocket sock = RelayManager.relayManager.getWorkingRelay( - (str) -> progressCallback.accept("Connecting: " + str), + RelayServerSocket sock = RelayManager.relayManager.getWorkingRelay((str) -> progressCallback.accept("Connecting: " + str), RelayManager.preferredRelayVersion, worldName + (worldHidden ? ";1" : ";0")); - if (sock == null) { + if(sock == null) { lanRelaySocket = null; return null; - } else { + }else { progressCallback.accept("Opening: " + sock.getURI()); - IPacket00Handshake hs = (IPacket00Handshake) sock.readPacket(); + RelayPacket00Handshake hs = (RelayPacket00Handshake)sock.readPacket(); lanRelaySocket = sock; String code = hs.connectionCode; logger.info("Relay [{}] connected as 'server', code: {}", sock.getURI(), code); progressCallback.accept("Opened '" + code + "' on " + sock.getURI()); - long millis = System.currentTimeMillis(); + long millis = EagRuntime.steadyTimeMillis(); do { - if (sock.isClosed()) { + sock.update(); + if(sock.isClosed()) { logger.info("Relay [{}] connection lost", sock.getURI()); lanRelaySocket = null; return null; } - IPacket pkt = sock.readPacket(); - if (pkt != null) { - if (pkt instanceof IPacket01ICEServers) { - IPacket01ICEServers ipkt = (IPacket01ICEServers) pkt; + RelayPacket pkt = sock.readPacket(); + if(pkt != null) { + if(pkt instanceof RelayPacket01ICEServers) { + RelayPacket01ICEServers ipkt = (RelayPacket01ICEServers)pkt; logger.info("Relay [{}] provided ICE servers:", sock.getURI()); currentICEServers.clear(); - for (net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.ICEServerSet.RelayServer srv : ipkt.servers) { + for(net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket01ICEServers.RelayServer srv : ipkt.servers) { logger.info("Relay [{}] {}: {}", sock.getURI(), srv.type.name(), srv.address); currentICEServers.add(srv.getICEString()); } - PlatformWebRTC.serverLANInitializeServer( - currentICEServers.toArray(new String[currentICEServers.size()])); + PlatformWebRTC.serverLANInitializeServer(currentICEServers.toArray(new String[currentICEServers.size()])); return currentCode = code; - } else { + }else { logger.error("Relay [{}] unexpected packet: {}", sock.getURI(), pkt.getClass().getSimpleName()); closeLAN(); return null; } } EagUtils.sleep(50l); - } while (System.currentTimeMillis() - millis < 1000l); + }while(EagRuntime.steadyTimeMillis() - millis < 1000l); logger.info("Relay [{}] relay provide ICE servers timeout", sock.getURI()); closeLAN(); return null; @@ -114,7 +106,7 @@ public class LANServerController { } public static void closeLANNoKick() { - if (lanRelaySocket != null) { + if(lanRelaySocket != null) { lanRelaySocket.close(); lanRelaySocket = null; currentCode = null; @@ -123,7 +115,7 @@ public class LANServerController { public static void cleanupLAN() { Iterator itr = clients.values().iterator(); - while (itr.hasNext()) { + while(itr.hasNext()) { itr.next().disconnect(); } clients.clear(); @@ -141,78 +133,73 @@ public class LANServerController { return lanRelaySocket != null; } - private static final Map clients = new HashMap(); + private static final Map clients = new HashMap<>(); public static void updateLANServer() { - if (lanRelaySocket != null) { - IPacket pkt; - while ((pkt = lanRelaySocket.readPacket()) != null) { - if (pkt instanceof IPacket02NewClient) { - IPacket02NewClient ipkt = (IPacket02NewClient) pkt; - if (clients.containsKey(ipkt.clientId)) { - logger.error("Relay [{}] relay provided duplicate client '{}'", lanRelaySocket.getURI(), - ipkt.clientId); - } else { + if(lanRelaySocket != null) { + lanRelaySocket.update(); + RelayPacket pkt; + while((pkt = lanRelaySocket.readPacket()) != null) { + if(pkt instanceof RelayPacket02NewClient) { + RelayPacket02NewClient ipkt = (RelayPacket02NewClient) pkt; + if(clients.containsKey(ipkt.clientId)) { + logger.error("Relay [{}] relay provided duplicate client '{}'", lanRelaySocket.getURI(), ipkt.clientId); + }else { clients.put(ipkt.clientId, new LANClientPeer(ipkt.clientId)); } - } else if (pkt instanceof IPacket03ICECandidate) { - IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt; + }else if(pkt instanceof RelayPacket03ICECandidate) { + RelayPacket03ICECandidate ipkt = (RelayPacket03ICECandidate) pkt; LANClientPeer c = clients.get(ipkt.peerId); - if (c != null) { - c.handleICECandidates(ipkt.candidate); - } else { - logger.error("Relay [{}] relay sent IPacket03ICECandidate for unknown client '{}'", - lanRelaySocket.getURI(), ipkt.peerId); + if(c != null) { + c.handleICECandidates(ipkt.getCandidateString()); + }else { + logger.error("Relay [{}] relay sent IPacket03ICECandidate for unknown client '{}'", lanRelaySocket.getURI(), ipkt.peerId); } - } else if (pkt instanceof IPacket04Description) { - IPacket04Description ipkt = (IPacket04Description) pkt; + }else if(pkt instanceof RelayPacket04Description) { + RelayPacket04Description ipkt = (RelayPacket04Description) pkt; LANClientPeer c = clients.get(ipkt.peerId); - if (c != null) { - c.handleDescription(ipkt.description); - } else { - logger.error("Relay [{}] relay sent IPacket04Description for unknown client '{}'", - lanRelaySocket.getURI(), ipkt.peerId); + if(c != null) { + c.handleDescription(ipkt.getDescriptionString()); + }else { + logger.error("Relay [{}] relay sent IPacket04Description for unknown client '{}'", lanRelaySocket.getURI(), ipkt.peerId); } - } else if (pkt instanceof IPacket05ClientSuccess) { - IPacket05ClientSuccess ipkt = (IPacket05ClientSuccess) pkt; + }else if(pkt instanceof RelayPacket05ClientSuccess) { + RelayPacket05ClientSuccess ipkt = (RelayPacket05ClientSuccess) pkt; LANClientPeer c = clients.get(ipkt.clientId); - if (c != null) { + if(c != null) { c.handleSuccess(); - } else { - logger.error("Relay [{}] relay sent IPacket05ClientSuccess for unknown client '{}'", - lanRelaySocket.getURI(), ipkt.clientId); + }else { + logger.error("Relay [{}] relay sent IPacket05ClientSuccess for unknown client '{}'", lanRelaySocket.getURI(), ipkt.clientId); } - } else if (pkt instanceof IPacket06ClientFailure) { - IPacket06ClientFailure ipkt = (IPacket06ClientFailure) pkt; + }else if(pkt instanceof RelayPacket06ClientFailure) { + RelayPacket06ClientFailure ipkt = (RelayPacket06ClientFailure) pkt; LANClientPeer c = clients.get(ipkt.clientId); - if (c != null) { + if(c != null) { c.handleFailure(); - } else { - logger.error("Relay [{}] relay sent IPacket06ClientFailure for unknown client '{}'", - lanRelaySocket.getURI(), ipkt.clientId); + }else { + logger.error("Relay [{}] relay sent IPacket06ClientFailure for unknown client '{}'", lanRelaySocket.getURI(), ipkt.clientId); } - } else if (pkt instanceof IPacketFFErrorCode) { - IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; - logger.error("Relay [{}] error code thrown: {}({}): {}", lanRelaySocket.getURI(), - IPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc); + }else if(pkt instanceof RelayPacketFFErrorCode) { + RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode) pkt; + logger.error("Relay [{}] error code thrown: {}({}): {}", lanRelaySocket.getURI(), RelayPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc); Throwable t; - while ((t = lanRelaySocket.getException()) != null) { + while((t = lanRelaySocket.getException()) != null) { logger.error(t); } - } else { - logger.error("Relay [{}] unexpected packet: {}", lanRelaySocket.getURI(), - pkt.getClass().getSimpleName()); + }else { + logger.error("Relay [{}] unexpected packet: {}", lanRelaySocket.getURI(), pkt.getClass().getSimpleName()); } + lanRelaySocket.update(); } - if (lanRelaySocket.isClosed()) { + if(lanRelaySocket.isClosed()) { lanRelaySocket = null; } } Iterator itr = clients.values().iterator(); - while (itr.hasNext()) { + while(itr.hasNext()) { LANClientPeer cl = itr.next(); cl.update(); - if (cl.dead) { + if(cl.dead) { itr.remove(); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerList.java index 478aede..d396f6e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerList.java @@ -9,87 +9,81 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServer; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQuery; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket07LocalWorlds; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class LANServerList { - - private final List lanServersList = new LinkedList(); - private final Map lanServersQueryList = new LinkedHashMap(); - private final Set deadURIs = new HashSet(); - + + private final List lanServersList = new LinkedList<>(); + private final Map lanServersQueryList = new LinkedHashMap<>(); + private final Set deadURIs = new HashSet<>(); + private long lastRefresh = 0l; private int refreshCounter = 0; - + public boolean update() { - long millis = System.currentTimeMillis(); - if (millis - lastRefresh > 20000l) { - if (++refreshCounter < 10) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastRefresh > 20000l) { + if(++refreshCounter < 10) { refresh(); - } else { + }else { lastRefresh = millis; } - } else { + }else { boolean changed = false; - Iterator> itr = lanServersQueryList.entrySet().iterator(); - while (itr.hasNext()) { - Entry etr = itr.next(); + Iterator> itr = lanServersQueryList.entrySet().iterator(); + while(itr.hasNext()) { + Entry etr = itr.next(); String uri = etr.getKey(); RelayWorldsQuery q = etr.getValue(); - if (!q.isQueryOpen()) { + q.update(); + if(!q.isQueryOpen()) { itr.remove(); - if (q.isQueryFailed()) { + if(q.isQueryFailed()) { deadURIs.add(uri); Iterator itr2 = lanServersList.iterator(); - while (itr2.hasNext()) { - if (itr2.next().lanServerRelay.address.equals(uri)) { + while(itr2.hasNext()) { + if(itr2.next().lanServerRelay.address.equals(uri)) { itr2.remove(); changed = true; } } - } else { + }else { RelayServer rl = RelayManager.relayManager.getByURI(uri); Iterator itr2 = lanServersList.iterator(); - while (itr2.hasNext()) { + while(itr2.hasNext()) { LanServer l = itr2.next(); - if (l.lanServerRelay.address.equals(uri)) { + if(l.lanServerRelay.address.equals(uri)) { l.flagged = false; } } - if (rl != null) { - Iterator itr3 = q.getWorlds().iterator(); - yee: while (itr3.hasNext()) { - IPacket07LocalWorlds.LocalWorld l = itr3.next(); + if(rl != null) { + Iterator itr3 = q.getWorlds().iterator(); + yee: while(itr3.hasNext()) { + RelayPacket07LocalWorlds.LocalWorld l = itr3.next(); itr2 = lanServersList.iterator(); - while (itr2.hasNext()) { + while(itr2.hasNext()) { LanServer l2 = itr2.next(); - if (l2.lanServerRelay.address.equals(uri) && l2.lanServerCode.equals(l.worldCode)) { + if(l2.lanServerRelay.address.equals(uri) && l2.lanServerCode.equals(l.worldCode)) { l2.lanServerMotd = l.worldName; l2.flagged = true; continue yee; @@ -100,10 +94,10 @@ public class LANServerList { } } itr2 = lanServersList.iterator(); - while (itr2.hasNext()) { + while(itr2.hasNext()) { LanServer l = itr2.next(); - if (l.lanServerRelay.address.equals(uri)) { - if (!l.flagged) { + if(l.lanServerRelay.address.equals(uri)) { + if(!l.flagged) { itr2.remove(); changed = true; } @@ -116,7 +110,7 @@ public class LANServerList { } return false; } - + public void forceRefresh() { deadURIs.clear(); refreshCounter = 0; @@ -124,11 +118,11 @@ public class LANServerList { } private void refresh() { - lastRefresh = System.currentTimeMillis(); - if (PlatformWebRTC.supported()) { - for (int i = 0, l = RelayManager.relayManager.count(); i < l; ++i) { + lastRefresh = EagRuntime.steadyTimeMillis(); + if(PlatformWebRTC.supported()) { + for(int i = 0, l = RelayManager.relayManager.count(); i < l; ++i) { RelayServer srv = RelayManager.relayManager.get(i); - if (!lanServersQueryList.containsKey(srv.address) && !deadURIs.contains(srv.address)) { + if(!lanServersQueryList.containsKey(srv.address) && !deadURIs.contains(srv.address)) { lanServersQueryList.put(srv.address, PlatformWebRTC.openRelayWorldsQuery(srv.address)); } } @@ -142,33 +136,33 @@ public class LANServerList { public int countServers() { return lanServersList.size(); } - + public class LanServer { - + private String lanServerMotd; private RelayServer lanServerRelay; private String lanServerCode; - + protected boolean flagged = true; - + protected LanServer(String lanServerMotd, RelayServer lanServerRelay, String lanServerCode) { this.lanServerMotd = lanServerMotd; this.lanServerRelay = lanServerRelay; this.lanServerCode = lanServerCode; } - + public String getLanServerMotd() { return lanServerMotd; } - + public RelayServer getLanServerRelay() { return lanServerRelay; } - + public String getLanServerCode() { return lanServerCode; } - + } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayLoggerImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayLoggerImpl.java new file mode 100755 index 0000000..306fd4b --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayLoggerImpl.java @@ -0,0 +1,54 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IRelayLogger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayLoggerImpl implements IRelayLogger { + + private final Logger impl; + + public RelayLoggerImpl(Logger impl) { + this.impl = impl; + } + + @Override + public void debug(String msg, Object... args) { + impl.debug(msg, args); + } + + @Override + public void info(String msg, Object... args) { + impl.debug(msg, args); + } + + @Override + public void warn(String msg, Object... args) { + impl.warn(msg, args); + } + + @Override + public void error(String msg, Object... args) { + impl.error(msg, args); + } + + @Override + public void error(Throwable th) { + impl.error(th); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayManager.java index 86778b4..937d580 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayManager.java @@ -14,32 +14,24 @@ import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket00Handshake; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacketFFErrorCode; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacketFFErrorCode; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -50,24 +42,24 @@ public class RelayManager { public static final RelayManager relayManager = new RelayManager(); public static final int preferredRelayVersion = 1; - - private final List relays = new ArrayList(); + + private final List relays = new ArrayList<>(); private long lastPingThrough = 0l; public void load(byte[] relayConfig) { NBTTagCompound relays = null; - if (relayConfig != null) { + if(relayConfig != null) { try { relays = CompressedStreamTools.readCompressed(new EaglerInputStream(relayConfig)); } catch (IOException ex) { } } - if (relays != null && relays.hasKey("relays", 9)) { + if(relays != null && relays.hasKey("relays", 9)) { load(relays.getTagList("relays", 10)); - if (!relays.getBoolean("f")) { + if(!relays.getBoolean("f")) { fixBullshit(); } - } else { + }else { sort(); // loads defaults save(); } @@ -76,14 +68,14 @@ public class RelayManager { // versions pre-u24 always have "relay.deev.is" as primary due to a glitch // this function is intended to randomize the list if that is detected private void fixBullshit() { - if (!relays.isEmpty()) { - for (int i = 0, l = relays.size(); i < l; ++i) { + if(!relays.isEmpty()) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer rs = relays.get(i); - if (rs.address.equalsIgnoreCase("wss://relay.deev.is/") && !rs.isPrimary()) { + if(rs.address.equalsIgnoreCase("wss://relay.deev.is/") && !rs.isPrimary()) { return; } } - for (int i = 0, l = relays.size(); i < l; ++i) { + for(int i = 0, l = relays.size(); i < l; ++i) { relays.get(i).setPrimary(false); } relays.get(ThreadLocalRandom.current().nextInt(relays.size())).setPrimary(true); @@ -94,15 +86,15 @@ public class RelayManager { private void load(NBTTagList relayConfig) { relays.clear(); - if (relayConfig != null && relayConfig.tagCount() > 0) { + if(relayConfig != null && relayConfig.tagCount() > 0) { boolean gotAPrimary = false; - for (int i = 0, l = relayConfig.tagCount(); i < l; ++i) { + for(int i = 0, l = relayConfig.tagCount(); i < l; ++i) { NBTTagCompound relay = relayConfig.getCompoundTagAt(i); boolean p = relay.getBoolean("primary"); - if (p) { - if (gotAPrimary) { + if(p) { + if(gotAPrimary) { p = false; - } else { + }else { gotAPrimary = true; } } @@ -111,21 +103,21 @@ public class RelayManager { } sort(); } - + public void save() { - if (relays.isEmpty()) { + if(relays.isEmpty()) { return; } byte[] data = write(); - if (data != null) { + if(data != null) { EagRuntime.setStorage("r", data); } } - + public byte[] write() { try { NBTTagList lst = new NBTTagList(); - for (int i = 0, l = relays.size(); i < l; ++i) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer srv = relays.get(i); NBTTagCompound etr = new NBTTagCompound(); etr.setString("addr", srv.address); @@ -147,83 +139,83 @@ public class RelayManager { return null; } } - + private void sort() { - if (relays.isEmpty()) { + if(relays.isEmpty()) { List defaultRelays = EagRuntime.getConfiguration().getRelays(); - for (int i = 0, l = defaultRelays.size(); i < l; ++i) { + for(int i = 0, l = defaultRelays.size(); i < l; ++i) { relays.add(new RelayServer(defaultRelays.get(i))); } } - if (relays.isEmpty()) { + if(relays.isEmpty()) { return; } int j = -1; - for (int i = 0, l = relays.size(); i < l; ++i) { - if (relays.get(i).isPrimary()) { - if (j == -1) { + for(int i = 0, l = relays.size(); i < l; ++i) { + if(relays.get(i).isPrimary()) { + if(j == -1) { j = i; - } else { + }else { relays.get(i).setPrimary(false); } } } - if (j == -1) { + if(j == -1) { boolean found = false; - for (int i = 0, l = relays.size(); i < l; ++i) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer srv = relays.get(i); - if (srv.getPing() > 0l) { + if(srv.getPing() > 0l) { found = true; srv.setPrimary(true); break; } } - if (!found) { + if(!found) { relays.get(0).setPrimary(true); } - } else { + }else { RelayServer srv = relays.remove(j); relays.add(0, srv); } } - + public void ping() { - lastPingThrough = System.currentTimeMillis(); - for (int i = 0, l = relays.size(); i < l; ++i) { + lastPingThrough = EagRuntime.steadyTimeMillis(); + for(int i = 0, l = relays.size(); i < l; ++i) { relays.get(i).ping(); } } - + public void update() { - for (int i = 0, l = relays.size(); i < l; ++i) { + for(int i = 0, l = relays.size(); i < l; ++i) { relays.get(i).update(); } } - + public void close() { - for (int i = 0, l = relays.size(); i < l; ++i) { + for(int i = 0, l = relays.size(); i < l; ++i) { relays.get(i).close(); } } - + public int count() { return relays.size(); } - + public RelayServer get(int idx) { return relays.get(idx); } - + public void add(String addr, String comment, boolean primary) { lastPingThrough = 0l; int i = relays.size(); relays.add(new RelayServer(addr, comment, false)); - if (primary) { + if(primary) { setPrimary0(i); } save(); } - + public void addNew(String addr, String comment, boolean primary) { lastPingThrough = 0l; int i = relays.size(); @@ -231,22 +223,22 @@ public class RelayManager { RelayServer newServer = new RelayServer(addr, comment, false); relays.add(j, newServer); newServer.ping(); - if (primary) { + if(primary) { setPrimary0(j); } save(); } - + public void setPrimary(int idx) { setPrimary0(idx); save(); } private void setPrimary0(int idx) { - if (idx >= 0 && idx < relays.size()) { - for (int i = 0, l = relays.size(); i < l; ++i) { + if(idx >= 0 && idx < relays.size()) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer srv = relays.get(i); - if (srv.isPrimary()) { + if(srv.isPrimary()) { srv.setPrimary(false); } } @@ -255,53 +247,53 @@ public class RelayManager { relays.add(0, pr); } } - + public void remove(int idx) { RelayServer srv = relays.remove(idx); srv.close(); sort(); save(); } - + public RelayServer getPrimary() { - if (!relays.isEmpty()) { - for (int i = 0, l = relays.size(); i < l; ++i) { + if(!relays.isEmpty()) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer srv = relays.get(i); - if (srv.isPrimary()) { + if(srv.isPrimary()) { return srv; } } sort(); save(); return getPrimary(); - } else { + }else { return null; } } - + public RelayServerSocket connectHandshake(RelayServer relay, int type, String code) { RelayServerSocket sock = relay.openSocket(); - while (!sock.isClosed()) { - if (sock.isOpen()) { - sock.writePacket(new IPacket00Handshake(type, preferredRelayVersion, code)); - while (!sock.isClosed()) { - IPacket pkt = sock.nextPacket(); - if (pkt != null) { - if (pkt instanceof IPacket00Handshake) { + while(!sock.isClosed()) { + sock.update(); + if(sock.isOpen()) { + sock.writePacket(new RelayPacket00Handshake(type, preferredRelayVersion, code)); + while(!sock.isClosed()) { + sock.update(); + RelayPacket pkt = sock.nextPacket(); + if(pkt != null) { + if(pkt instanceof RelayPacket00Handshake) { return sock; - } else if (pkt instanceof IPacketFFErrorCode) { - IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; - logger.error("Relay [{}] failed: {}({}): {}", relay.address, - IPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc); + }else if(pkt instanceof RelayPacketFFErrorCode) { + RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode) pkt; + logger.error("Relay [{}] failed: {}({}): {}", relay.address, RelayPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc); Throwable t; - while ((t = sock.getException()) != null) { + while((t = sock.getException()) != null) { logger.error(t); } sock.close(); return null; - } else { - logger.error("Relay [{}] unexpected packet: {}", relay.address, - pkt.getClass().getSimpleName()); + }else { + logger.error("Relay [{}] unexpected packet: {}", relay.address, pkt.getClass().getSimpleName()); sock.close(); return null; } @@ -313,42 +305,42 @@ public class RelayManager { } logger.error("Relay [{}] connection failed!", relay.address); Throwable t; - while ((t = sock.getException()) != null) { + while((t = sock.getException()) != null) { logger.error(t); } return null; } - - private final List brokenServers = new LinkedList(); + + private final List brokenServers = new LinkedList<>(); public RelayServerSocket getWorkingRelay(Consumer progressCallback, int type, String code) { brokenServers.clear(); - if (!relays.isEmpty()) { - long millis = System.currentTimeMillis(); - if (millis - lastPingThrough < 10000l) { + if(!relays.isEmpty()) { + long millis = EagRuntime.steadyTimeMillis(); + if(millis - lastPingThrough < 10000l) { RelayServer relay = getPrimary(); - if (relay.getPing() > 0l && relay.getPingCompatible().isCompatible()) { + if(relay.getPing() > 0l && relay.getPingCompatible().isCompatible()) { progressCallback.accept(relay.address); RelayServerSocket sock = connectHandshake(relay, type, code); - if (sock != null) { - if (!sock.isFailed()) { + if(sock != null) { + if(!sock.isFailed()) { return sock; } - } else { + }else { brokenServers.add(relay); } } - for (int i = 0, l = relays.size(); i < l; ++i) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer relayEtr = relays.get(i); - if (relayEtr != relay) { - if (relayEtr.getPing() > 0l && relayEtr.getPingCompatible().isCompatible()) { + if(relayEtr != relay) { + if(relayEtr.getPing() > 0l && relayEtr.getPingCompatible().isCompatible()) { progressCallback.accept(relayEtr.address); RelayServerSocket sock = connectHandshake(relayEtr, type, code); - if (sock != null) { - if (!sock.isFailed()) { + if(sock != null) { + if(!sock.isFailed()) { return sock; } - } else { + }else { brokenServers.add(relayEtr); } } @@ -356,39 +348,39 @@ public class RelayManager { } } return getWorkingCodeRelayActive(progressCallback, type, code); - } else { + }else { return null; } } - + private RelayServerSocket getWorkingCodeRelayActive(Consumer progressCallback, int type, String code) { - if (!relays.isEmpty()) { - for (int i = 0, l = relays.size(); i < l; ++i) { + if(!relays.isEmpty()) { + for(int i = 0, l = relays.size(); i < l; ++i) { RelayServer srv = relays.get(i); - if (!brokenServers.contains(srv)) { + if(!brokenServers.contains(srv)) { progressCallback.accept(srv.address); RelayServerSocket sock = connectHandshake(srv, type, code); - if (sock != null) { - if (!sock.isFailed()) { + if(sock != null) { + if(!sock.isFailed()) { return sock; } - } else { + }else { brokenServers.add(srv); } } } return null; - } else { + }else { return null; } } - + public void loadDefaults() { List defaultRelays = EagRuntime.getConfiguration().getRelays(); - eee: for (int i = 0, l = defaultRelays.size(); i < l; ++i) { + eee: for(int i = 0, l = defaultRelays.size(); i < l; ++i) { RelayEntry etr = defaultRelays.get(i); - for (int j = 0, l2 = relays.size(); j < l2; ++j) { - if (relays.get(j).address.equalsIgnoreCase(etr.address)) { + for(int j = 0, l2 = relays.size(); j < l2; ++j) { + if(relays.get(j).address.equalsIgnoreCase(etr.address)) { continue eee; } } @@ -396,17 +388,17 @@ public class RelayManager { } sort(); } - + public String makeNewRelayName() { String str = "Relay Server #" + (relays.size() + 1); - for (int i = relays.size() + 2, l = relays.size() + 50; i < l; ++i) { - if (str.equalsIgnoreCase("Relay Server #" + i)) { + for(int i = relays.size() + 2, l = relays.size() + 50; i < l; ++i) { + if(str.equalsIgnoreCase("Relay Server #" + i)) { str = "Relay Server #" + (i + 1); } } - eee: while (true) { - for (int i = 0, l = relays.size(); i < l; ++i) { - if (str.equalsIgnoreCase(relays.get(i).comment)) { + eee: while(true) { + for(int i = 0, l = relays.size(); i < l; ++i) { + if(str.equalsIgnoreCase(relays.get(i).comment)) { str = str + "_"; continue eee; } @@ -415,16 +407,16 @@ public class RelayManager { } return str; } - + public RelayServer getByURI(String uri) { Iterator itr = relays.iterator(); - while (itr.hasNext()) { + while(itr.hasNext()) { RelayServer rl = itr.next(); - if (rl.address.equals(uri)) { + if(rl.address.equals(uri)) { return rl; } } return null; } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQuery.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQuery.java index 72cb55d..05e24a2 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQuery.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQuery.java @@ -1,24 +1,16 @@ package net.lax1dude.eaglercraft.v1_8.sp.relay; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -31,26 +23,20 @@ public interface RelayQuery { enum VersionMismatch { COMPATIBLE, CLIENT_OUTDATED, RELAY_OUTDATED, UNKNOWN; - public boolean isCompatible() { return this == COMPATIBLE; } } + void update(); boolean isQueryOpen(); - boolean isQueryFailed(); - RateLimit isQueryRateLimit(); - void close(); - + int getVersion(); - String getComment(); - String getBrand(); - long getPing(); VersionMismatch getCompatible(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQueryImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQueryImpl.java new file mode 100755 index 0000000..7ec3b58 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQueryImpl.java @@ -0,0 +1,225 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket69Pong; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket70SpecialUpdate; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacketFFErrorCode; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayQueryImpl implements RelayQuery { + + private static final Logger logger = LogManager.getLogger("RelayQuery"); + private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket")); + + private final IWebSocketClient sock; + private final String uri; + + private boolean failed; + + private boolean hasSentHandshake = false; + private boolean hasRecievedAnyData = false; + + private int vers = -1; + private String comment = ""; + private String brand = ""; + + private long connectionOpenedAt; + private long connectionPingStart = -1; + private long connectionPingTimer = -1; + + private RateLimit rateLimitStatus = RateLimit.NONE; + + private VersionMismatch versError = VersionMismatch.UNKNOWN; + + public RelayQueryImpl(String uri) { + this.uri = uri; + IWebSocketClient s; + try { + connectionOpenedAt = EagRuntime.steadyTimeMillis(); + s = PlatformNetworking.openWebSocketUnsafe(uri); + }catch(Throwable t) { + connectionOpenedAt = 0l; + sock = null; + failed = true; + return; + } + sock = s; + + } + + @Override + public void update() { + if(sock == null) return; + if(sock.availableStringFrames() > 0) { + logger.warn("[{}] discarding {} string frames recieved on a binary connection", uri, sock.availableStringFrames()); + sock.clearStringFrames(); + } + List frames = sock.getNextBinaryFrames(); + if(frames != null) { + for(int i = 0, l = frames.size(); i < l; ++i) { + hasRecievedAnyData = true; + byte[] arr = frames.get(i).getByteArray(); + if(arr.length == 2 && arr[0] == (byte)0xFC) { + if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) { + rateLimitStatus = RateLimit.BLOCKED; + RelayServerRateLimitTracker.setLimited(RelayQueryImpl.this.uri); + }else if(arr[1] == (byte)0x02) { + rateLimitStatus = RateLimit.NOW_LOCKED; + RelayServerRateLimitTracker.setLimitedLocked(RelayQueryImpl.this.uri); + }else { + rateLimitStatus = RateLimit.LOCKED; + RelayServerRateLimitTracker.setLocked(RelayQueryImpl.this.uri); + } + failed = true; + sock.close(); + }else { + try { + RelayPacket pkt = RelayPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)), loggerImpl); + if(pkt instanceof RelayPacket69Pong) { + RelayPacket69Pong ipkt = (RelayPacket69Pong)pkt; + versError = VersionMismatch.COMPATIBLE; + if(connectionPingTimer == -1) { + connectionPingTimer = frames.get(i).getTimestamp() - connectionPingStart; + } + vers = ipkt.protcolVersion; + comment = ipkt.comment; + brand = ipkt.brand; + failed = false; + sock.close(); + return; + }else if(pkt instanceof RelayPacket70SpecialUpdate) { + RelayPacket70SpecialUpdate ipkt = (RelayPacket70SpecialUpdate)pkt; + if(ipkt.operation == RelayPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { + UpdateService.addCertificateToSet(ipkt.updatePacket); + } + }else if(pkt instanceof RelayPacketFFErrorCode) { + RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode)pkt; + if(ipkt.code == RelayPacketFFErrorCode.TYPE_PROTOCOL_VERSION) { + String s1 = ipkt.desc.toLowerCase(); + if(s1.contains("outdated client") || s1.contains("client outdated")) { + versError = VersionMismatch.CLIENT_OUTDATED; + }else if(s1.contains("outdated server") || s1.contains("server outdated") || + s1.contains("outdated relay") || s1.contains("server relay")) { + versError = VersionMismatch.RELAY_OUTDATED; + }else { + versError = VersionMismatch.UNKNOWN; + } + } + logger.error("[{}] Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc); + failed = true; + sock.close(); + return; + }else { + throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'"); + } + } catch (IOException e) { + logger.error("Relay query error: {}", e.toString()); + logger.error(e); + failed = true; + sock.close(); + return; + } + } + } + } + if(sock.isOpen() && !hasSentHandshake) { + hasSentHandshake = true; + try { + connectionPingStart = EagRuntime.steadyTimeMillis(); + sock.send(RelayPacket.writePacket(new RelayPacket00Handshake(0x03, RelayManager.preferredRelayVersion, ""), loggerImpl)); + } catch (IOException e) { + logger.error("Failed to write handshake: {}", e.toString()); + logger.error(e); + sock.close(); + failed = true; + } + } + if(sock.isClosed()) { + if(!hasRecievedAnyData) { + failed = true; + rateLimitStatus = RelayServerRateLimitTracker.isLimitedLong(uri); + } + } + if(EagRuntime.steadyTimeMillis() - connectionOpenedAt > 10000l) { + logger.error("Terminating connection that was open for too long: {}", uri); + sock.close(); + failed = true; + } + } + + @Override + public boolean isQueryOpen() { + return sock != null && !sock.isClosed(); + } + + @Override + public boolean isQueryFailed() { + return failed || sock == null || sock.getState() == EnumEaglerConnectionState.FAILED; + } + + @Override + public RateLimit isQueryRateLimit() { + return rateLimitStatus; + } + + @Override + public void close() { + if(sock != null) { + sock.close(); + } + } + + @Override + public int getVersion() { + return vers; + } + + @Override + public String getComment() { + return comment; + } + + @Override + public String getBrand() { + return brand; + } + + @Override + public long getPing() { + return connectionPingTimer < 1 ? 1 : connectionPingTimer; + } + + @Override + public VersionMismatch getCompatible() { + return versError; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQueryRateLimitDummy.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQueryRateLimitDummy.java new file mode 100755 index 0000000..c740fa9 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayQueryRateLimitDummy.java @@ -0,0 +1,75 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayQueryRateLimitDummy implements RelayQuery { + + private final RateLimit type; + + public RelayQueryRateLimitDummy(RateLimit type) { + this.type = type; + } + + @Override + public void update() { + + } + + @Override + public boolean isQueryOpen() { + return false; + } + + @Override + public boolean isQueryFailed() { + return true; + } + + @Override + public RateLimit isQueryRateLimit() { + return type; + } + + @Override + public void close() { + } + + @Override + public int getVersion() { + return RelayManager.preferredRelayVersion; + } + + @Override + public String getComment() { + return "this query was rate limited"; + } + + @Override + public String getBrand() { + return "lax1dude"; + } + + @Override + public long getPing() { + return 0l; + } + + @Override + public VersionMismatch getCompatible() { + return VersionMismatch.COMPATIBLE; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServer.java index da0908f..4ccf839 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServer.java @@ -1,39 +1,32 @@ package net.lax1dude.eaglercraft.v1_8.sp.relay; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery.VersionMismatch; import net.minecraft.client.Minecraft; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class RelayServer { - + public final String address; public final String comment; private boolean primary; - + private RelayQuery query = null; private int queriedVersion = -1; private String queriedComment; @@ -48,15 +41,15 @@ public class RelayServer { this.comment = comment; this.primary = primary; } - + public RelayServer(RelayEntry etr) { this(etr.address, etr.comment, etr.primary); } - + public boolean isPrimary() { return primary; } - + public void setPrimary(boolean primaryee) { primary = primaryee; } @@ -68,33 +61,33 @@ public class RelayServer { public long getWorkingPing() { return workingPing; } - + public int getPingVersion() { return queriedVersion; } - + public String getPingComment() { return queriedComment == null ? "" : queriedComment; } - + public String getPingVendor() { return queriedVendor == null ? "" : queriedVendor; } - + public VersionMismatch getPingCompatible() { return queriedCompatible; } - + public void pingBlocking() { ping(); - while (getPing() < 0l) { + while(getPing() < 0l) { EagUtils.sleep(250l); update(); } } - + public void ping() { - if (PlatformWebRTC.supported()) { + if(PlatformWebRTC.supported()) { close(); query = PlatformWebRTC.openRelayQuery(address); queriedVersion = -1; @@ -102,7 +95,7 @@ public class RelayServer { queriedVendor = null; queriedCompatible = VersionMismatch.UNKNOWN; ping = -1l; - } else { + }else { query = null; queriedVersion = 1; queriedComment = "LAN NOT SUPPORTED"; @@ -111,30 +104,33 @@ public class RelayServer { ping = -1l; } } - + public void update() { - if (query != null && !query.isQueryOpen()) { - if (query.isQueryFailed()) { - queriedVersion = -1; - queriedComment = null; - queriedVendor = null; - queriedCompatible = VersionMismatch.UNKNOWN; - ping = 0l; - } else { - queriedVersion = query.getVersion(); - queriedComment = query.getComment(); - queriedVendor = query.getBrand(); - ping = query.getPing(); - queriedCompatible = query.getCompatible(); - workingPing = ping; + if(query != null) { + query.update(); + if(!query.isQueryOpen()) { + if(query.isQueryFailed()) { + queriedVersion = -1; + queriedComment = null; + queriedVendor = null; + queriedCompatible = VersionMismatch.UNKNOWN; + ping = 0l; + }else { + queriedVersion = query.getVersion(); + queriedComment = query.getComment(); + queriedVendor = query.getBrand(); + ping = query.getPing(); + queriedCompatible = query.getCompatible(); + workingPing = ping; + } + lastPing = EagRuntime.steadyTimeMillis(); + query = null; } - lastPing = System.currentTimeMillis(); - query = null; } } - + public void close() { - if (query != null && query.isQueryOpen()) { + if(query != null && query.isQueryOpen()) { query.close(); query = null; queriedVersion = -1; @@ -144,9 +140,9 @@ public class RelayServer { ping = 0l; } } - + public RelayServerSocket openSocket() { return PlatformWebRTC.openRelayConnection(address, Minecraft.getMinecraft().gameSettings.relayTimeout * 1000); } - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerRateLimitTracker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerRateLimitTracker.java new file mode 100755 index 0000000..0ecca12 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerRateLimitTracker.java @@ -0,0 +1,98 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import java.util.HashMap; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayServerRateLimitTracker { + + private static final Map relayQueryLimited = new HashMap<>(); + private static final Map relayQueryLocked = new HashMap<>(); + + public static void setLimited(String str) { + synchronized(relayQueryLimited) { + relayQueryLimited.put(str, EagRuntime.steadyTimeMillis()); + } + } + + public static void setLocked(String str) { + synchronized(relayQueryLocked) { + relayQueryLocked.put(str, EagRuntime.steadyTimeMillis()); + } + } + + public static void setLimitedLocked(String str) { + long now = EagRuntime.steadyTimeMillis(); + synchronized(relayQueryLimited) { + relayQueryLimited.put(str, now); + } + synchronized(relayQueryLocked) { + relayQueryLocked.put(str, now); + } + } + + public static RelayQuery.RateLimit isLimited(String str) { + long now = EagRuntime.steadyTimeMillis(); + synchronized(relayQueryLocked) { + Long l = relayQueryLocked.get(str); + if(l != null && now - l.longValue() < 60000l) { + return RelayQuery.RateLimit.LOCKED; + } + } + synchronized(relayQueryLimited) { + Long l = relayQueryLimited.get(str); + if(l != null && now - l.longValue() < 10000l) { + return RelayQuery.RateLimit.BLOCKED; + } + } + return RelayQuery.RateLimit.NONE; + } + + public static RelayQuery.RateLimit isLimitedLong(String str) { + long now = EagRuntime.steadyTimeMillis(); + synchronized(relayQueryLocked) { + Long l = relayQueryLocked.get(str); + if(l != null && now - l.longValue() < 400000l) { + return RelayQuery.RateLimit.LOCKED; + } + } + synchronized(relayQueryLimited) { + Long l = relayQueryLimited.get(str); + if(l != null && now - l.longValue() < 900000l) { + return RelayQuery.RateLimit.BLOCKED; + } + } + return RelayQuery.RateLimit.NONE; + } + + public static RelayQuery.RateLimit isLimitedEver(String str) { + synchronized(relayQueryLocked) { + if(relayQueryLocked.containsKey(str)) { + return RelayQuery.RateLimit.LOCKED; + } + } + synchronized(relayQueryLimited) { + if(relayQueryLimited.containsKey(str)) { + return RelayQuery.RateLimit.BLOCKED; + } + } + return RelayQuery.RateLimit.NONE; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocket.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocket.java index 3e87d7d..e5fdbf0 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocket.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocket.java @@ -1,50 +1,39 @@ package net.lax1dude.eaglercraft.v1_8.sp.relay; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public interface RelayServerSocket { + void update(); boolean isOpen(); - boolean isClosed(); - void close(); - + boolean isFailed(); - Throwable getException(); - - void writePacket(IPacket pkt); - - IPacket readPacket(); - - IPacket nextPacket(); - + + void writePacket(RelayPacket pkt); + + RelayPacket readPacket(); + RelayPacket nextPacket(); + RelayQuery.RateLimit getRatelimitHistory(); - + String getURI(); - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocketImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocketImpl.java new file mode 100755 index 0000000..7c0fb50 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocketImpl.java @@ -0,0 +1,173 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket70SpecialUpdate; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayServerSocketImpl implements RelayServerSocket { + + private static final Logger logger = LogManager.getLogger("RelayServerSocket"); + private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket")); + + private final IWebSocketClient sock; + private final String uri; + + private boolean hasRecievedAnyData = false; + private boolean failed = false; + + private final List exceptions = new LinkedList<>(); + private final List packets = new LinkedList<>(); + + public RelayServerSocketImpl(String uri, int timeout) { + this.uri = uri; + IWebSocketClient s; + try { + s = PlatformNetworking.openWebSocketUnsafe(uri); + }catch(Throwable t) { + exceptions.add(t); + sock = null; + failed = true; + return; + } + sock = s; + } + + @Override + public void update() { + if(sock == null) return; + if(sock.availableStringFrames() > 0) { + logger.warn("[{}] discarding {} string frames recieved on a binary connection", uri, sock.availableStringFrames()); + sock.clearStringFrames(); + } + List frames = sock.getNextBinaryFrames(); + if(frames != null) { + for(int i = 0, l = frames.size(); i < l; ++i) { + hasRecievedAnyData = true; + try { + RelayPacket pkt = RelayPacket.readPacket(new DataInputStream(frames.get(i).getInputStream()), loggerImpl); + if(pkt instanceof RelayPacket70SpecialUpdate) { + RelayPacket70SpecialUpdate ipkt = (RelayPacket70SpecialUpdate)pkt; + if(ipkt.operation == RelayPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { + UpdateService.addCertificateToSet(ipkt.updatePacket); + } + }else { + packets.add(pkt); + } + } catch (IOException e) { + exceptions.add(e); + logger.error("[{}] Relay Socket Error: {}", uri, e.toString()); + EagRuntime.debugPrintStackTrace(e); + failed = true; + sock.close(); + return; + } + } + } + if(sock.isClosed()) { + if (!hasRecievedAnyData) { + failed = true; + } + } + } + + @Override + public boolean isOpen() { + return sock != null && sock.isOpen(); + } + + @Override + public boolean isClosed() { + return sock == null || sock.isClosed(); + } + + @Override + public void close() { + if(sock != null) { + sock.close(); + } + } + + @Override + public boolean isFailed() { + return failed || sock == null || sock.getState() == EnumEaglerConnectionState.FAILED; + } + + @Override + public Throwable getException() { + if(!exceptions.isEmpty()) { + return exceptions.remove(0); + }else { + return null; + } + } + + @Override + public void writePacket(RelayPacket pkt) { + if(sock != null) { + try { + sock.send(RelayPacket.writePacket(pkt, loggerImpl)); + } catch (Throwable e) { + logger.error("Relay connection error: {}", e.toString()); + EagRuntime.debugPrintStackTrace(e); + exceptions.add(e); + sock.close(); + } + } + } + + @Override + public RelayPacket readPacket() { + if(!packets.isEmpty()) { + return packets.remove(0); + }else { + return null; + } + } + + @Override + public RelayPacket nextPacket() { + if(!packets.isEmpty()) { + return packets.get(0); + }else { + return null; + } + } + + @Override + public RelayQuery.RateLimit getRatelimitHistory() { + return RelayServerRateLimitTracker.isLimitedEver(uri); + } + + @Override + public String getURI() { + return uri; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocketRateLimitDummy.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocketRateLimitDummy.java new file mode 100755 index 0000000..6e052e1 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayServerSocketRateLimitDummy.java @@ -0,0 +1,80 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayServerSocketRateLimitDummy implements RelayServerSocket { + + private final RelayQuery.RateLimit limit; + + public RelayServerSocketRateLimitDummy(RelayQuery.RateLimit limit) { + this.limit = limit; + } + + @Override + public void update() { + } + + @Override + public boolean isOpen() { + return false; + } + + @Override + public boolean isClosed() { + return true; + } + + @Override + public void close() { + } + + @Override + public boolean isFailed() { + return true; + } + + @Override + public Throwable getException() { + return null; + } + + @Override + public void writePacket(RelayPacket pkt) { + } + + @Override + public RelayPacket readPacket() { + return null; + } + + @Override + public RelayPacket nextPacket() { + return null; + } + + @Override + public RelayQuery.RateLimit getRatelimitHistory() { + return limit; + } + + @Override + public String getURI() { + return ""; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQuery.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQuery.java index 4588070..ae461fc 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQuery.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQuery.java @@ -3,43 +3,33 @@ package net.lax1dude.eaglercraft.v1_8.sp.relay; import java.util.List; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery.VersionMismatch; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket07LocalWorlds; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public interface RelayWorldsQuery { + void update(); boolean isQueryOpen(); - boolean isQueryFailed(); - RelayQuery.RateLimit isQueryRateLimit(); - void close(); - - List getWorlds(); - + + List getWorlds(); + VersionMismatch getCompatible(); - + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQueryImpl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQueryImpl.java new file mode 100755 index 0000000..d849d12 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQueryImpl.java @@ -0,0 +1,197 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery.RateLimit; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket70SpecialUpdate; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacketFFErrorCode; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayWorldsQueryImpl implements RelayWorldsQuery { + + private static final Logger logger = LogManager.getLogger("RelayWorldsQuery"); + private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket")); + + private final IWebSocketClient sock; + private final String uri; + + private boolean failed; + + private long openedAt; + + private boolean hasSentHandshake = false; + private boolean hasRecievedAnyData = false; + private RelayQuery.RateLimit rateLimitStatus = RelayQuery.RateLimit.NONE; + + private RelayQuery.VersionMismatch versError = RelayQuery.VersionMismatch.UNKNOWN; + + private List worlds = null; + + public RelayWorldsQueryImpl(String uri) { + this.uri = uri; + IWebSocketClient s; + try { + openedAt = EagRuntime.steadyTimeMillis(); + s = PlatformNetworking.openWebSocketUnsafe(uri); + }catch(Throwable t) { + sock = null; + failed = true; + return; + } + sock = s; + } + + @Override + public void update() { + if(sock == null) return; + if(sock.availableStringFrames() > 0) { + logger.warn("[{}] discarding {} string frames recieved on a binary connection", uri, sock.availableStringFrames()); + sock.clearStringFrames(); + } + List frames = sock.getNextBinaryFrames(); + if(frames != null) { + for(int i = 0, l = frames.size(); i < l; ++i) { + hasRecievedAnyData = true; + byte[] arr = frames.get(i).getByteArray(); + if(arr.length == 2 && arr[0] == (byte)0xFC) { + if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) { + rateLimitStatus = RateLimit.BLOCKED; + RelayServerRateLimitTracker.setLimited(RelayWorldsQueryImpl.this.uri); + }else if(arr[1] == (byte)0x02) { + rateLimitStatus = RateLimit.NOW_LOCKED; + RelayServerRateLimitTracker.setLimitedLocked(RelayWorldsQueryImpl.this.uri); + }else { + rateLimitStatus = RateLimit.LOCKED; + RelayServerRateLimitTracker.setLocked(RelayWorldsQueryImpl.this.uri); + } + failed = true; + sock.close(); + }else { + try { + RelayPacket pkt = RelayPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)), loggerImpl); + if(pkt instanceof RelayPacket07LocalWorlds) { + worlds = ((RelayPacket07LocalWorlds)pkt).worldsList; + sock.close(); + failed = false; + return; + }else if(pkt instanceof RelayPacket70SpecialUpdate) { + RelayPacket70SpecialUpdate ipkt = (RelayPacket70SpecialUpdate)pkt; + if(ipkt.operation == RelayPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { + UpdateService.addCertificateToSet(ipkt.updatePacket); + } + }else if(pkt instanceof RelayPacketFFErrorCode) { + RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode)pkt; + if(ipkt.code == RelayPacketFFErrorCode.TYPE_PROTOCOL_VERSION) { + String s1 = ipkt.desc.toLowerCase(); + if(s1.contains("outdated client") || s1.contains("client outdated")) { + versError = RelayQuery.VersionMismatch.CLIENT_OUTDATED; + }else if(s1.contains("outdated server") || s1.contains("server outdated") || + s1.contains("outdated relay") || s1.contains("server relay")) { + versError = RelayQuery.VersionMismatch.RELAY_OUTDATED; + }else { + versError = RelayQuery.VersionMismatch.UNKNOWN; + } + } + logger.error("[{}] Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc); + failed = true; + sock.close(); + return; + }else { + throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'"); + } + } catch (IOException e) { + logger.error("Relay World Query Error: {}", e.toString()); + EagRuntime.debugPrintStackTrace(e); + failed = true; + sock.close(); + return; + } + } + } + } + if(sock.isOpen() && !hasSentHandshake) { + hasSentHandshake = true; + try { + sock.send(RelayPacket.writePacket(new RelayPacket00Handshake(0x04, RelayManager.preferredRelayVersion, ""), loggerImpl)); + } catch (IOException e) { + logger.error("Failed to write handshake: {}", e.toString()); + logger.error(e); + sock.close(); + failed = true; + } + } + if(sock.isClosed()) { + if(!hasRecievedAnyData) { + failed = true; + rateLimitStatus = RelayServerRateLimitTracker.isLimitedLong(uri); + } + }else { + if(EagRuntime.steadyTimeMillis() - openedAt > 10000l) { + logger.error("Terminating connection that was open for too long: {}", uri); + sock.close(); + failed = true; + } + } + } + + @Override + public boolean isQueryOpen() { + return sock != null && !sock.isClosed(); + } + + @Override + public boolean isQueryFailed() { + return failed || sock == null || sock.getState() == EnumEaglerConnectionState.FAILED; + } + + @Override + public RelayQuery.RateLimit isQueryRateLimit() { + return rateLimitStatus; + } + + @Override + public void close() { + if(sock != null) { + sock.close(); + } + } + + @Override + public List getWorlds() { + return worlds; + } + + @Override + public RelayQuery.VersionMismatch getCompatible() { + return versError; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQueryRateLimitDummy.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQueryRateLimitDummy.java new file mode 100755 index 0000000..cb802f3 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/RelayWorldsQueryRateLimitDummy.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay; + +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayWorldsQueryRateLimitDummy implements RelayWorldsQuery { + + private final RelayQuery.RateLimit rateLimit; + + public RelayWorldsQueryRateLimitDummy(RelayQuery.RateLimit rateLimit) { + this.rateLimit = rateLimit; + } + + @Override + public void update() { + } + + @Override + public boolean isQueryOpen() { + return false; + } + + @Override + public boolean isQueryFailed() { + return true; + } + + @Override + public RelayQuery.RateLimit isQueryRateLimit() { + return rateLimit; + } + + @Override + public void close() { + } + + @Override + public List getWorlds() { + return new ArrayList<>(0); + } + + @Override + public RelayQuery.VersionMismatch getCompatible() { + return RelayQuery.VersionMismatch.COMPATIBLE; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/ICEServerSet.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/ICEServerSet.java deleted file mode 100644 index 2ac4b9d..0000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/ICEServerSet.java +++ /dev/null @@ -1,55 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ICEServerSet { - - public static enum RelayType { - STUN, TURN; - } - - public static class RelayServer { - - public final RelayType type; - public final String address; - public final String username; - public final String password; - - protected RelayServer(RelayType type, String address, String username, String password) { - this.type = type; - this.address = address; - this.username = username; - this.password = password; - } - - protected RelayServer(RelayType type, String address) { - this.type = type; - this.address = address; - this.username = null; - this.password = null; - } - - public String getICEString() { - if(username == null) { - return address; - }else { - return address + ";" + username + ";" + password; - } - } - - } - -} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket01ICEServers.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket01ICEServers.java deleted file mode 100644 index 7d32761..0000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket01ICEServers.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket01ICEServers extends IPacket { - - public final Collection servers; - - public IPacket01ICEServers() { - servers = new ArrayList(); - } - - public void read(DataInputStream input) throws IOException { - servers.clear(); - int l = input.readUnsignedShort(); - for(int i = 0; i < l; ++i) { - char type = (char)input.read(); - ICEServerSet.RelayType typeEnum; - if(type == 'S') { - typeEnum = ICEServerSet.RelayType.STUN; - }else if(type == 'T') { - typeEnum = ICEServerSet.RelayType.TURN; - }else { - throw new IOException("Unknown/Unsupported Relay Type: '" + type + "'"); - } - servers.add(new ICEServerSet.RelayServer( - typeEnum, - readASCII16(input), - readASCII8(input), - readASCII8(input) - )); - } - } - -} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java index 933b88e..11bd903 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import net.hoosiertransfer.Alfheim.ILightingEngineProvider; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -72,7 +71,7 @@ public class EaglerChunkLoader extends AnvilChunkLoader { @Override public Chunk loadChunk(World var1, int var2, int var3) throws IOException { - VFile2 file = new VFile2(chunkDirectory, getChunkPath(var2, var3) + ".dat"); + VFile2 file = WorldsDB.newVFile(chunkDirectory, getChunkPath(var2, var3) + ".dat"); if(!file.exists()) { return null; } @@ -90,12 +89,11 @@ public class EaglerChunkLoader extends AnvilChunkLoader { @Override public void saveChunk(World var1, Chunk var2) throws IOException, MinecraftException { - var1.alfheim$getLightingEngine().processLightUpdates(); NBTTagCompound chunkData = new NBTTagCompound(); this.writeChunkToNBT(var2, var1, chunkData); NBTTagCompound fileData = new NBTTagCompound(); fileData.setTag("Level", chunkData); - VFile2 file = new VFile2(chunkDirectory, getChunkPath(var2.xPosition, var2.zPosition) + ".dat"); + VFile2 file = WorldsDB.newVFile(chunkDirectory, getChunkPath(var2.xPosition, var2.zPosition) + ".dat"); try(OutputStream os = file.getOutputStream()) { CompressedStreamTools.writeCompressed(fileData, os); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java index 4203ddc..0aad61c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java @@ -35,24 +35,16 @@ import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; /** - * Copyright (c) 2023-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -66,29 +58,29 @@ public class EaglerIntegratedServerWorker { public static final EaglerSaveFormat saveFormat = new EaglerSaveFormat(EaglerSaveFormat.worldsFolder); - private static final Map openChannels = new HashMap(); + private static final Map openChannels = new HashMap<>(); private static final IPCPacketManager packetManagerInstance = new IPCPacketManager(); private static void processAsyncMessageQueue() { List pktList = ServerPlatformSingleplayer.recieveAllPacket(); - if (pktList != null) { + if(pktList != null) { IPCPacketData packetData; - for (int i = 0, l = pktList.size(); i < l; ++i) { + for(int i = 0, l = pktList.size(); i < l; ++i) { packetData = pktList.get(i); - if (packetData.channel.equals(SingleplayerServerController.IPC_CHANNEL)) { + if(packetData.channel.equals(SingleplayerServerController.IPC_CHANNEL)) { IPCPacketBase ipc; try { ipc = packetManagerInstance.IPCDeserialize(packetData.contents); - } catch (IOException ex) { + }catch(IOException ex) { throw new RuntimeException("Failed to deserialize IPC packet", ex); } handleIPCPacket(ipc); - } else { + }else { IntegratedServerPlayerNetworkManager netHandler = openChannels.get(packetData.channel); - if (netHandler != null) { + if(netHandler != null) { netHandler.addRecievedPacket(packetData.contents); - } else { + }else { logger.error("Recieved packet on channel that does not exist: \"{}\"", packetData.channel); } } @@ -98,7 +90,7 @@ public class EaglerIntegratedServerWorker { public static void tick() { List ocs = new ArrayList<>(openChannels.values()); - for (int i = 0, l = ocs.size(); i < l; ++i) { + for(int i = 0, l = ocs.size(); i < l; ++i) { ocs.get(i).tick(); } } @@ -113,18 +105,18 @@ public class EaglerIntegratedServerWorker { public static void closeChannel(String channel) { IntegratedServerPlayerNetworkManager netmanager = openChannels.remove(channel); - if (netmanager != null) { + if(netmanager != null) { netmanager.closeChannel(new ChatComponentText("End of stream")); sendIPCPacket(new IPCPacket0CPlayerChannel(channel, false)); } } private static void startPlayerConnnection(String channel) { - if (openChannels.containsKey(channel)) { + if(openChannels.containsKey(channel)) { logger.error("Tried opening player channel that already exists: {}", channel); return; } - if (currentProcess == null) { + if(currentProcess == null) { logger.error("Tried opening player channel while server is stopped: {}", channel); return; } @@ -137,295 +129,281 @@ public class EaglerIntegratedServerWorker { private static void handleIPCPacket(IPCPacketBase ipc) { int id = ipc.id(); try { - switch (id) { - case IPCPacket00StartServer.ID: { - IPCPacket00StartServer pkt = (IPCPacket00StartServer) ipc; - - if (!isServerStopped()) { - currentProcess.stopServer(); - } - - currentProcess = new EaglerMinecraftServer(pkt.worldName, pkt.ownerName, pkt.initialViewDistance, - newWorldSettings, pkt.demoMode); - currentProcess.setBaseServerProperties(EnumDifficulty.getDifficultyEnum(pkt.initialDifficulty), - newWorldSettings == null ? GameType.SURVIVAL : newWorldSettings.getGameType()); - currentProcess.startServer(); - - String[] worlds = EaglerSaveFormat.worldsList.getAllLines(); - if (worlds == null || (worlds.length == 1 && worlds[0].trim().length() <= 0)) { - worlds = null; - } - if (worlds == null) { - EaglerSaveFormat.worldsList.setAllChars(pkt.worldName); - } else { - boolean found = false; - for (int i = 0; i < worlds.length; ++i) { - if (worlds[i].equals(pkt.worldName)) { - found = true; - break; - } - } - if (!found) { - String[] s = new String[worlds.length + 1]; - s[0] = pkt.worldName; - System.arraycopy(worlds, 0, s, 1, worlds.length); - EaglerSaveFormat.worldsList.setAllChars(String.join("\n", s)); - } - } - - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket00StartServer.ID)); - break; + switch(id) { + case IPCPacket00StartServer.ID: { + IPCPacket00StartServer pkt = (IPCPacket00StartServer)ipc; + + if(!isServerStopped()) { + currentProcess.stopServer(); } - case IPCPacket01StopServer.ID: { - if (currentProcess != null) { - currentProcess.stopServer(); - currentProcess = null; - } - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID)); - break; + + currentProcess = new EaglerMinecraftServer(pkt.worldName, pkt.ownerName, pkt.initialViewDistance, newWorldSettings, pkt.demoMode); + currentProcess.setBaseServerProperties(EnumDifficulty.getDifficultyEnum(pkt.initialDifficulty), newWorldSettings == null ? GameType.SURVIVAL : newWorldSettings.getGameType()); + currentProcess.startServer(); + + String[] worlds = EaglerSaveFormat.worldsList.getAllLines(); + if(worlds == null || (worlds.length == 1 && worlds[0].trim().length() <= 0)) { + worlds = null; } - case IPCPacket02InitWorld.ID: { - tryStopServer(); - IPCPacket02InitWorld pkt = (IPCPacket02InitWorld) ipc; - newWorldSettings = new WorldSettings(pkt.seed, GameType.getByID(pkt.gamemode), pkt.structures, - pkt.hardcore, WorldType.worldTypes[pkt.worldType]); - newWorldSettings.setWorldName(pkt.worldArgs); // "setWorldName" is actually for setting generator - // arguments, MCP fucked up - if (pkt.bonusChest) { - newWorldSettings.enableBonusChest(); - } - if (pkt.cheats) { - newWorldSettings.enableCommands(); - } - break; - } - case IPCPacket03DeleteWorld.ID: { - tryStopServer(); - IPCPacket03DeleteWorld pkt = (IPCPacket03DeleteWorld) ipc; - if (!saveFormat.deleteWorldDirectory(pkt.worldName)) { - sendTaskFailed(); - break; - } - String[] worldsTxt = EaglerSaveFormat.worldsList.getAllLines(); - if (worldsTxt != null) { - List newWorlds = new ArrayList(); - for (int i = 0; i < worldsTxt.length; ++i) { - String str = worldsTxt[i]; - if (!str.equalsIgnoreCase(pkt.worldName)) { - newWorlds.add(str); - } - } - EaglerSaveFormat.worldsList.setAllChars(String.join("\n", newWorlds)); - } - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket03DeleteWorld.ID)); - break; - } - case IPCPacket05RequestData.ID: { - tryStopServer(); - IPCPacket05RequestData pkt = (IPCPacket05RequestData) ipc; - if (pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) { - sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterEPK.exportWorld(pkt.worldName))); - } else if (pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_MCA) { - sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterMCA.exportWorld(pkt.worldName))); - } else { - logger.error("Unknown IPCPacket05RequestData type {}", ((int) pkt.request & 0xFF)); - sendTaskFailed(); - } - break; - } - case IPCPacket06RenameWorldNBT.ID: { - tryStopServer(); - IPCPacket06RenameWorldNBT pkt = (IPCPacket06RenameWorldNBT) ipc; - boolean b = false; - if (pkt.duplicate) { - b = saveFormat.duplicateWorld(pkt.worldName, pkt.displayName); - } else { - b = saveFormat.renameWorld(pkt.worldName, pkt.displayName); - } - if (!b) { - sendTaskFailed(); - break; - } - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket06RenameWorldNBT.ID)); - break; - } - case IPCPacket07ImportWorld.ID: { - tryStopServer(); - IPCPacket07ImportWorld pkt = (IPCPacket07ImportWorld) ipc; - try { - if (pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_EAG) { - WorldConverterEPK.importWorld(pkt.worldData, pkt.worldName); - } else if (pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_MCA) { - WorldConverterMCA.importWorld(pkt.worldData, pkt.worldName, pkt.gameRules); - } else { - throw new IOException("Client requested an unsupported export format!"); - } - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID)); - } catch (IOException ex) { - sendIPCPacket(new IPCPacket15Crashed( - "COULD NOT IMPORT WORLD \"" + pkt.worldName + "\"!!!\n\n" + EagRuntime.getStackTrace(ex) - + "\n\nFile is probably corrupt, try a different world")); - sendTaskFailed(); - } - break; - } - case IPCPacket0ASetWorldDifficulty.ID: { - IPCPacket0ASetWorldDifficulty pkt = (IPCPacket0ASetWorldDifficulty) ipc; - if (!isServerStopped()) { - if (pkt.difficulty == (byte) -1) { - currentProcess.setDifficultyLockedForAllWorlds(true); - } else { - currentProcess.setDifficultyForAllWorlds(EnumDifficulty.getDifficultyEnum(pkt.difficulty)); - } - } else { - logger.warn("Client tried to set difficulty while server was stopped"); - } - break; - } - case IPCPacket0BPause.ID: { - IPCPacket0BPause pkt = (IPCPacket0BPause) ipc; - if (!isServerStopped()) { - currentProcess.setPaused(pkt.pause); - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket0BPause.ID)); - } else { - logger.error("Client tried to {} while server was stopped", pkt.pause ? "pause" : "unpause"); - sendTaskFailed(); - } - break; - } - case IPCPacket0CPlayerChannel.ID: { - IPCPacket0CPlayerChannel pkt = (IPCPacket0CPlayerChannel) ipc; - if (!isServerStopped()) { - if (pkt.open) { - startPlayerConnnection(pkt.channel); - } else { - closeChannel(pkt.channel); - } - } else { - logger.error("Client tried to {} channel server was stopped", pkt.open ? "open" : "close"); - } - break; - } - case IPCPacket0EListWorlds.ID: { - IPCPacket0EListWorlds pkt = (IPCPacket0EListWorlds) ipc; - if (!isServerStopped()) { - logger.error("Client tried to list worlds while server was running"); - sendTaskFailed(); - } else { - String[] worlds = EaglerSaveFormat.worldsList.getAllLines(); - if (worlds == null) { - sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, - new LinkedList())); + if(worlds == null) { + EaglerSaveFormat.worldsList.setAllChars(pkt.worldName); + }else { + boolean found = false; + for(int i = 0; i < worlds.length; ++i) { + if(worlds[i].equals(pkt.worldName)) { + found = true; break; } - LinkedHashSet updatedList = new LinkedHashSet(); - LinkedList sendListNBT = new LinkedList(); - boolean rewrite = false; - for (int i = 0; i < worlds.length; ++i) { - String w = worlds[i].trim(); - if (w.length() > 0) { - VFile2 vf = new VFile2(EaglerSaveFormat.worldsFolder, w, "level.dat"); - if (!vf.exists()) { - vf = new VFile2(EaglerSaveFormat.worldsFolder, w, "level.dat_old"); - } - if (vf.exists()) { - try (InputStream dat = vf.getInputStream()) { - if (updatedList.add(w)) { - NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(dat); - worldDatNBT.setString("folderNameEagler", w); - sendListNBT.add(worldDatNBT); - } else { - rewrite = true; - } - continue; - } catch (IOException e) { - // shit fuck - } - } - rewrite = true; - logger.error("World level.dat for '{}' was not found, attempting to delete", w); - if (!saveFormat.deleteWorldDirectory(w)) { - logger.error( - "Failed to delete '{}'! It will be removed from the worlds list anyway", w); - } - } else { - rewrite = true; - } - } - if (rewrite) { - EaglerSaveFormat.worldsList.setAllChars(String.join("\n", updatedList)); - } - sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, sendListNBT)); } - break; - } - case IPCPacket14StringList.ID: { - IPCPacket14StringList pkt = (IPCPacket14StringList) ipc; - switch (pkt.opCode) { - case IPCPacket14StringList.LOCALE: - StringTranslate.initServer(pkt.stringList); - break; - // case IPCPacket14StringList.STAT_GUID: - // AchievementMap.init(pkt.stringList); - // AchievementList.init(); - // break; - default: - logger.error("Strange string list 0x{} with length{} recieved", - Integer.toHexString(pkt.opCode), pkt.stringList.size()); - break; + if(!found) { + String[] s = new String[worlds.length + 1]; + s[0] = pkt.worldName; + System.arraycopy(worlds, 0, s, 1, worlds.length); + EaglerSaveFormat.worldsList.setAllChars(String.join("\n", s)); } - break; } - case IPCPacket17ConfigureLAN.ID: { - - IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN) ipc; - if (!pkt.iceServers.isEmpty() - && ServerPlatformSingleplayer.getClientConfigAdapter().isAllowVoiceClient()) { - currentProcess.enableVoice(pkt.iceServers.toArray(new String[pkt.iceServers.size()])); - } - currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats); // don't use - // iceServers - - break; + + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket00StartServer.ID)); + break; + } + case IPCPacket01StopServer.ID: { + if(currentProcess != null) { + currentProcess.stopServer(); + currentProcess = null; } - case IPCPacket18ClearPlayers.ID: { - if (!isServerStopped()) { - logger.error("Client tried to clear players while server was running"); - sendTaskFailed(); - } else { - saveFormat.clearPlayers(((IPCPacket18ClearPlayers) ipc).worldName); - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket18ClearPlayers.ID)); - } - break; + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID)); + break; + } + case IPCPacket02InitWorld.ID: { + tryStopServer(); + IPCPacket02InitWorld pkt = (IPCPacket02InitWorld)ipc; + newWorldSettings = new WorldSettings(pkt.seed, GameType.getByID(pkt.gamemode), pkt.structures, + pkt.hardcore, WorldType.worldTypes[pkt.worldType]); + newWorldSettings.setWorldName(pkt.worldArgs); // "setWorldName" is actually for setting generator arguments, MCP fucked up + if(pkt.bonusChest) { + newWorldSettings.enableBonusChest(); } - case IPCPacket19Autosave.ID: { - if (!isServerStopped()) { - currentProcess.getConfigurationManager().saveAllPlayerData(); - currentProcess.saveAllWorlds(false); - sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket19Autosave.ID)); - } else { - logger.error("Client tried to autosave while server was stopped"); - sendTaskFailed(); - } - break; + if(pkt.cheats) { + newWorldSettings.enableCommands(); } - case IPCPacket21EnableLogging.ID: { - enableLoggingRedirector(((IPCPacket21EnableLogging) ipc).enable); - break; - } - default: - logger.error("IPC packet type 0x{} class \"{}\" was not handled", Integer.toHexString(id), - ipc.getClass().getSimpleName()); + break; + } + case IPCPacket03DeleteWorld.ID: { + tryStopServer(); + IPCPacket03DeleteWorld pkt = (IPCPacket03DeleteWorld)ipc; + if(!saveFormat.deleteWorldDirectory(pkt.worldName)) { sendTaskFailed(); break; + } + String[] worldsTxt = EaglerSaveFormat.worldsList.getAllLines(); + if(worldsTxt != null) { + List newWorlds = new ArrayList<>(); + for(int i = 0; i < worldsTxt.length; ++i) { + String str = worldsTxt[i]; + if(!str.equalsIgnoreCase(pkt.worldName)) { + newWorlds.add(str); + } + } + EaglerSaveFormat.worldsList.setAllChars(String.join("\n", newWorlds)); + } + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket03DeleteWorld.ID)); + break; } - } catch (Throwable t) { - logger.error("IPC packet type 0x{} class \"{}\" was not processed correctly", Integer.toHexString(id), - ipc.getClass().getSimpleName()); + case IPCPacket05RequestData.ID: { + tryStopServer(); + IPCPacket05RequestData pkt = (IPCPacket05RequestData)ipc; + if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) { + sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterEPK.exportWorld(pkt.worldName))); + }else if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_MCA) { + sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterMCA.exportWorld(pkt.worldName))); + }else { + logger.error("Unknown IPCPacket05RequestData type {}", ((int)pkt.request & 0xFF)); + sendTaskFailed(); + } + break; + } + case IPCPacket06RenameWorldNBT.ID: { + tryStopServer(); + IPCPacket06RenameWorldNBT pkt = (IPCPacket06RenameWorldNBT)ipc; + boolean b = false; + if(pkt.duplicate) { + b = saveFormat.duplicateWorld(pkt.worldName, pkt.displayName); + }else { + b = saveFormat.renameWorld(pkt.worldName, pkt.displayName); + } + if(!b) { + sendTaskFailed(); + break; + } + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket06RenameWorldNBT.ID)); + break; + } + case IPCPacket07ImportWorld.ID: { + tryStopServer(); + IPCPacket07ImportWorld pkt = (IPCPacket07ImportWorld)ipc; + try { + if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_EAG) { + WorldConverterEPK.importWorld(pkt.worldData, pkt.worldName); + }else if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_MCA) { + WorldConverterMCA.importWorld(pkt.worldData, pkt.worldName, pkt.gameRules); + }else { + throw new IOException("Client requested an unsupported export format!"); + } + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID)); + }catch(IOException ex) { + sendIPCPacket(new IPCPacket15Crashed("COULD NOT IMPORT WORLD \"" + pkt.worldName + "\"!!!\n\n" + EagRuntime.getStackTrace(ex) + "\n\nFile is probably corrupt, try a different world")); + sendTaskFailed(); + } + break; + } + case IPCPacket0ASetWorldDifficulty.ID: { + IPCPacket0ASetWorldDifficulty pkt = (IPCPacket0ASetWorldDifficulty)ipc; + if(!isServerStopped()) { + if(pkt.difficulty == (byte)-1) { + currentProcess.setDifficultyLockedForAllWorlds(true); + }else { + currentProcess.setDifficultyForAllWorlds(EnumDifficulty.getDifficultyEnum(pkt.difficulty)); + } + }else { + logger.warn("Client tried to set difficulty while server was stopped"); + } + break; + } + case IPCPacket0BPause.ID: { + IPCPacket0BPause pkt = (IPCPacket0BPause)ipc; + if(!isServerStopped()) { + currentProcess.setPaused(pkt.pause); + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket0BPause.ID)); + }else { + logger.error("Client tried to {} while server was stopped", pkt.pause ? "pause" : "unpause"); + sendTaskFailed(); + } + break; + } + case IPCPacket0CPlayerChannel.ID: { + IPCPacket0CPlayerChannel pkt = (IPCPacket0CPlayerChannel)ipc; + if(!isServerStopped()) { + if(pkt.open) { + startPlayerConnnection(pkt.channel); + }else { + closeChannel(pkt.channel); + } + }else { + logger.error("Client tried to {} channel server was stopped", pkt.open ? "open" : "close"); + } + break; + } + case IPCPacket0EListWorlds.ID: { + IPCPacket0EListWorlds pkt = (IPCPacket0EListWorlds)ipc; + if(!isServerStopped()) { + logger.error("Client tried to list worlds while server was running"); + sendTaskFailed(); + }else { + String[] worlds = EaglerSaveFormat.worldsList.getAllLines(); + if(worlds == null) { + sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, new LinkedList<>())); + break; + } + LinkedHashSet updatedList = new LinkedHashSet<>(); + LinkedList sendListNBT = new LinkedList<>(); + boolean rewrite = false; + for(int i = 0; i < worlds.length; ++i) { + String w = worlds[i].trim(); + if(w.length() > 0) { + VFile2 vf = WorldsDB.newVFile(EaglerSaveFormat.worldsFolder, w, "level.dat"); + if(!vf.exists()) { + vf = WorldsDB.newVFile(EaglerSaveFormat.worldsFolder, w, "level.dat_old"); + } + if(vf.exists()) { + try(InputStream dat = vf.getInputStream()) { + if(updatedList.add(w)) { + NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(dat); + worldDatNBT.setString("folderNameEagler", w); + sendListNBT.add(worldDatNBT); + }else { + rewrite = true; + } + continue; + }catch(IOException e) { + // shit fuck + } + } + rewrite = true; + logger.error("World level.dat for '{}' was not found, attempting to delete", w); + if(!saveFormat.deleteWorldDirectory(w)) { + logger.error("Failed to delete '{}'! It will be removed from the worlds list anyway", w); + } + }else { + rewrite = true; + } + } + if(rewrite) { + EaglerSaveFormat.worldsList.setAllChars(String.join("\n", updatedList)); + } + sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, sendListNBT)); + } + break; + } + case IPCPacket14StringList.ID: { + IPCPacket14StringList pkt = (IPCPacket14StringList)ipc; + switch(pkt.opCode) { + case IPCPacket14StringList.LOCALE: + StringTranslate.initServer(pkt.stringList); + break; + //case IPCPacket14StringList.STAT_GUID: + // AchievementMap.init(pkt.stringList); + // AchievementList.init(); + // break; + default: + logger.error("Strange string list 0x{} with length{} recieved", Integer.toHexString(pkt.opCode), pkt.stringList.size()); + break; + } + break; + } + case IPCPacket17ConfigureLAN.ID: { + + IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN)ipc; + if(!pkt.iceServers.isEmpty() && ServerPlatformSingleplayer.getClientConfigAdapter().isAllowVoiceClient()) { + currentProcess.enableVoice(pkt.iceServers.toArray(new String[pkt.iceServers.size()])); + } + currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats); // don't use iceServers + + break; + } + case IPCPacket18ClearPlayers.ID: { + if(!isServerStopped()) { + logger.error("Client tried to clear players while server was running"); + sendTaskFailed(); + }else { + saveFormat.clearPlayers(((IPCPacket18ClearPlayers)ipc).worldName); + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket18ClearPlayers.ID)); + } + break; + } + case IPCPacket19Autosave.ID: { + if(!isServerStopped()) { + currentProcess.getConfigurationManager().saveAllPlayerData(); + currentProcess.saveAllWorlds(false); + sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket19Autosave.ID)); + }else { + logger.error("Client tried to autosave while server was stopped"); + sendTaskFailed(); + } + break; + } + case IPCPacket1BEnableLogging.ID: { + enableLoggingRedirector(((IPCPacket1BEnableLogging)ipc).enable); + break; + } + default: + logger.error("IPC packet type 0x{} class \"{}\" was not handled", Integer.toHexString(id), ipc.getClass().getSimpleName()); + sendTaskFailed(); + break; + } + }catch(Throwable t) { + logger.error("IPC packet type 0x{} class \"{}\" was not processed correctly", Integer.toHexString(id), ipc.getClass().getSimpleName()); logger.error(t); - sendIPCPacket(new IPCPacket15Crashed( - "IPC packet type 0x" + Integer.toHexString(id) + " class \"" + ipc.getClass().getSimpleName() - + "\" was not processed correctly!\n\n" + EagRuntime.getStackTrace(t))); + sendIPCPacket(new IPCPacket15Crashed("IPC packet type 0x" + Integer.toHexString(id) + " class \"" + ipc.getClass().getSimpleName() + "\" was not processed correctly!\n\n" + EagRuntime.getStackTrace(t))); sendTaskFailed(); } } @@ -440,14 +418,14 @@ public class EaglerIntegratedServerWorker { } public static void sendLogMessagePacket(String txt, boolean err) { - sendIPCPacket(new IPCPacket20LoggerMessage(txt, err)); + sendIPCPacket(new IPCPacket1ALoggerMessage(txt, err)); } public static void sendIPCPacket(IPCPacketBase ipc) { byte[] pkt; try { pkt = packetManagerInstance.IPCSerialize(ipc); - } catch (IOException ex) { + }catch (IOException ex) { throw new RuntimeException("Failed to serialize IPC packet", ex); } ServerPlatformSingleplayer.sendPacket(new IPCPacketData(SingleplayerServerController.IPC_CHANNEL, pkt)); @@ -470,26 +448,28 @@ public class EaglerIntegratedServerWorker { } private static void tryStopServer() { - if (!isServerStopped()) { + if(!isServerStopped()) { currentProcess.stopServer(); } currentProcess = null; } - private static void mainLoop() { + private static void mainLoop(boolean singleThreadMode) { processAsyncMessageQueue(); - - if (currentProcess != null) { - if (currentProcess.isServerRunning()) { - currentProcess.mainLoop(); + + if(currentProcess != null) { + if(currentProcess.isServerRunning()) { + currentProcess.mainLoop(singleThreadMode); } - if (!currentProcess.isServerRunning()) { + if(!currentProcess.isServerRunning()) { currentProcess.stopServer(); currentProcess = null; sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID)); } - } else { - EagUtils.sleep(50l); + }else { + if(!singleThreadMode) { + EagUtils.sleep(50l); + } } } @@ -497,29 +477,33 @@ public class EaglerIntegratedServerWorker { try { currentProcess = null; logger.info("Starting EaglercraftX integrated server worker..."); - + + if(ServerPlatformSingleplayer.getWorldsDatabase().isRamdisk()) { + sendIPCPacket(new IPCPacket1CIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE)); + } + // signal thread startup successful sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF)); - - while (true) { - mainLoop(); - EagUtils.sleep(0l); + + while(true) { + mainLoop(false); + ServerPlatformSingleplayer.immediateContinue(); } - } catch (Throwable tt) { - if (tt instanceof ReportedException) { - String fullReport = ((ReportedException) tt).getCrashReport().getCompleteReport(); + }catch(Throwable tt) { + if(tt instanceof ReportedException) { + String fullReport = ((ReportedException)tt).getCrashReport().getCompleteReport(); logger.error(fullReport); sendIPCPacket(new IPCPacket15Crashed(fullReport)); - } else { + }else { logger.error("Server process encountered a fatal error!"); logger.error(tt); sendIPCPacket(new IPCPacket15Crashed("SERVER PROCESS EXITED!\n\n" + EagRuntime.getStackTrace(tt))); } - } finally { - if (!isServerStopped()) { + }finally { + if(!isServerStopped()) { try { currentProcess.stopServer(); - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Encountered exception while stopping server!"); logger.error(t); } @@ -528,4 +512,17 @@ public class EaglerIntegratedServerWorker { sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacketFFProcessKeepAlive.EXITED)); } } + + public static void singleThreadMain() { + logger.info("Starting EaglercraftX integrated server worker..."); + if(ServerPlatformSingleplayer.getWorldsDatabase().isRamdisk()) { + sendIPCPacket(new IPCPacket1CIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE)); + } + sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF)); + } + + public static void singleThreadUpdate() { + mainLoop(true); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java index 80feab9..7f99536 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java @@ -1,11 +1,12 @@ package net.lax1dude.eaglercraft.v1_8.sp.server; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.minecraft.entity.player.EntityPlayer; @@ -21,24 +22,16 @@ import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedSkinService; import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; /** - * Copyright (c) 2023-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -47,7 +40,7 @@ public class EaglerMinecraftServer extends MinecraftServer { public static final Logger logger = EaglerIntegratedServerWorker.logger; - public static final VFile2 savesDir = new VFile2("worlds"); + public static final VFile2 savesDir = WorldsDB.newVFile("worlds"); protected EnumDifficulty difficulty; protected GameType gamemode; @@ -67,14 +60,13 @@ public class EaglerMinecraftServer extends MinecraftServer { public static int counterTileUpdate = 0; public static int counterLightUpdate = 0; - private final List scheduledTasks = new LinkedList(); + private final List scheduledTasks = new LinkedList<>(); - public EaglerMinecraftServer(String world, String owner, int viewDistance, WorldSettings currentWorldSettings, - boolean demo) { + public EaglerMinecraftServer(String world, String owner, int viewDistance, WorldSettings currentWorldSettings, boolean demo) { super(world); Bootstrap.register(); this.saveHandler = new EaglerSaveHandler(savesDir, world); - this.skinService = new IntegratedSkinService(new VFile2(saveHandler.getWorldDirectory(), "eagler/skulls")); + this.skinService = new IntegratedSkinService(WorldsDB.newVFile(saveHandler.getWorldDirectory(), "eagler/skulls")); this.capeService = new IntegratedCapeService(); this.voiceService = null; this.setServerOwner(owner); @@ -100,12 +92,12 @@ public class EaglerMinecraftServer extends MinecraftServer { } public void enableVoice(String[] iceServers) { - if (iceServers != null) { - if (voiceService != null) { + if(iceServers != null) { + if(voiceService != null) { voiceService.changeICEServers(iceServers); - } else { + }else { voiceService = new IntegratedVoiceService(iceServers); - for (EntityPlayerMP player : getConfigurationManager().func_181057_v()) { + for(EntityPlayerMP player : getConfigurationManager().func_181057_v()) { voiceService.handlePlayerLoggedIn(player); } } @@ -140,16 +132,16 @@ public class EaglerMinecraftServer extends MinecraftServer { EaglerIntegratedServerWorker.saveFormat.deleteWorldDirectory(getFolderName()); } - public void mainLoop() { + public void mainLoop(boolean singleThreadMode) { long k = getCurrentTimeMillis(); this.sendTPSToClient(k); - if (paused && this.playersOnline.size() <= 1) { + if(paused && this.playersOnline.size() <= 1) { currentTime = k; return; } long j = k - this.currentTime; - if (j > 2000L && this.currentTime - this.timeOfLastWarning >= 15000L) { + if (j > (singleThreadMode ? 500L : 2000L) && this.currentTime - this.timeOfLastWarning >= (singleThreadMode ? 5000L : 15000L)) { logger.warn( "Can\'t keep up! Did the system time change, or is the server overloaded? Running {}ms behind, skipping {} tick(s)", new Object[] { Long.valueOf(j), Long.valueOf(j / 50L) }); @@ -183,17 +175,18 @@ public class EaglerMinecraftServer extends MinecraftServer { } protected void sendTPSToClient(long millis) { - if (millis - lastTPSUpdate > 1000l) { + if(millis - lastTPSUpdate > 1000l) { lastTPSUpdate = millis; - if (serverRunning && this.worldServers != null) { - List lst = new ArrayList<>(Arrays.asList( + if(serverRunning && this.worldServers != null) { + List lst = Lists.newArrayList( "TPS: " + counterTicksPerSecond + "/20", "Chunks: " + countChunksLoaded(this.worldServers) + "/" + countChunksTotal(this.worldServers), "Entities: " + countEntities(this.worldServers) + "+" + countTileEntities(this.worldServers), "R: " + counterChunkRead + ", G: " + counterChunkGenerate + ", W: " + counterChunkWrite, - "TU: " + counterTileUpdate + ", LU: " + counterLightUpdate)); + "TU: " + counterTileUpdate + ", LU: " + counterLightUpdate + ); int players = countPlayerEntities(this.worldServers); - if (players > 1) { + if(players > 1) { lst.add("Players: " + players); } counterTicksPerSecond = counterChunkRead = counterChunkGenerate = 0; @@ -205,8 +198,8 @@ public class EaglerMinecraftServer extends MinecraftServer { private static int countChunksLoaded(WorldServer[] worlds) { int i = 0; - for (int j = 0; j < worlds.length; ++j) { - if (worlds[j] != null) { + for(int j = 0; j < worlds.length; ++j) { + if(worlds[j] != null) { i += worlds[j].theChunkProviderServer.getLoadedChunkCount(); } } @@ -215,11 +208,11 @@ public class EaglerMinecraftServer extends MinecraftServer { private static int countChunksTotal(WorldServer[] worlds) { int i = 0; - for (int j = 0; j < worlds.length; ++j) { - if (worlds[j] != null) { + for(int j = 0; j < worlds.length; ++j) { + if(worlds[j] != null) { List players = worlds[j].playerEntities; - for (int l = 0, n = players.size(); l < n; ++l) { - i += ((EntityPlayerMP) players.get(l)).loadedChunks.size(); + for(int l = 0, n = players.size(); l < n; ++l) { + i += ((EntityPlayerMP)players.get(l)).loadedChunks.size(); } i += worlds[j].theChunkProviderServer.getLoadedChunkCount(); } @@ -229,8 +222,8 @@ public class EaglerMinecraftServer extends MinecraftServer { private static int countEntities(WorldServer[] worlds) { int i = 0; - for (int j = 0; j < worlds.length; ++j) { - if (worlds[j] != null) { + for(int j = 0; j < worlds.length; ++j) { + if(worlds[j] != null) { i += worlds[j].loadedEntityList.size(); } } @@ -239,8 +232,8 @@ public class EaglerMinecraftServer extends MinecraftServer { private static int countTileEntities(WorldServer[] worlds) { int i = 0; - for (int j = 0; j < worlds.length; ++j) { - if (worlds[j] != null) { + for(int j = 0; j < worlds.length; ++j) { + if(worlds[j] != null) { i += worlds[j].loadedTileEntityList.size(); } } @@ -249,8 +242,8 @@ public class EaglerMinecraftServer extends MinecraftServer { private static int countPlayerEntities(WorldServer[] worlds) { int i = 0; - for (int j = 0; j < worlds.length; ++j) { - if (worlds[j] != null) { + for(int j = 0; j < worlds.length; ++j) { + if(worlds[j] != null) { i += worlds[j].playerEntities.size(); } } @@ -259,19 +252,18 @@ public class EaglerMinecraftServer extends MinecraftServer { public void setPaused(boolean p) { paused = p; - if (!p) { - currentTime = System.currentTimeMillis(); + if(!p) { + currentTime = EagRuntime.steadyTimeMillis(); } } - + public boolean getPaused() { return paused; } @Override public boolean canStructuresSpawn() { - return worldServers != null ? worldServers[0].getWorldInfo().isMapFeaturesEnabled() - : newWorldSettings.isMapFeaturesEnabled(); + return worldServers != null ? worldServers[0].getWorldInfo().isMapFeaturesEnabled() : newWorldSettings.isMapFeaturesEnabled(); } @Override @@ -286,8 +278,7 @@ public class EaglerMinecraftServer extends MinecraftServer { @Override public boolean isHardcore() { - return worldServers != null ? worldServers[0].getWorldInfo().isHardcoreModeEnabled() - : newWorldSettings.getHardcoreEnabled(); + return worldServers != null ? worldServers[0].getWorldInfo().isHardcoreModeEnabled() : newWorldSettings.getHardcoreEnabled(); } @Override diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveFormat.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveFormat.java index 0f2d4b2..4fb29bd 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveFormat.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveFormat.java @@ -30,13 +30,13 @@ import net.minecraft.world.storage.WorldInfo; */ public class EaglerSaveFormat extends SaveFormatOld { - public static final VFile2 worldsList = new VFile2("worlds_list.txt"); - public static final VFile2 worldsFolder = new VFile2("worlds"); - public EaglerSaveFormat(VFile2 parFile) { super(parFile); } + public static final VFile2 worldsList = WorldsDB.newVFile("worlds_list.txt"); + public static final VFile2 worldsFolder = WorldsDB.newVFile("worlds"); + public String getName() { return "eagler"; } @@ -46,7 +46,7 @@ public class EaglerSaveFormat extends SaveFormatOld { } public List getSaveList() { - ArrayList arraylist = Lists.newArrayList(); + ArrayList arraylist = Lists.newArrayList(); if(worldsList.exists()) { String[] lines = worldsList.getAllLines(); for (int i = 0; i < lines.length; ++i) { @@ -70,7 +70,7 @@ public class EaglerSaveFormat extends SaveFormatOld { } public void clearPlayers(String worldFolder) { - VFile2 file1 = new VFile2(this.savesDirectory, worldFolder, "player"); + VFile2 file1 = WorldsDB.newVFile(this.savesDirectory, worldFolder, "player"); deleteFiles(file1.listFiles(true), null); } @@ -80,12 +80,12 @@ public class EaglerSaveFormat extends SaveFormatOld { public boolean duplicateWorld(String worldFolder, String displayName) { String newFolderName = displayName.replaceAll("[\\./\"]", "_"); - VFile2 newFolder = new VFile2(savesDirectory, newFolderName); - while((new VFile2(newFolder, "level.dat")).exists() || (new VFile2(newFolder, "level.dat_old")).exists()) { + VFile2 newFolder = WorldsDB.newVFile(savesDirectory, newFolderName); + while((WorldsDB.newVFile(newFolder, "level.dat")).exists() || (WorldsDB.newVFile(newFolder, "level.dat_old")).exists()) { newFolderName += "_"; - newFolder = new VFile2(savesDirectory, newFolderName); + newFolder = WorldsDB.newVFile(savesDirectory, newFolderName); } - VFile2 oldFolder = new VFile2(this.savesDirectory, worldFolder); + VFile2 oldFolder = WorldsDB.newVFile(this.savesDirectory, worldFolder); String oldPath = oldFolder.getPath(); int totalSize = 0; int lastUpdate = 0; @@ -94,7 +94,7 @@ public class EaglerSaveFormat extends SaveFormatOld { for(int i = 0, l = vfl.size(); i < l; ++i) { VFile2 vf = vfl.get(i); String fileNameRelative = vf.getPath().substring(oldPath.length() + 1); - totalSize += VFile2.copyFile(vf, new VFile2(finalNewFolder, fileNameRelative)); + totalSize += VFile2.copyFile(vf, WorldsDB.newVFile(finalNewFolder, fileNameRelative)); if (totalSize - lastUpdate > 10000) { lastUpdate = totalSize; EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.duplicating", totalSize); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveHandler.java index 0e7fe7a..dae1b7e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveHandler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerSaveHandler.java @@ -29,7 +29,7 @@ public class EaglerSaveHandler extends SaveHandler { } public IChunkLoader getChunkLoader(WorldProvider provider) { - return new EaglerChunkLoader(new VFile2(this.getWorldDirectory(), "level" + provider.getDimensionId())); + return new EaglerChunkLoader(WorldsDB.newVFile(this.getWorldDirectory(), "level" + provider.getDimensionId())); } public void saveWorldInfoWithPlayer(WorldInfo worldInformation, NBTTagCompound tagCompound) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/WorldsDB.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/WorldsDB.java new file mode 100755 index 0000000..01aa28d --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/WorldsDB.java @@ -0,0 +1,32 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server; + +import java.util.function.Supplier; + +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; +import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class WorldsDB { + + private static final Supplier fsGetter = ServerPlatformSingleplayer::getWorldsDatabase; + + public static VFile2 newVFile(Object... path) { + return VFile2.create(fsGetter, path); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKCompiler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKCompiler.java index 86e198c..c0c3cb0 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKCompiler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKCompiler.java @@ -1,167 +1,184 @@ -package net.lax1dude.eaglercraft.v1_8.sp.server.export; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.zip.CRC32; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EPKCompiler { - - private final EaglerOutputStream os; - private final CRC32 checkSum = new CRC32(); - private int lengthIntegerOffset = 0; - private int totalFileCount = 0; - - public EPKCompiler(String name, String owner, String type) { - os = new EaglerOutputStream(0x200000); - try { - - os.write(new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36}); // EAGPKG$$ - os.write(new byte[]{(byte)6,(byte)118,(byte)101,(byte)114,(byte)50,(byte)46,(byte)48}); // 6 + ver2.0 - Date d = new Date(); - - byte[] filename = (name + ".epk").getBytes(StandardCharsets.UTF_8); - os.write(filename.length); - os.write(filename); - - byte[] comment = ("\n\n # Eagler EPK v2.0 (c) " + EagRuntime.fixDateFormat(new SimpleDateFormat("yyyy")).format(d) + " " + - owner + "\n # export: on " + EagRuntime.fixDateFormat(new SimpleDateFormat("MM/dd/yyyy")).format(d) + " at " + - EagRuntime.fixDateFormat(new SimpleDateFormat("hh:mm:ss aa")).format(d) + "\n\n # world name: " + name + "\n\n") - .getBytes(StandardCharsets.UTF_8); - - os.write((comment.length >>> 8) & 255); - os.write(comment.length & 255); - os.write(comment); - - writeLong(d.getTime(), os); - - lengthIntegerOffset = os.size(); - os.write(new byte[]{(byte)255,(byte)255,(byte)255,(byte)255}); // this will be replaced with the file count - - os.write('0'); // compression type: none - - os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD - os.write(new byte[]{(byte)9,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)116,(byte)121, - (byte)112,(byte)101}); // 9 + file-type - - byte[] typeBytes = type.getBytes(StandardCharsets.UTF_8); - writeInt(typeBytes.length, os); - os.write(typeBytes); // write type - os.write('>'); - - ++totalFileCount; - - os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD - os.write(new byte[]{(byte)10,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)110, - (byte)97,(byte)109,(byte)101}); // 10 + world-name - - byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8); - writeInt(nameBytes.length, os); - os.write(nameBytes); // write name - os.write('>'); - - ++totalFileCount; - - os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD - os.write(new byte[]{(byte)11,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)111, - (byte)119,(byte)110,(byte)101,(byte)114}); // 11 + world-owner - - byte[] ownerBytes = owner.getBytes(StandardCharsets.UTF_8); - writeInt(ownerBytes.length, os); - os.write(ownerBytes); // write owner - os.write('>'); - - ++totalFileCount; - - }catch(IOException ex) { - throw new RuntimeException("This happened somehow", ex); - } - } - - public void append(String name, byte[] dat) { - try { - - checkSum.reset(); - checkSum.update(dat, 0, dat.length); - long sum = checkSum.getValue(); - - os.write(new byte[]{(byte)70,(byte)73,(byte)76,(byte)69}); // FILE - - byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8); - os.write(nameBytes.length); - os.write(nameBytes); - - writeInt(dat.length + 5, os); - writeInt((int)sum, os); - - os.write(dat); - - os.write(':'); - os.write('>'); - - ++totalFileCount; - - }catch(IOException ex) { - throw new RuntimeException("This happened somehow", ex); - } - } - - public byte[] complete() { - try { - - os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$ - os.write(new byte[]{(byte)58,(byte)58,(byte)58,(byte)89,(byte)69,(byte)69,(byte)58,(byte)62}); // :::YEE:> - - byte[] ret = os.toByteArray(); - - ret[lengthIntegerOffset] = (byte)(totalFileCount >>> 24); - ret[lengthIntegerOffset + 1] = (byte)(totalFileCount >>> 16); - ret[lengthIntegerOffset + 2] = (byte)(totalFileCount >>> 8); - ret[lengthIntegerOffset + 3] = (byte)(totalFileCount & 0xFF); - - return ret; - - }catch(IOException ex) { - throw new RuntimeException("This happened somehow", ex); - } - } - - public static void writeInt(int i, OutputStream os) throws IOException { - os.write((i >>> 24) & 0xFF); - os.write((i >>> 16) & 0xFF); - os.write((i >>> 8) & 0xFF); - os.write(i & 0xFF); - } - - public static void writeLong(long i, OutputStream os) throws IOException { - os.write((int)((i >>> 56l) & 0xFFl)); - os.write((int)((i >>> 48l) & 0xFFl)); - os.write((int)((i >>> 40l) & 0xFFl)); - os.write((int)((i >>> 32l) & 0xFFl)); - os.write((int)((i >>> 24l) & 0xFFl)); - os.write((int)((i >>> 16l) & 0xFFl)); - os.write((int)((i >>> 8l) & 0xFFl)); - os.write((int)(i & 0xFFl)); - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.server.export; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.zip.CRC32; + +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EPKCompiler { + + private final EaglerOutputStream os; + private final OutputStream dos; + private final CRC32 checkSum = new CRC32(); + private int lengthIntegerOffset = 0; + private int totalFileCount = 0; + + public EPKCompiler(String name, String owner, String type) { + this(name, owner, type, false, true, null); + } + + public EPKCompiler(String name, String owner, String type, boolean gzip, boolean world, String commentStr) { + os = new EaglerOutputStream(0x200000); + try { + + os.write(new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36}); // EAGPKG$$ + os.write(new byte[]{(byte)6,(byte)118,(byte)101,(byte)114,(byte)50,(byte)46,(byte)48}); // 6 + ver2.0 + Date d = new Date(); + + byte[] filename = (name + ".epk").getBytes(StandardCharsets.UTF_8); + os.write(filename.length); + os.write(filename); + + byte[] comment = (world ? ("\n\n # Eagler EPK v2.0 (c) " + + (new SimpleDateFormat("yyyy")).format(d) + " " + owner + + "\n # export: on " + (new SimpleDateFormat("MM/dd/yyyy")).format(d) + + " at " + (new SimpleDateFormat("hh:mm:ss aa")).format(d) + + "\n\n # world name: " + name + "\n\n") : commentStr).getBytes(StandardCharsets.UTF_8); + + os.write((comment.length >>> 8) & 255); + os.write(comment.length & 255); + os.write(comment); + + writeLong(d.getTime(), os); + + lengthIntegerOffset = os.size(); + os.write(new byte[]{(byte)255,(byte)255,(byte)255,(byte)255}); // this will be replaced with the file count + + if(gzip) { + os.write('G'); // compression type: gzip + dos = EaglerZLIB.newGZIPOutputStream(os); + }else { + os.write('0'); // compression type: none + dos = os; + } + + dos.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD + dos.write(new byte[]{(byte)9,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)116,(byte)121, + (byte)112,(byte)101}); // 9 + file-type + + byte[] typeBytes = type.getBytes(StandardCharsets.UTF_8); + writeInt(typeBytes.length, dos); + dos.write(typeBytes); // write type + dos.write('>'); + + ++totalFileCount; + + if(world) { + dos.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD + dos.write(new byte[]{(byte)10,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)110, + (byte)97,(byte)109,(byte)101}); // 10 + world-name + + byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8); + writeInt(nameBytes.length, dos); + dos.write(nameBytes); // write name + dos.write('>'); + + ++totalFileCount; + } + + if(world && owner != null) { + dos.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD + dos.write(new byte[]{(byte)11,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)111, + (byte)119,(byte)110,(byte)101,(byte)114}); // 11 + world-owner + + byte[] ownerBytes = owner.getBytes(StandardCharsets.UTF_8); + writeInt(ownerBytes.length, dos); + dos.write(ownerBytes); // write owner + dos.write('>'); + + ++totalFileCount; + } + + }catch(IOException ex) { + throw new RuntimeException("This happened somehow", ex); + } + } + + public void append(String name, byte[] dat) { + try { + + checkSum.reset(); + checkSum.update(dat, 0, dat.length); + long sum = checkSum.getValue(); + + dos.write(new byte[]{(byte)70,(byte)73,(byte)76,(byte)69}); // FILE + + byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8); + dos.write(nameBytes.length); + dos.write(nameBytes); + + writeInt(dat.length + 5, dos); + writeInt((int)sum, dos); + + dos.write(dat); + + dos.write(':'); + dos.write('>'); + + ++totalFileCount; + + }catch(IOException ex) { + throw new RuntimeException("This happened somehow", ex); + } + } + + public byte[] complete() { + try { + dos.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$ + dos.close(); + + os.write(new byte[]{(byte)58,(byte)58,(byte)58,(byte)89,(byte)69,(byte)69,(byte)58,(byte)62}); // :::YEE:> + + byte[] ret = os.toByteArray(); + + ret[lengthIntegerOffset] = (byte)(totalFileCount >>> 24); + ret[lengthIntegerOffset + 1] = (byte)(totalFileCount >>> 16); + ret[lengthIntegerOffset + 2] = (byte)(totalFileCount >>> 8); + ret[lengthIntegerOffset + 3] = (byte)(totalFileCount & 0xFF); + + return ret; + + }catch(IOException ex) { + throw new RuntimeException("This happened somehow", ex); + } + } + + public static void writeInt(int i, OutputStream os) throws IOException { + os.write((i >>> 24) & 0xFF); + os.write((i >>> 16) & 0xFF); + os.write((i >>> 8) & 0xFF); + os.write(i & 0xFF); + } + + public static void writeLong(long i, OutputStream os) throws IOException { + os.write((int)((i >>> 56l) & 0xFFl)); + os.write((int)((i >>> 48l) & 0xFFl)); + os.write((int)((i >>> 40l) & 0xFFl)); + os.write((int)((i >>> 32l) & 0xFFl)); + os.write((int)((i >>> 24l) & 0xFFl)); + os.write((int)((i >>> 16l) & 0xFFl)); + os.write((int)((i >>> 8l) & 0xFFl)); + os.write((int)(i & 0xFFl)); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java index 5f4600f..524de85 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java @@ -10,6 +10,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerSaveFormat; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.storage.WorldInfo; @@ -17,21 +18,14 @@ import net.minecraft.world.storage.WorldInfo; /** * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -44,48 +38,48 @@ public class WorldConverterEPK { logger.info("Importing world \"{}\" from EPK", newName); String folderName = newName.replaceAll("[\\./\"]", "_"); VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); - while ((new VFile2(worldDir, "level.dat")).exists() || (new VFile2(worldDir, "level.dat_old")).exists()) { + while(WorldsDB.newVFile(worldDir, "level.dat").exists() || WorldsDB.newVFile(worldDir, "level.dat_old").exists()) { folderName += "_"; worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); } - try (EPKDecompiler dc = new EPKDecompiler(archiveContents)) { + try(EPKDecompiler dc = new EPKDecompiler(archiveContents)) { EPKDecompiler.FileEntry f = null; int lastProgUpdate = 0; int prog = 0; String hasReadType = null; boolean has152Format = false; int cnt = 0; - while ((f = dc.readFile()) != null) { + while((f = dc.readFile()) != null) { byte[] b = f.data; - if (hasReadType == null) { + if(hasReadType == null) { if (f.type.equals("HEAD") && f.name.equals("file-type") && ((hasReadType = EPKDecompiler.readASCII(f.data)).equals("epk/world188") || (has152Format = hasReadType.equals("epk/world152")))) { - if (has152Format) { + if(has152Format) { logger.warn("World type detected as 1.5.2, it will be converted to 1.8.8 format"); } continue; - } else { + }else { throw new IOException("file does not contain a singleplayer 1.5.2 or 1.8.8 world!"); } } - if (f.type.equals("FILE")) { - if (f.name.equals("level.dat") || f.name.equals("level.dat_old")) { + if(f.type.equals("FILE")) { + if(f.name.equals("level.dat") || f.name.equals("level.dat_old")) { NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); - if (has152Format) { + if(has152Format) { WorldInfo.initEaglerVersion(worldDatNBT.getCompoundTag("Data")); } EaglerOutputStream tmp = new EaglerOutputStream(); CompressedStreamTools.writeCompressed(worldDatNBT, tmp); b = tmp.toByteArray(); } - VFile2 ff = new VFile2(worldDir, f.name); + VFile2 ff = WorldsDB.newVFile(worldDir, f.name); ff.setAllBytes(b); prog += b.length; ++cnt; - if (prog - lastProgUpdate > 25000) { + if(prog - lastProgUpdate > 25000) { lastProgUpdate = prog; logger.info("Extracted {} files, {} bytes from EPK...", cnt, prog); EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.1", prog); @@ -95,10 +89,9 @@ public class WorldConverterEPK { } logger.info("EPK was successfully extracted into directory \"{}\"", worldDir.getPath()); String[] worldsTxt = EaglerSaveFormat.worldsList.getAllLines(); - if (worldsTxt == null || worldsTxt.length <= 0 - || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) { + if(worldsTxt == null || worldsTxt.length <= 0 || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) { worldsTxt = new String[] { folderName }; - } else { + }else { String[] tmp = worldsTxt; worldsTxt = new String[worldsTxt.length + 1]; System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length); @@ -110,14 +103,13 @@ public class WorldConverterEPK { public static byte[] exportWorld(String worldName) { String realWorldName = worldName; String worldOwner = "UNKNOWN"; - String splitter = new String(new char[] { (char) 253, (char) 233, (char) 233 }); - if (worldName.contains(splitter)) { + String splitter = new String(new char[] { (char)253, (char)233, (char)233 }); + if(worldName.contains(splitter)) { int i = worldName.lastIndexOf(splitter); worldOwner = worldName.substring(i + 3); realWorldName = worldName.substring(0, i); } - VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(realWorldName, false) - .getWorldDirectory(); + VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(realWorldName, false).getWorldDirectory(); logger.info("Exporting world directory \"{}\" as EPK", worldDir.getPath()); final int[] bytesWritten = new int[1]; final int[] filesWritten = new int[1]; @@ -125,7 +117,7 @@ public class WorldConverterEPK { EPKCompiler c = new EPKCompiler(realWorldName, worldOwner, "epk/world188"); String pfx = worldDir.getPath(); List filesList = worldDir.listFiles(true); - for (int i = 0, l = filesList.size(); i < l; ++i) { + for(int i = 0, l = filesList.size(); i < l; ++i) { VFile2 vf = filesList.get(i); ++filesWritten[0]; byte[] b = vf.getAllBytes(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java index 4750960..7e5bfa5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java @@ -19,6 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerChunkLoader; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerSaveFormat; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; import net.minecraft.world.chunk.storage.RegionFile; import net.minecraft.world.storage.WorldInfo; import net.minecraft.nbt.CompressedStreamTools; @@ -27,21 +28,14 @@ import net.minecraft.nbt.NBTTagCompound; /** * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -54,54 +48,49 @@ public class WorldConverterMCA { logger.info("Importing world \"{}\" from MCA", newName); String folderName = newName.replaceAll("[\\./\"]", "_"); VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); - while ((new VFile2(worldDir, "level.dat")).exists() || (new VFile2(worldDir, "level.dat_old")).exists()) { + while(WorldsDB.newVFile(worldDir, "level.dat").exists() || WorldsDB.newVFile(worldDir, "level.dat_old").exists()) { folderName += "_"; worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); } List fileNames = new ArrayList<>(); - try (ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) { + try(ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) { ZipEntry folderNameFile = null; - while ((folderNameFile = zis.getNextEntry()) != null) { - if (folderNameFile.getName().contains("__MACOSX/")) - continue; - if (folderNameFile.isDirectory()) - continue; + while((folderNameFile = zis.getNextEntry()) != null) { + if (folderNameFile.getName().contains("__MACOSX/")) continue; + if (folderNameFile.isDirectory()) continue; String lowerName = folderNameFile.getName().toLowerCase(); - if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") - || lowerName.endsWith(".mcr"))) - continue; + if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue; fileNames.add(folderNameFile.getName().toCharArray()); } } final int[] i = new int[] { 0 }; - while (fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) - i[0]++; + while(fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) i[0]++; int folderPrefixOffset = i[0]; - try (ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) { + try(ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) { ZipEntry f = null; int lastProgUpdate = 0; int prog = 0; byte[] bb = new byte[16384]; while ((f = zis.getNextEntry()) != null) { - if (f.getName().contains("__MACOSX/")) - continue; - if (f.isDirectory()) - continue; + if (f.getName().contains("__MACOSX/")) continue; + if (f.isDirectory()) continue; String lowerName = f.getName().toLowerCase(); - if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") - || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) - continue; - EaglerOutputStream baos = new EaglerOutputStream(); - int len; - while ((len = zis.read(bb)) != -1) { - baos.write(bb, 0, len); + if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) continue; + byte[] b; + int sz = (int)f.getSize(); + if(sz >= 0) { + b = new byte[sz]; + int j = 0, k; + while(j < b.length && (k = zis.read(b, j, b.length - j)) != -1) { + j += k; + } + }else { + b = EaglerInputStream.inputStreamToBytes(zis); } - baos.close(); - byte[] b = baos.toByteArray(); String fileName = f.getName().substring(folderPrefixOffset); if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) { NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); - + NBTTagCompound gameRulesNBT = worldDatNBT.getCompoundTag("Data").getCompoundTag("GameRules"); gameRulesNBT.setString("loadSpawnChunks", (gameRules & 2) != 0 ? "true" : "false"); String s = (gameRules & 1) != 0 ? "true" : "false"; @@ -117,36 +106,33 @@ public class WorldConverterMCA { EaglerOutputStream bo = new EaglerOutputStream(); CompressedStreamTools.writeCompressed(worldDatNBT, bo); b = bo.toByteArray(); - VFile2 ff = new VFile2(worldDir, fileName); + VFile2 ff = WorldsDB.newVFile(worldDir, fileName); ff.setAllBytes(b); prog += b.length; - } else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") - || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) { - VFile2 chunkFolder = new VFile2(worldDir, fileName.startsWith("DIM1") ? "level1" - : (fileName.startsWith("DIM-1") ? "level-1" : "level0")); + } else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) { + VFile2 chunkFolder = WorldsDB.newVFile(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0")); RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length)); int loadChunksCount = 0; - for (int j = 0; j < 32; ++j) { - for (int k = 0; k < 32; ++k) { - if (mca.isChunkSaved(j, k)) { + for(int j = 0; j < 32; ++j) { + for(int k = 0; k < 32; ++k) { + if(mca.isChunkSaved(j, k)) { NBTTagCompound chunkNBT; NBTTagCompound chunkLevel; try { chunkNBT = CompressedStreamTools.read(mca.getChunkDataInputStream(j, k)); - if (!chunkNBT.hasKey("Level", 10)) { + if(!chunkNBT.hasKey("Level", 10)) { throw new IOException("Chunk is missing level data!"); } chunkLevel = chunkNBT.getCompoundTag("Level"); - } catch (Throwable t) { + }catch(Throwable t) { logger.error("{}: Could not read chunk: {}, {}", fileName, j, k); logger.error(t); continue; } int chunkX = chunkLevel.getInteger("xPos"); int chunkZ = chunkLevel.getInteger("zPos"); - VFile2 chunkOut = new VFile2(chunkFolder, - EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat"); - if (chunkOut.exists()) { + VFile2 chunkOut = WorldsDB.newVFile(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat"); + if(chunkOut.exists()) { logger.error("{}: Chunk already exists: {}", fileName, chunkOut.getPath()); continue; } @@ -165,10 +151,9 @@ public class WorldConverterMCA { } logger.info("{}: Imported {} chunks successfully ({} bytes)", fileName, loadChunksCount, prog); } else if (fileName.startsWith("playerdata/") || fileName.startsWith("stats/")) { - // TODO: LAN player inventories - } else if (fileName.startsWith("data/") || fileName.startsWith("players/") - || fileName.startsWith("eagler/skulls/")) { - VFile2 ff = new VFile2(worldDir, fileName); + //TODO: LAN player inventories + } else if (fileName.startsWith("data/") || fileName.startsWith("players/") || fileName.startsWith("eagler/skulls/")) { + VFile2 ff = WorldsDB.newVFile(worldDir, fileName); ff.setAllBytes(b); prog += b.length; } else if (!fileName.equals("level.dat_mcr") && !fileName.equals("session.lock")) { @@ -182,10 +167,9 @@ public class WorldConverterMCA { } logger.info("MCA was successfully extracted into directory \"{}\"", worldDir.getPath()); String[] worldsTxt = EaglerSaveFormat.worldsList.getAllLines(); - if (worldsTxt == null || worldsTxt.length <= 0 - || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) { + if(worldsTxt == null || worldsTxt.length <= 0 || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) { worldsTxt = new String[] { folderName }; - } else { + }else { String[] tmp = worldsTxt; worldsTxt = new String[worldsTxt.length + 1]; System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length); @@ -197,24 +181,24 @@ public class WorldConverterMCA { public static byte[] exportWorld(String folderName) throws IOException { EaglerOutputStream bao = new EaglerOutputStream(); VFile2 worldFolder; - try (ZipOutputStream zos = new ZipOutputStream(bao)) { + try(ZipOutputStream zos = new ZipOutputStream(bao)) { zos.setComment("contains backup of world '" + folderName + "'"); worldFolder = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); logger.info("Exporting world directory \"{}\" as MCA", worldFolder.getPath()); - VFile2 vf = new VFile2(worldFolder, "level.dat"); + VFile2 vf = WorldsDB.newVFile(worldFolder, "level.dat"); byte[] b; int lastProgUpdate = 0; int prog = 0; boolean safe = false; - if (vf.exists()) { + if(vf.exists()) { zos.putNextEntry(new ZipEntry(folderName + "/level.dat")); b = vf.getAllBytes(); zos.write(b); prog += b.length; safe = true; } - vf = new VFile2(worldFolder, "level.dat_old"); - if (vf.exists()) { + vf = WorldsDB.newVFile(worldFolder, "level.dat_old"); + if(vf.exists()) { zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old")); b = vf.getAllBytes(); zos.write(b); @@ -228,24 +212,24 @@ public class WorldConverterMCA { String[] srcFolderNames = new String[] { "level0", "level-1", "level1" }; String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" }; List fileList; - for (int i = 0; i < 3; ++i) { - vf = new VFile2(worldFolder, srcFolderNames[i]); + for(int i = 0; i < 3; ++i) { + vf = WorldsDB.newVFile(worldFolder, srcFolderNames[i]); fileList = vf.listFiles(true); String regionFolder = folderName + dstFolderNames[i]; logger.info("Converting chunks in \"{}\" as MCA to \"{}\"...", vf.getPath(), regionFolder); - Map regionFiles = new HashMap(); - for (int k = 0, l = fileList.size(); k < l; ++k) { + Map regionFiles = new HashMap<>(); + for(int k = 0, l = fileList.size(); k < l; ++k) { VFile2 chunkFile = fileList.get(k); NBTTagCompound chunkNBT; NBTTagCompound chunkLevel; try { b = chunkFile.getAllBytes(); chunkNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); - if (!chunkNBT.hasKey("Level", 10)) { + if(!chunkNBT.hasKey("Level", 10)) { throw new IOException("Chunk is missing level data!"); } chunkLevel = chunkNBT.getCompoundTag("Level"); - } catch (IOException t) { + }catch(IOException t) { logger.error("Could not read chunk: {}", chunkFile.getPath()); logger.error(t); continue; @@ -254,13 +238,13 @@ public class WorldConverterMCA { int chunkZ = chunkLevel.getInteger("zPos"); String regionFileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca"; RegionFile rf = regionFiles.get(regionFileName); - if (rf == null) { + if(rf == null) { rf = new RegionFile(new RandomAccessMemoryFile(new byte[65536], 0)); regionFiles.put(regionFileName, rf); } - try (DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) { + try(DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) { CompressedStreamTools.write(chunkNBT, dos); - } catch (IOException t) { + }catch(IOException t) { logger.error("Could not write chunk to {}: {}", regionFileName, chunkFile.getPath()); logger.error(t); continue; @@ -271,11 +255,11 @@ public class WorldConverterMCA { EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); } } - if (regionFiles.isEmpty()) { + if(regionFiles.isEmpty()) { logger.info("No region files were generated"); continue; } - for (Entry etr : regionFiles.entrySet()) { + for(Entry etr : regionFiles.entrySet()) { String regionPath = regionFolder + etr.getKey(); logger.info("Writing region file: {}", regionPath); zos.putNextEntry(new ZipEntry(regionPath)); @@ -283,8 +267,8 @@ public class WorldConverterMCA { } } logger.info("Copying extra world data..."); - fileList = (new VFile2(worldFolder, "data")).listFiles(false); - for (int k = 0, l = fileList.size(); k < l; ++k) { + fileList = WorldsDB.newVFile(worldFolder, "data").listFiles(false); + for(int k = 0, l = fileList.size(); k < l; ++k) { VFile2 dataFile = fileList.get(k); zos.putNextEntry(new ZipEntry(folderName + "/data/" + dataFile.getName())); b = dataFile.getAllBytes(); @@ -295,8 +279,8 @@ public class WorldConverterMCA { EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); } } - fileList = (new VFile2(worldFolder, "players")).listFiles(false); - for (int k = 0, l = fileList.size(); k < l; ++k) { + fileList = WorldsDB.newVFile(worldFolder, "players").listFiles(false); + for(int k = 0, l = fileList.size(); k < l; ++k) { VFile2 dataFile = fileList.get(k); zos.putNextEntry(new ZipEntry(folderName + "/players/" + dataFile.getName())); b = dataFile.getAllBytes(); @@ -307,8 +291,8 @@ public class WorldConverterMCA { EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); } } - fileList = (new VFile2(worldFolder, "eagler/skulls")).listFiles(false); - for (int k = 0, l = fileList.size(); k < l; ++k) { + fileList = WorldsDB.newVFile(worldFolder, "eagler/skulls").listFiles(false); + for(int k = 0, l = fileList.size(); k < l; ++k) { VFile2 dataFile = fileList.get(k); zos.putNextEntry(new ZipEntry(folderName + "/eagler/skulls/" + dataFile.getName())); b = dataFile.getAllBytes(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java index 5a90a9e..63dda81 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java @@ -1,39 +1,50 @@ -package net.lax1dude.eaglercraft.v1_8.sp.server.skins; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class CustomSkullData { - - public String skinURL; - public long lastHit; - public byte[] skinData; - - public CustomSkullData(String skinURL, byte[] skinData) { - this.skinURL = skinURL; - this.lastHit = System.currentTimeMillis(); - this.skinData = skinData; - } - - public byte[] getFullSkin() { - if(skinData.length == 16384) { - return skinData; - } - byte[] ret = new byte[16384]; - System.arraycopy(skinData, 0, ret, 0, skinData.length > ret.length ? ret.length : skinData.length); - return ret; - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV3EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CustomSkullData { + + public String skinURL; + public long lastHit; + public SkinPacketVersionCache skinData; + + public CustomSkullData(String skinURL, byte[] skinData) { + this.skinURL = skinURL; + this.lastHit = EagRuntime.steadyTimeMillis(); + if(skinData.length != 16384) { + byte[] fixed = new byte[16384]; + System.arraycopy(skinData, 0, fixed, 0, skinData.length > fixed.length ? fixed.length : skinData.length); + skinData = fixed; + } + this.skinData = SkinPacketVersionCache.createCustomV3(0l, 0l, 0, skinData); + } + + public byte[] getFullSkin() { + return ((SPacketOtherSkinCustomV3EAG)skinData.getV3()).customSkin; + } + + public GameMessagePacket getSkinPacket(EaglercraftUUID uuid, GamePluginMessageProtocol protocol) { + return SkinPacketVersionCache.rewriteUUID(skinData.get(protocol), uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java index f1f63f6..6df2b5c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java @@ -3,7 +3,9 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.skins; import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.minecraft.entity.player.EntityPlayerMP; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -24,56 +26,27 @@ public class IntegratedCapePackets { public static final int PACKET_MY_CAPE_PRESET = 0x01; public static final int PACKET_MY_CAPE_CUSTOM = 0x02; - public static final int PACKET_GET_OTHER_CAPE = 0x03; - public static final int PACKET_OTHER_CAPE_PRESET = 0x04; - public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05; - - public static void processPacket(byte[] data, EntityPlayerMP sender, IntegratedCapeService capeService) throws IOException { - if(data.length == 0) { - throw new IOException("Zero-length packet recieved"); - } - int packetId = (int)data[0] & 0xFF; - try { - switch(packetId) { - case PACKET_GET_OTHER_CAPE: - processGetOtherCape(data, sender, capeService); - break; - default: - throw new IOException("Unknown packet type " + packetId); - } - }catch(IOException ex) { - throw ex; - }catch(Throwable t) { - throw new IOException("Unhandled exception handling packet type " + packetId, t); - } - } - - private static void processGetOtherCape(byte[] data, EntityPlayerMP sender, IntegratedCapeService capeService) throws IOException { - if(data.length != 17) { - throw new IOException("Invalid length " + data.length + " for skin request packet"); - } - EaglercraftUUID searchUUID = IntegratedSkinPackets.bytesToUUID(data, 1); - capeService.processGetOtherCape(searchUUID, sender); - } public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedCapeService capeService) throws IOException { if(bs.length == 0) { throw new IOException("Zero-length packet recieved"); } - byte[] generatedPacket; + GameMessagePacket generatedPacket; int packetType = (int)bs[0] & 0xFF; switch(packetType) { case PACKET_MY_CAPE_PRESET: if(bs.length != 5) { throw new IOException("Invalid length " + bs.length + " for preset cape packet"); } - generatedPacket = IntegratedCapePackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); + generatedPacket = new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); break; case PACKET_MY_CAPE_CUSTOM: if(bs.length != 1174) { throw new IOException("Invalid length " + bs.length + " for custom cape packet"); } - generatedPacket = IntegratedCapePackets.makeCustomResponse(clientUUID, bs, 1, 1173); + byte[] capePixels = new byte[bs.length - 1]; + System.arraycopy(bs, 1, capePixels, 0, capePixels.length); + generatedPacket = new SPacketOtherCapeCustomEAG(clientUUID.msb, clientUUID.lsb, capePixels); break; default: throw new IOException("Unknown skin packet type: " + packetType); @@ -82,29 +55,7 @@ public class IntegratedCapePackets { } public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedCapeService capeService) { - capeService.registerEaglercraftPlayer(clientUUID, IntegratedCapePackets.makePresetResponse(clientUUID, 0)); + capeService.registerEaglercraftPlayer(clientUUID, new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, 0)); } - public static byte[] makePresetResponse(EaglercraftUUID uuid, int presetId) { - byte[] ret = new byte[1 + 16 + 4]; - ret[0] = (byte)PACKET_OTHER_CAPE_PRESET; - IntegratedSkinPackets.UUIDToBytes(uuid, ret, 1); - ret[17] = (byte)(presetId >>> 24); - ret[18] = (byte)(presetId >>> 16); - ret[19] = (byte)(presetId >>> 8); - ret[20] = (byte)(presetId & 0xFF); - return ret; - } - - public static byte[] makeCustomResponse(EaglercraftUUID uuid, byte[] pixels) { - return makeCustomResponse(uuid, pixels, 0, pixels.length); - } - - public static byte[] makeCustomResponse(EaglercraftUUID uuid, byte[] pixels, int offset, int length) { - byte[] ret = new byte[1 + 16 + length]; - ret[0] = (byte)PACKET_OTHER_CAPE_CUSTOM; - IntegratedSkinPackets.UUIDToBytes(uuid, ret, 1); - System.arraycopy(pixels, offset, ret, 17, length); - return ret; - } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java index 3d4014d..7e17e67 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java @@ -7,10 +7,9 @@ import java.util.Map; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.play.server.S3FPacketCustomPayload; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -33,19 +32,7 @@ public class IntegratedCapeService { public static final int masterRateLimitPerPlayer = 250; - public static final String CHANNEL = "EAG|Capes-1.8"; - - private final Map capesCache = new HashMap(); - - public void processPacket(byte[] packetData, EntityPlayerMP sender) { - try { - IntegratedCapePackets.processPacket(packetData, sender, this); - } catch (IOException e) { - logger.error("Invalid skin request packet recieved from player {}!", sender.getName()); - logger.error(e); - sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin request packet recieved!"); - } - } + private final Map capesCache = new HashMap<>(); public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) { try { @@ -57,16 +44,16 @@ public class IntegratedCapeService { } } - public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, byte[] capePacket) { + public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, GameMessagePacket capePacket) { capesCache.put(playerUUID, capePacket); } public void processGetOtherCape(EaglercraftUUID searchUUID, EntityPlayerMP sender) { - byte[] maybeCape = capesCache.get(searchUUID); + GameMessagePacket maybeCape = capesCache.get(searchUUID); if(maybeCape == null) { - maybeCape = IntegratedCapePackets.makePresetResponse(searchUUID, 0); + maybeCape = new SPacketOtherCapePresetEAG(searchUUID.msb, searchUUID.lsb, 0); } - sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(maybeCape, maybeCape.length).writerIndex(maybeCape.length)))); + sender.playerNetServerHandler.sendEaglerMessage(maybeCape); } public void unregisterPlayer(EaglercraftUUID playerUUID) { @@ -74,4 +61,4 @@ public class IntegratedCapeService { capesCache.remove(playerUUID); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java index 4c93d56..67da9f9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java @@ -3,7 +3,9 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.skins; import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.minecraft.entity.player.EntityPlayerMP; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; /** * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. @@ -24,76 +26,14 @@ public class IntegratedSkinPackets { public static final int PACKET_MY_SKIN_PRESET = 0x01; public static final int PACKET_MY_SKIN_CUSTOM = 0x02; - public static final int PACKET_GET_OTHER_SKIN = 0x03; - public static final int PACKET_OTHER_SKIN_PRESET = 0x04; - public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05; - public static final int PACKET_GET_SKIN_BY_URL = 0x06; - public static final int PACKET_INSTALL_NEW_SKIN = 0x07; - public static void processPacket(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException { - if(data.length == 0) { - throw new IOException("Zero-length packet recieved"); - } - int packetId = (int)data[0] & 0xFF; - try { - switch(packetId) { - case PACKET_GET_OTHER_SKIN: - processGetOtherSkin(data, sender, skinService); - break; - case PACKET_GET_SKIN_BY_URL: - processGetOtherSkinByURL(data, sender, skinService); - break; - case PACKET_INSTALL_NEW_SKIN: - processInstallNewSkin(data, sender, skinService); - break; - default: - throw new IOException("Unknown packet type " + packetId); - } - }catch(IOException ex) { - throw ex; - }catch(Throwable t) { - throw new IOException("Unhandled exception handling packet type " + packetId, t); - } - } - - private static void processGetOtherSkin(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException { - if(data.length != 17) { - throw new IOException("Invalid length " + data.length + " for skin request packet"); - } - EaglercraftUUID searchUUID = bytesToUUID(data, 1); - skinService.processPacketGetOtherSkin(searchUUID, sender); - } - - private static void processGetOtherSkinByURL(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException { - if(data.length < 20) { - throw new IOException("Invalid length " + data.length + " for skin request packet"); - } - EaglercraftUUID searchUUID = bytesToUUID(data, 1); - int urlLength = (data[17] << 8) | data[18]; - if(data.length < 19 + urlLength) { - throw new IOException("Invalid length " + data.length + " for skin request packet with " + urlLength + " length URL"); - } - skinService.processPacketGetOtherSkin(searchUUID, bytesToAscii(data, 19, urlLength), sender); - } - - private static void processInstallNewSkin(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException { - if(data.length < 3) { - throw new IOException("Invalid length " + data.length + " for skin data packet"); - } - int dataLength = (data[1] << 8) | data[2]; - byte[] dataBmp = new byte[dataLength]; - if(data.length != dataLength + 3) { - throw new IOException("Invalid data length " + dataLength + " for " + data.length + " byte skin data packet"); - } - System.arraycopy(data, 3, dataBmp, 0, dataLength); - skinService.processPacketInstallNewSkin(dataBmp, sender); - } - - public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedSkinService skinService) throws IOException { + public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedSkinService skinService, + int protocolVers) throws IOException { if(bs.length == 0) { throw new IOException("Zero-length packet recieved"); } - byte[] generatedPacket; + GameMessagePacket generatedPacketV3 = null; + GameMessagePacket generatedPacketV4 = null; int skinModel = -1; int packetType = (int)bs[0] & 0xFF; switch(packetType) { @@ -101,118 +41,63 @@ public class IntegratedSkinPackets { if(bs.length != 5) { throw new IOException("Invalid length " + bs.length + " for preset skin packet"); } - generatedPacket = makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); + generatedPacketV3 = generatedPacketV4 = new SPacketOtherSkinPresetEAG(clientUUID.msb, clientUUID.lsb, + (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); break; case PACKET_MY_SKIN_CUSTOM: - byte[] pixels = new byte[16384]; - if(bs.length != 2 + pixels.length) { - throw new IOException("Invalid length " + bs.length + " for custom skin packet"); + if(protocolVers <= 3) { + byte[] pixels = new byte[16384]; + if(bs.length != 2 + pixels.length) { + throw new IOException("Invalid length " + bs.length + " for custom skin packet"); + } + setAlphaForChestV3(pixels); + System.arraycopy(bs, 2, pixels, 0, pixels.length); + generatedPacketV3 = new SPacketOtherSkinCustomV3EAG(clientUUID.msb, clientUUID.lsb, (skinModel = (int)bs[1] & 0xFF), pixels); + }else { + byte[] pixels = new byte[12288]; + if(bs.length != 2 + pixels.length) { + throw new IOException("Invalid length " + bs.length + " for custom skin packet"); + } + setAlphaForChestV4(pixels); + System.arraycopy(bs, 2, pixels, 0, pixels.length); + generatedPacketV4 = new SPacketOtherSkinCustomV4EAG(clientUUID.msb, clientUUID.lsb, (skinModel = (int)bs[1] & 0xFF), pixels); } - setAlphaForChest(pixels, (byte)255); - System.arraycopy(bs, 2, pixels, 0, pixels.length); - generatedPacket = makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), pixels); break; default: throw new IOException("Unknown skin packet type: " + packetType); } - skinService.processPacketPlayerSkin(clientUUID, generatedPacket, skinModel); + skinService.processPacketPlayerSkin(clientUUID, new SkinPacketVersionCache(generatedPacketV3, generatedPacketV4), skinModel); } public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedSkinService skinService) throws IOException { int skinModel = (clientUUID.hashCode() & 1) != 0 ? 1 : 0; - byte[] generatedPacket = makePresetResponse(clientUUID, skinModel); - skinService.processPacketPlayerSkin(clientUUID, generatedPacket, skinModel); + skinService.processPacketPlayerSkin(clientUUID, SkinPacketVersionCache.createPreset(clientUUID.msb, clientUUID.lsb, skinModel), skinModel); } - public static void setAlphaForChest(byte[] skin64x64, byte alpha) { + public static void setAlphaForChestV3(byte[] skin64x64) { if(skin64x64.length != 16384) { throw new IllegalArgumentException("Skin is not 64x64!"); } for(int y = 20; y < 32; ++y) { for(int x = 16; x < 40; ++x) { - skin64x64[(y << 8) | (x << 2)] = alpha; + skin64x64[(y << 8) | (x << 2)] = (byte)0xFF; } } } - public static byte[] makePresetResponse(EaglercraftUUID uuid) { - return makePresetResponse(uuid, (uuid.hashCode() & 1) != 0 ? 1 : 0); - } - - public static byte[] makePresetResponse(EaglercraftUUID uuid, int presetId) { - byte[] ret = new byte[1 + 16 + 4]; - ret[0] = (byte)PACKET_OTHER_SKIN_PRESET; - UUIDToBytes(uuid, ret, 1); - ret[17] = (byte)(presetId >>> 24); - ret[18] = (byte)(presetId >>> 16); - ret[19] = (byte)(presetId >>> 8); - ret[20] = (byte)(presetId & 0xFF); - return ret; - } - - public static byte[] makeCustomResponse(EaglercraftUUID uuid, int model, byte[] pixels) { - byte[] ret = new byte[1 + 16 + 1 + pixels.length]; - ret[0] = (byte)PACKET_OTHER_SKIN_CUSTOM; - UUIDToBytes(uuid, ret, 1); - ret[17] = (byte)model; - System.arraycopy(pixels, 0, ret, 18, pixels.length); - return ret; - } - - public static EaglercraftUUID bytesToUUID(byte[] bytes, int off) { - long msb = (((long) bytes[off] & 0xFFl) << 56l) | (((long) bytes[off + 1] & 0xFFl) << 48l) - | (((long) bytes[off + 2] & 0xFFl) << 40l) | (((long) bytes[off + 3] & 0xFFl) << 32l) - | (((long) bytes[off + 4] & 0xFFl) << 24l) | (((long) bytes[off + 5] & 0xFFl) << 16l) - | (((long) bytes[off + 6] & 0xFFl) << 8l) | ((long) bytes[off + 7] & 0xFFl); - long lsb = (((long) bytes[off + 8] & 0xFFl) << 56l) | (((long) bytes[off + 9] & 0xFFl) << 48l) - | (((long) bytes[off + 10] & 0xFFl) << 40l) | (((long) bytes[off + 11] & 0xFFl) << 32l) - | (((long) bytes[off + 12] & 0xFFl) << 24l) | (((long) bytes[off + 13] & 0xFFl) << 16l) - | (((long) bytes[off + 14] & 0xFFl) << 8l) | ((long) bytes[off + 15] & 0xFFl); - return new EaglercraftUUID(msb, lsb); - } - - private static final String hex = "0123456789abcdef"; - - public static String bytesToString(byte[] bytes, int off, int len) { - char[] ret = new char[len << 1]; - for(int i = 0; i < len; ++i) { - ret[i * 2] = hex.charAt((bytes[off + i] >> 4) & 0xF); - ret[i * 2 + 1] = hex.charAt(bytes[off + i] & 0xF); + public static void setAlphaForChestV4(byte[] skin64x64) { + if(skin64x64.length != 12288) { + throw new IllegalArgumentException("Skin is not 64x64!"); } - return new String(ret); - } - - public static String bytesToAscii(byte[] bytes, int off, int len) { - char[] ret = new char[len]; - for(int i = 0; i < len; ++i) { - ret[i] = (char)((int)bytes[off + i] & 0xFF); + for(int y = 20; y < 32; ++y) { + for(int x = 16; x < 40; ++x) { + skin64x64[((y << 6) | x) * 3] |= 0x80; + } } - return new String(ret); - } - - public static String bytesToAscii(byte[] bytes) { - return bytesToAscii(bytes, 0, bytes.length); } - public static void UUIDToBytes(EaglercraftUUID uuid, byte[] bytes, int off) { - long msb = uuid.getMostSignificantBits(); - long lsb = uuid.getLeastSignificantBits(); - bytes[off] = (byte)(msb >>> 56l); - bytes[off + 1] = (byte)(msb >>> 48l); - bytes[off + 2] = (byte)(msb >>> 40l); - bytes[off + 3] = (byte)(msb >>> 32l); - bytes[off + 4] = (byte)(msb >>> 24l); - bytes[off + 5] = (byte)(msb >>> 16l); - bytes[off + 6] = (byte)(msb >>> 8l); - bytes[off + 7] = (byte)(msb & 0xFFl); - bytes[off + 8] = (byte)(lsb >>> 56l); - bytes[off + 9] = (byte)(lsb >>> 48l); - bytes[off + 10] = (byte)(lsb >>> 40l); - bytes[off + 11] = (byte)(lsb >>> 32l); - bytes[off + 12] = (byte)(lsb >>> 24l); - bytes[off + 13] = (byte)(lsb >>> 16l); - bytes[off + 14] = (byte)(lsb >>> 8l); - bytes[off + 15] = (byte)(lsb & 0xFFl); + public static SPacketOtherSkinPresetEAG makePresetResponse(EaglercraftUUID uuid) { + return new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, (uuid.hashCode() & 1) != 0 ? 1 : 0); } public static byte[] asciiString(String string) { @@ -231,21 +116,4 @@ public class IntegratedSkinPackets { return "slim".equalsIgnoreCase(modelName) ? 1 : 0; } - public static byte[] rewriteUUID(EaglercraftUUID newUUID, byte[] pkt) { - byte[] ret = new byte[pkt.length]; - System.arraycopy(pkt, 0, ret, 0, pkt.length); - UUIDToBytes(newUUID, ret, 1); - return ret; - } - - public static byte[] rewriteUUIDModel(EaglercraftUUID newUUID, byte[] pkt, int model) { - byte[] ret = new byte[pkt.length]; - System.arraycopy(pkt, 0, ret, 0, pkt.length); - UUIDToBytes(newUUID, ret, 1); - if(ret[0] == (byte)PACKET_OTHER_SKIN_CUSTOM) { - ret[17] = (byte)model; - } - return ret; - } - } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java index e2a8917..0aa1ce1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java @@ -7,17 +7,19 @@ import java.util.Iterator; import java.util.Map; import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.play.server.S3FPacketCustomPayload; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; import net.minecraft.nbt.NBTTagCompound; @@ -43,8 +45,6 @@ public class IntegratedSkinService { public static final Logger logger = LogManager.getLogger("IntegratedSkinService"); - public static final String CHANNEL = "EAG|Skins-1.8"; - public static final byte[] skullNotFoundTexture = new byte[4096]; static { @@ -62,8 +62,8 @@ public class IntegratedSkinService { public final VFile2 skullsDirectory; - public final Map playerSkins = new HashMap(); - public final Map customSkulls = new HashMap(); + public final Map playerSkins = new HashMap<>(); + public final Map customSkulls = new HashMap<>(); private long lastFlush = 0l; @@ -71,19 +71,9 @@ public class IntegratedSkinService { this.skullsDirectory = skullsDirectory; } - public void processPacket(byte[] packetData, EntityPlayerMP sender) { + public void processLoginPacket(byte[] packetData, EntityPlayerMP sender, int protocolVers) { try { - IntegratedSkinPackets.processPacket(packetData, sender, this); - } catch (IOException e) { - logger.error("Invalid skin request packet recieved from player {}!", sender.getName()); - logger.error(e); - sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin request packet recieved!"); - } - } - - public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) { - try { - IntegratedSkinPackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this); + IntegratedSkinPackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this, protocolVers); } catch (IOException e) { logger.error("Invalid skin data packet recieved from player {}!", sender.getName()); logger.error(e); @@ -92,36 +82,39 @@ public class IntegratedSkinService { } public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, EntityPlayerMP sender) { - byte[] playerSkin = playerSkins.get(searchUUID); - if(playerSkin == null) { - playerSkin = IntegratedSkinPackets.makePresetResponse(searchUUID); + SkinPacketVersionCache playerSkin = playerSkins.get(searchUUID); + GameMessagePacket toSend = null; + if(playerSkin != null) { + toSend = playerSkin.get(sender.playerNetServerHandler.getEaglerMessageProtocol()); + }else { + toSend = IntegratedSkinPackets.makePresetResponse(searchUUID); } - sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(playerSkin, playerSkin.length).writerIndex(playerSkin.length)))); + sender.playerNetServerHandler.sendEaglerMessage(toSend); } public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, String urlStr, EntityPlayerMP sender) { urlStr = urlStr.toLowerCase(); - byte[] playerSkin; + GameMessagePacket playerSkin; if(!urlStr.startsWith("eagler://")) { - playerSkin = IntegratedSkinPackets.makePresetResponse(searchUUID, 0); + playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0); }else { urlStr = urlStr.substring(9); if(urlStr.contains(VFile2.pathSeperator)) { - playerSkin = IntegratedSkinPackets.makePresetResponse(searchUUID, 0); + playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0); }else { CustomSkullData sk = customSkulls.get(urlStr); if(sk == null) { customSkulls.put(urlStr, sk = loadCustomSkull(urlStr)); }else { - sk.lastHit = System.currentTimeMillis(); + sk.lastHit = EagRuntime.steadyTimeMillis(); } - playerSkin = IntegratedSkinPackets.makeCustomResponse(searchUUID, 0, sk.getFullSkin()); + playerSkin = sk.getSkinPacket(searchUUID, sender.playerNetServerHandler.getEaglerMessageProtocol()); } } - sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(playerSkin, playerSkin.length).writerIndex(playerSkin.length)))); + sender.playerNetServerHandler.sendEaglerMessage(playerSkin); } - public void processPacketPlayerSkin(EaglercraftUUID clientUUID, byte[] generatedPacket, int skinModel) { + public void processPacketPlayerSkin(EaglercraftUUID clientUUID, SkinPacketVersionCache generatedPacket, int skinModel) { playerSkins.put(clientUUID, generatedPacket); } @@ -188,12 +181,12 @@ public class IntegratedSkinService { } String str = "skin-" + new String(hashText) + ".bmp"; customSkulls.put(str, new CustomSkullData(str, skullData)); - (new VFile2(skullsDirectory, str)).setAllBytes(skullData); + WorldsDB.newVFile(skullsDirectory, str).setAllBytes(skullData); return str; } private CustomSkullData loadCustomSkull(String urlStr) { - byte[] data = (new VFile2(skullsDirectory, urlStr)).getAllBytes(); + byte[] data = WorldsDB.newVFile(skullsDirectory, urlStr).getAllBytes(); if(data == null) { return new CustomSkullData(urlStr, skullNotFoundTexture); }else { @@ -202,7 +195,7 @@ public class IntegratedSkinService { } public void flushCache() { - long cur = System.currentTimeMillis(); + long cur = EagRuntime.steadyTimeMillis(); if(cur - lastFlush > 300000l) { lastFlush = cur; Iterator customSkullsItr = customSkulls.values().iterator(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java index 68ce33a..419c69b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java @@ -28,24 +28,16 @@ import net.minecraft.util.ITickable; import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -64,85 +56,81 @@ public class IntegratedServerPlayerNetworkManager { private boolean firstPacket = true; - private List fragmentedPacket = new ArrayList(); + private List fragmentedPacket = new ArrayList<>(); public static final int fragmentSize = 0xFF00; public static final int compressionThreshold = 1024; - + public static final Logger logger = LogManager.getLogger("NetworkManager"); public IntegratedServerPlayerNetworkManager(String playerChannel) { - if (temporaryBuffer == null) { + if(temporaryBuffer == null) { temporaryBuffer = new PacketBuffer(Unpooled.buffer(0x1FFFF)); } this.playerChannel = playerChannel; this.enableSendCompression = !SingleplayerServerController.PLAYER_CHANNEL.equals(playerChannel); - if (this.enableSendCompression) { - if (temporaryOutputStream == null) { + if(this.enableSendCompression) { + if(temporaryOutputStream == null) { temporaryOutputStream = new EaglerOutputStream(16386); } } } - + public void connect() { fragmentedPacket.clear(); firstPacket = true; } - + public EnumEaglerConnectionState getConnectStatus() { - return EaglerIntegratedServerWorker.getChannelExists(playerChannel) ? EnumEaglerConnectionState.CONNECTED - : EnumEaglerConnectionState.CLOSED; + return EaglerIntegratedServerWorker.getChannelExists(playerChannel) ? EnumEaglerConnectionState.CONNECTED : EnumEaglerConnectionState.CLOSED; } - + public void closeChannel(IChatComponent reason) { EaglerIntegratedServerWorker.closeChannel(playerChannel); - if (nethandler != null) { + if(nethandler != null) { nethandler.onDisconnect(reason); } } - + public void setConnectionState(EnumConnectionState state) { packetState = state; } public void addRecievedPacket(byte[] next) { - if (recievedPacketBufferCounter < recievedPacketBuffer.length - 1) { + if(recievedPacketBufferCounter < recievedPacketBuffer.length - 1) { recievedPacketBuffer[recievedPacketBufferCounter++] = next; - } else { + }else { logger.error("Dropping packets on recievedPacketBuffer for channel \"{}\"! (overflow)", playerChannel); } } public void processReceivedPackets() { - if (nethandler == null) - return; + if(nethandler == null) return; - for (int i = 0; i < recievedPacketBufferCounter; ++i) { + + for(int i = 0; i < recievedPacketBufferCounter; ++i) { byte[] data = recievedPacketBuffer[i]; byte[] fullData; - if (enableSendCompression) { - if (firstPacket) { - if (data.length > 2 && data[0] == (byte) 0x02 && data[1] == (byte) 0x3D) { + if(enableSendCompression) { + if(firstPacket) { + if(data.length > 2 && data[0] == (byte)0x02 && data[1] == (byte)0x3D) { EaglerOutputStream kickPacketBAO = new EaglerOutputStream(); try { DataOutputStream kickDAO = new DataOutputStream(kickPacketBAO); kickDAO.write(0); kickDAO.write(0xFF); - String msg = "This is an EaglercraftL 1.9 LAN world!"; + String msg = "This is an EaglercraftX 1.8 LAN world!"; kickDAO.write(0x00); kickDAO.write(msg.length()); - for (int j = 0, l = msg.length(); j < l; ++j) { - kickDAO.write(0); - kickDAO.write(msg.codePointAt(j)); + for(int j = 0, l = msg.length(); j < l; ++j) { + kickDAO.writeChar(msg.charAt(j)); } - } catch (IOException ex) { + }catch(IOException ex) { throw new RuntimeException(ex); } - ServerPlatformSingleplayer - .sendPacket(new IPCPacketData(playerChannel, kickPacketBAO.toByteArray())); - closeChannel(new ChatComponentText( - "Recieved unsuppoorted connection from an Eaglercraft 1.5.2 client!")); + ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, kickPacketBAO.toByteArray())); + closeChannel(new ChatComponentText("Recieved unsuppoorted connection from an Eaglercraft 1.5.2 client!")); firstPacket = false; recievedPacketBufferCounter = 0; return; @@ -150,19 +138,19 @@ public class IntegratedServerPlayerNetworkManager { firstPacket = false; } if (data[0] == 0) { - if (fragmentedPacket.isEmpty()) { + if(fragmentedPacket.isEmpty()) { fullData = new byte[data.length - 1]; System.arraycopy(data, 1, fullData, 0, fullData.length); - } else { + }else { fragmentedPacket.add(data); int len = 0; int fragCount = fragmentedPacket.size(); - for (int j = 0; j < fragCount; ++j) { + for(int j = 0; j < fragCount; ++j) { len += fragmentedPacket.get(j).length - 1; } fullData = new byte[len]; len = 0; - for (int j = 0; j < fragCount; ++j) { + for(int j = 0; j < fragCount; ++j) { byte[] f = fragmentedPacket.get(j); System.arraycopy(f, 1, fullData, len, f.length - 1); len += f.length - 1; @@ -173,14 +161,14 @@ public class IntegratedServerPlayerNetworkManager { fragmentedPacket.add(data); continue; } else { - logger.error("Recieved {} byte fragment of unknown type: {}", data.length, ((int) data[0] & 0xFF)); + logger.error("Recieved {} byte fragment of unknown type: {}", data.length, ((int)data[0] & 0xFF)); continue; } - - } else { + + }else { fullData = data; } - + recievedPacketBuffer[i] = null; ++debugPacketCounter; try { @@ -188,36 +176,33 @@ public class IntegratedServerPlayerNetworkManager { nettyBuffer.writerIndex(fullData.length); PacketBuffer input = new PacketBuffer(nettyBuffer); int pktId = input.readVarIntFromBuffer(); - + Packet pkt; try { pkt = packetState.getPacket(EnumPacketDirection.SERVERBOUND, pktId); - } catch (IllegalAccessException | InstantiationException ex) { + }catch(IllegalAccessException | InstantiationException ex) { throw new IOException("Recieved a packet with type " + pktId + " which is invalid!"); } - if (pkt == null) { - throw new IOException( - "Recieved packet type " + pktId + " which is undefined in state " + packetState); + if(pkt == null) { + throw new IOException("Recieved packet type " + pktId + " which is undefined in state " + packetState); } try { pkt.readPacketData(input); - } catch (Throwable t) { + }catch(Throwable t) { throw new IOException("Failed to read packet type '" + pkt.getClass().getSimpleName() + "'", t); } - + try { pkt.processPacket(nethandler); - } catch (Throwable t) { - logger.error("Failed to process {}! It'll be skipped for debug purposes.", - pkt.getClass().getSimpleName()); + }catch(Throwable t) { + logger.error("Failed to process {}! It'll be skipped for debug purposes.", pkt.getClass().getSimpleName()); logger.error(t); } - - } catch (Throwable t) { - logger.error("Failed to process socket frame {}! It'll be skipped for debug purposes.", - debugPacketCounter); + + }catch(Throwable t) { + logger.error("Failed to process socket frame {}! It'll be skipped for debug purposes.", debugPacketCounter); logger.error(t); } } @@ -225,30 +210,30 @@ public class IntegratedServerPlayerNetworkManager { } public void sendPacket(Packet pkt) { - if (!isChannelOpen()) { + if(!isChannelOpen()) { return; } - + int i; try { i = packetState.getPacketId(EnumPacketDirection.CLIENTBOUND, pkt); - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Incorrect packet for state: {}", pkt.getClass().getSimpleName()); return; } - + temporaryBuffer.clear(); temporaryBuffer.writeVarIntToBuffer(i); try { pkt.writePacketData(temporaryBuffer); - } catch (IOException ex) { + }catch(IOException ex) { logger.error("Failed to write packet {}!", pkt.getClass().getSimpleName()); return; } - + int len = temporaryBuffer.readableBytes(); - if (enableSendCompression) { - if (len > compressionThreshold) { + if(enableSendCompression) { + if(len > compressionThreshold) { temporaryOutputStream.reset(); byte[] compressedData; try { @@ -257,59 +242,57 @@ public class IntegratedServerPlayerNetworkManager { temporaryOutputStream.write((len >>> 16) & 0xFF); temporaryOutputStream.write((len >>> 8) & 0xFF); temporaryOutputStream.write(len & 0xFF); - try (OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream)) { + try(OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream)) { temporaryBuffer.readBytes(os, len); } compressedData = temporaryOutputStream.toByteArray(); - } catch (IOException ex) { + }catch(IOException ex) { logger.error("Failed to compress packet {}!", pkt.getClass().getSimpleName()); return; } - if (compressedData.length > fragmentSize) { + if(compressedData.length > fragmentSize) { int fragmentSizeN1 = fragmentSize - 1; for (int j = 1; j < compressedData.length; j += fragmentSizeN1) { - byte[] fragData = new byte[((j + fragmentSizeN1 > (compressedData.length - 1)) - ? ((compressedData.length - 1) % fragmentSizeN1) - : fragmentSizeN1) + 1]; + byte[] fragData = new byte[((j + fragmentSizeN1 > (compressedData.length - 1)) ? ((compressedData.length - 1) % fragmentSizeN1) : fragmentSizeN1) + 1]; System.arraycopy(compressedData, j, fragData, 1, fragData.length - 1); fragData[0] = (j + fragmentSizeN1 < compressedData.length) ? (byte) 1 : (byte) 2; ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, fragData)); } - } else { + }else { ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, compressedData)); } - } else { + }else { int fragmentSizeN1 = fragmentSize - 1; - if (len > fragmentSizeN1) { + if(len > fragmentSizeN1) { do { int readLen = len > fragmentSizeN1 ? fragmentSizeN1 : len; byte[] frag = new byte[readLen + 1]; temporaryBuffer.readBytes(frag, 1, readLen); - frag[0] = temporaryBuffer.readableBytes() == 0 ? (byte) 0 : (byte) 1; + frag[0] = temporaryBuffer.readableBytes() == 0 ? (byte)0 : (byte)1; ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, frag)); - } while ((len = temporaryBuffer.readableBytes()) > 0); - } else { + }while((len = temporaryBuffer.readableBytes()) > 0); + }else { byte[] bytes = new byte[len + 1]; bytes[0] = 0; temporaryBuffer.readBytes(bytes, 1, len); ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, bytes)); } } - } else { + }else { byte[] bytes = new byte[len]; temporaryBuffer.readBytes(bytes, 0, len); ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, bytes)); } } - + public void setNetHandler(INetHandler nethandler) { this.nethandler = nethandler; } - + public boolean isLocalChannel() { return false; } - + public boolean isChannelOpen() { return getConnectStatus() == EnumEaglerConnectionState.CONNECTED; } @@ -324,8 +307,8 @@ public class IntegratedServerPlayerNetworkManager { public void tick() { processReceivedPackets(); - if (nethandler instanceof ITickable) { - ((ITickable) nethandler).update(); + if(nethandler instanceof ITickable) { + ((ITickable)nethandler).update(); } } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java new file mode 100755 index 0000000..98f1163 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java @@ -0,0 +1,90 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; +import net.minecraft.network.NetHandlerPlayServer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerV3MessageHandler implements GameMessageHandler { + + private final NetHandlerPlayServer netHandler; + private final EaglerMinecraftServer server; + + public ServerV3MessageHandler(NetHandlerPlayServer netHandler) { + this.netHandler = netHandler; + this.server = (EaglerMinecraftServer)netHandler.serverController; + } + + public void handleClient(CPacketGetOtherCapeEAG packet) { + server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + + public void handleClient(CPacketGetOtherSkinEAG packet) { + server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + + public void handleClient(CPacketGetSkinByURLEAG packet) { + server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity); + } + + public void handleClient(CPacketInstallSkinSPEAG packet) { + server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity); + } + + public void handleClient(CPacketVoiceSignalConnectEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeConnect(netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalDescEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.desc, netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalDisconnectV3EAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + if(packet.isPeerType) { + voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + }else { + voiceSvc.handleVoiceSignalPacketTypeDisconnect(netHandler.playerEntity); + } + } + } + + public void handleClient(CPacketVoiceSignalICEEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalRequestEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java new file mode 100755 index 0000000..d980fb4 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java @@ -0,0 +1,104 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherPlayerClientUUIDV4EAG; +import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerV4MessageHandler implements GameMessageHandler { + + private final NetHandlerPlayServer netHandler; + private final EaglerMinecraftServer server; + + public ServerV4MessageHandler(NetHandlerPlayServer netHandler) { + this.netHandler = netHandler; + this.server = (EaglerMinecraftServer)netHandler.serverController; + } + + public void handleClient(CPacketGetOtherCapeEAG packet) { + server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + + public void handleClient(CPacketGetOtherSkinEAG packet) { + server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + + public void handleClient(CPacketGetSkinByURLEAG packet) { + server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity); + } + + public void handleClient(CPacketInstallSkinSPEAG packet) { + server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity); + } + + public void handleClient(CPacketVoiceSignalConnectEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeConnect(netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalDescEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.desc, netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalDisconnectV4EAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeDisconnect(netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalDisconnectPeerV4EAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalICEEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity); + } + } + + public void handleClient(CPacketVoiceSignalRequestEAG packet) { + IntegratedVoiceService voiceSvc = server.getVoiceService(); + if(voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } + } + + public void handleClient(CPacketGetOtherClientUUIDV4EAG packet) { + EntityPlayerMP player = server.getConfigurationManager().getPlayerByUUID(new EaglercraftUUID(packet.playerUUIDMost, packet.playerUUIDLeast)); + if(player != null && player.clientBrandUUID != null) { + netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, player.clientBrandUUID.msb, player.clientBrandUUID.lsb)); + }else { + netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, 0l, 0l)); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java index 5c640bb..f6a802a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java @@ -1,6 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.voice; -import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -10,11 +11,10 @@ import java.util.Set; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; import net.lax1dude.eaglercraft.v1_8.voice.ExpiringSet; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.play.server.S3FPacketCustomPayload; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -35,20 +35,18 @@ public class IntegratedVoiceService { public static final Logger logger = LogManager.getLogger("IntegratedVoiceService"); - public static final String CHANNEL = "EAG|Voice-1.8"; - - private byte[] iceServersPacket; + private GameMessagePacket iceServersPacket; private final Map voicePlayers = new HashMap<>(); private final Map> voiceRequests = new HashMap<>(); private final Set voicePairs = new HashSet<>(); public IntegratedVoiceService(String[] iceServers) { - iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers); + iceServersPacket = new SPacketVoiceSignalAllowedEAG(true, iceServers); } public void changeICEServers(String[] iceServers) { - iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers); + iceServersPacket = new SPacketVoiceSignalAllowedEAG(true, iceServers); } private static class VoicePair { @@ -85,25 +83,14 @@ public class IntegratedVoiceService { } public void handlePlayerLoggedIn(EntityPlayerMP player) { - player.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer( - Unpooled.buffer(iceServersPacket, iceServersPacket.length).writerIndex(iceServersPacket.length)))); + player.playerNetServerHandler.sendEaglerMessage(iceServersPacket); } public void handlePlayerLoggedOut(EntityPlayerMP player) { removeUser(player.getUniqueID()); } - public void processPacket(PacketBuffer packetData, EntityPlayerMP sender) { - try { - IntegratedVoiceSignalPackets.processPacket(packetData, sender, this); - } catch (IOException e) { - logger.error("Invalid voice signal packet recieved from player {}!", sender.getName()); - logger.error(e); - sender.playerNetServerHandler.kickPlayerFromServer("Invalid voice signal packet recieved!"); - } - } - - void handleVoiceSignalPacketTypeRequest(EaglercraftUUID player, EntityPlayerMP sender) { + public void handleVoiceSignalPacketTypeRequest(EaglercraftUUID player, EntityPlayerMP sender) { EaglercraftUUID senderUUID = sender.getUniqueID(); if (senderUUID.equals(player)) return; // prevent duplicates @@ -134,14 +121,24 @@ public class IntegratedVoiceService { voiceRequests.remove(senderUUID); // send each other add data voicePairs.add(newPair); - targetPlayerCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - IntegratedVoiceSignalPackets.makeVoiceSignalPacketConnect(senderUUID, false))); - sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - IntegratedVoiceSignalPackets.makeVoiceSignalPacketConnect(player, true))); + if(targetPlayerCon.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) { + targetPlayerCon.playerNetServerHandler + .sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(senderUUID.msb, senderUUID.lsb, false, false)); + }else { + targetPlayerCon.playerNetServerHandler + .sendEaglerMessage(new SPacketVoiceSignalConnectV4EAG(senderUUID.msb, senderUUID.lsb, false)); + } + if(sender.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) { + sender.playerNetServerHandler + .sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(player.msb, player.lsb, false, true)); + }else { + sender.playerNetServerHandler + .sendEaglerMessage(new SPacketVoiceSignalConnectV4EAG(player.msb, player.lsb, true)); + } } } - void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) { + public void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) { if (voicePlayers.containsKey(sender.getUniqueID())) { return; } @@ -150,63 +147,60 @@ public class IntegratedVoiceService { if (hasNoOtherPlayers) { return; } - byte[] packetToBroadcast = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values()); + Collection userDatas = new ArrayList<>(voicePlayers.size()); + for(EntityPlayerMP player : voicePlayers.values()) { + EaglercraftUUID uuid = player.getUniqueID(); + userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.msb, uuid.lsb, player.getName())); + } + SPacketVoiceSignalGlobalEAG packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas); for (EntityPlayerMP userCon : voicePlayers.values()) { - userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled - .buffer(packetToBroadcast, packetToBroadcast.length).writerIndex(packetToBroadcast.length)))); + userCon.playerNetServerHandler.sendEaglerMessage(packetToBroadcast); } } - void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, String str, EntityPlayerMP sender) { - VoicePair pair = new VoicePair(player, sender.getUniqueID()); + public void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, byte[] str, EntityPlayerMP sender) { + EaglercraftUUID uuid = sender.getUniqueID(); + VoicePair pair = new VoicePair(player, uuid); EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null; if (pass != null) { - pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - IntegratedVoiceSignalPackets.makeVoiceSignalPacketICE(sender.getUniqueID(), str))); + pass.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalICEEAG(uuid.msb, uuid.lsb, str)); } } - void handleVoiceSignalPacketTypeDesc(EaglercraftUUID player, String str, EntityPlayerMP sender) { - VoicePair pair = new VoicePair(player, sender.getUniqueID()); + public void handleVoiceSignalPacketTypeDesc(EaglercraftUUID player, byte[] str, EntityPlayerMP sender) { + EaglercraftUUID uuid = sender.getUniqueID(); + VoicePair pair = new VoicePair(player, uuid); EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null; if (pass != null) { - pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - IntegratedVoiceSignalPackets.makeVoiceSignalPacketDesc(sender.getUniqueID(), str))); + pass.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDescEAG(uuid.msb, uuid.lsb, str)); } } - void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID player, EntityPlayerMP sender) { - if (player != null) { - if (!voicePlayers.containsKey(player)) { - return; + public void handleVoiceSignalPacketTypeDisconnect(EntityPlayerMP sender) { + removeUser(sender.getUniqueID()); + } + + public void handleVoiceSignalPacketTypeDisconnectPeer(EaglercraftUUID player, EntityPlayerMP sender) { + if (!voicePlayers.containsKey(player)) { + return; + } + Iterator pairsItr = voicePairs.iterator(); + while (pairsItr.hasNext()) { + VoicePair voicePair = pairsItr.next(); + EaglercraftUUID target = null; + if (voicePair.uuid1.equals(player)) { + target = voicePair.uuid2; + } else if (voicePair.uuid2.equals(player)) { + target = voicePair.uuid1; } - byte[] userDisconnectPacket = null; - Iterator pairsItr = voicePairs.iterator(); - while (pairsItr.hasNext()) { - VoicePair voicePair = pairsItr.next(); - EaglercraftUUID target = null; - if (voicePair.uuid1.equals(player)) { - target = voicePair.uuid2; - } else if (voicePair.uuid2.equals(player)) { - target = voicePair.uuid1; - } - if (target != null) { - pairsItr.remove(); - EntityPlayerMP conn = voicePlayers.get(target); - if (conn != null) { - if (userDisconnectPacket == null) { - userDisconnectPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnect(player); - } - conn.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - new PacketBuffer(Unpooled.buffer(userDisconnectPacket, userDisconnectPacket.length) - .writerIndex(userDisconnectPacket.length)))); - } - sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnectPB(target))); + if (target != null) { + pairsItr.remove(); + EntityPlayerMP conn = voicePlayers.get(target); + if (conn != null) { + conn.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(player.msb, player.lsb)); } + sender.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(target.msb, target.lsb)); } - } else { - removeUser(sender.getUniqueID()); } } @@ -216,16 +210,16 @@ public class IntegratedVoiceService { } voiceRequests.remove(user); if (voicePlayers.size() > 0) { - byte[] voicePlayersPkt = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values()); + Collection userDatas = new ArrayList<>(voicePlayers.size()); + for(EntityPlayerMP player : voicePlayers.values()) { + EaglercraftUUID uuid = player.getUniqueID(); + userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.msb, uuid.lsb, player.getName())); + } + SPacketVoiceSignalGlobalEAG packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas); for (EntityPlayerMP userCon : voicePlayers.values()) { - if (!user.equals(userCon.getUniqueID())) { - userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - new PacketBuffer(Unpooled.buffer(voicePlayersPkt, voicePlayersPkt.length) - .writerIndex(voicePlayersPkt.length)))); - } + userCon.playerNetServerHandler.sendEaglerMessage(packetToBroadcast); } } - byte[] userDisconnectPacket = null; Iterator pairsItr = voicePairs.iterator(); while (pairsItr.hasNext()) { VoicePair voicePair = pairsItr.next(); @@ -240,16 +234,11 @@ public class IntegratedVoiceService { if (voicePlayers.size() > 0) { EntityPlayerMP conn = voicePlayers.get(target); if (conn != null) { - if (userDisconnectPacket == null) { - userDisconnectPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnect(user); - } - conn.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, - new PacketBuffer(Unpooled.buffer(userDisconnectPacket, userDisconnectPacket.length) - .writerIndex(userDisconnectPacket.length)))); + conn.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(user.msb, user.lsb)); } } } } } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceSignalPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceSignalPackets.java deleted file mode 100644 index 978b0c7..0000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceSignalPackets.java +++ /dev/null @@ -1,198 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.sp.server.voice; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collection; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IntegratedVoiceSignalPackets { - - static final int VOICE_SIGNAL_ALLOWED = 0; - static final int VOICE_SIGNAL_REQUEST = 0; - static final int VOICE_SIGNAL_CONNECT = 1; - static final int VOICE_SIGNAL_DISCONNECT = 2; - static final int VOICE_SIGNAL_ICE = 3; - static final int VOICE_SIGNAL_DESC = 4; - static final int VOICE_SIGNAL_GLOBAL = 5; - - public static void processPacket(PacketBuffer buffer, EntityPlayerMP sender, IntegratedVoiceService voiceService) throws IOException { - int packetId = -1; - if(buffer.readableBytes() == 0) { - throw new IOException("Zero-length packet recieved"); - } - try { - packetId = buffer.readUnsignedByte(); - switch(packetId) { - case VOICE_SIGNAL_REQUEST: { - voiceService.handleVoiceSignalPacketTypeRequest(buffer.readUuid(), sender); - break; - } - case VOICE_SIGNAL_CONNECT: { - voiceService.handleVoiceSignalPacketTypeConnect(sender); - break; - } - case VOICE_SIGNAL_ICE: { - voiceService.handleVoiceSignalPacketTypeICE(buffer.readUuid(), buffer.readStringFromBuffer(32767), sender); - break; - } - case VOICE_SIGNAL_DESC: { - voiceService.handleVoiceSignalPacketTypeDesc(buffer.readUuid(), buffer.readStringFromBuffer(32767), sender); - break; - } - case VOICE_SIGNAL_DISCONNECT: { - voiceService.handleVoiceSignalPacketTypeDisconnect(buffer.readableBytes() > 0 ? buffer.readUuid() : null, sender); - break; - } - default: { - throw new IOException("Unknown packet type " + packetId); - } - } - if(buffer.readableBytes() > 0) { - throw new IOException("Voice packet is too long!"); - } - }catch(IOException ex) { - throw ex; - }catch(Throwable t) { - throw new IOException("Unhandled exception handling voice packet type " + packetId, t); - } - } - - static byte[] makeVoiceSignalPacketAllowed(boolean allowed, String[] iceServers) { - if (iceServers == null) { - byte[] ret = new byte[2]; - ByteBuf wrappedBuffer = Unpooled.buffer(ret, ret.length); - wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED); - wrappedBuffer.writeBoolean(allowed); - return ret; - } - byte[][] iceServersBytes = new byte[iceServers.length][]; - int totalLen = 2 + PacketBuffer.getVarIntSize(iceServers.length); - for(int i = 0; i < iceServers.length; ++i) { - byte[] b = iceServersBytes[i] = iceServers[i].getBytes(StandardCharsets.UTF_8); - totalLen += PacketBuffer.getVarIntSize(b.length) + b.length; - } - byte[] ret = new byte[totalLen]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED); - wrappedBuffer.writeBoolean(allowed); - wrappedBuffer.writeVarIntToBuffer(iceServersBytes.length); - for(int i = 0; i < iceServersBytes.length; ++i) { - byte[] b = iceServersBytes[i]; - wrappedBuffer.writeVarIntToBuffer(b.length); - wrappedBuffer.writeBytes(b); - } - return ret; - } - - static byte[] makeVoiceSignalPacketGlobal(Collection users) { - int cnt = users.size(); - byte[][] displayNames = new byte[cnt][]; - int i = 0; - for(EntityPlayerMP user : users) { - String name = user.getName(); - if(name.length() > 16) name = name.substring(0, 16); - displayNames[i++] = name.getBytes(StandardCharsets.UTF_8); - } - int totalLength = 1 + PacketBuffer.getVarIntSize(cnt) + (cnt << 4); - for(i = 0; i < cnt; ++i) { - totalLength += PacketBuffer.getVarIntSize(displayNames[i].length) + displayNames[i].length; - } - byte[] ret = new byte[totalLength]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_GLOBAL); - wrappedBuffer.writeVarIntToBuffer(cnt); - for(EntityPlayerMP user : users) { - wrappedBuffer.writeUuid(user.getUniqueID()); - } - for(i = 0; i < cnt; ++i) { - wrappedBuffer.writeVarIntToBuffer(displayNames[i].length); - wrappedBuffer.writeBytes(displayNames[i]); - } - return ret; - } - - static PacketBuffer makeVoiceSignalPacketConnect(EaglercraftUUID player, boolean offer) { - byte[] ret = new byte[18]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT); - wrappedBuffer.writeUuid(player); - wrappedBuffer.writeBoolean(offer); - return wrappedBuffer; - } - - static byte[] makeVoiceSignalPacketConnectAnnounce(EaglercraftUUID player) { - byte[] ret = new byte[17]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT); - wrappedBuffer.writeUuid(player); - return ret; - } - - static byte[] makeVoiceSignalPacketDisconnect(EaglercraftUUID player) { - if(player == null) { - return new byte[] { (byte)VOICE_SIGNAL_DISCONNECT }; - } - byte[] ret = new byte[17]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT); - wrappedBuffer.writeUuid(player); - return ret; - } - - static PacketBuffer makeVoiceSignalPacketDisconnectPB(EaglercraftUUID player) { - if(player == null) { - byte[] ret = new byte[1]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT); - return wrappedBuffer; - } - byte[] ret = new byte[17]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT); - wrappedBuffer.writeUuid(player); - return wrappedBuffer; - } - - static PacketBuffer makeVoiceSignalPacketICE(EaglercraftUUID player, String str) { - byte[] strBytes = str.getBytes(StandardCharsets.UTF_8); - byte[] ret = new byte[17 + PacketBuffer.getVarIntSize(strBytes.length) + strBytes.length]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_ICE); - wrappedBuffer.writeUuid(player); - wrappedBuffer.writeVarIntToBuffer(strBytes.length); - wrappedBuffer.writeBytes(strBytes); - return wrappedBuffer; - } - - static PacketBuffer makeVoiceSignalPacketDesc(EaglercraftUUID player, String str) { - byte[] strBytes = str.getBytes(StandardCharsets.UTF_8); - byte[] ret = new byte[17 + PacketBuffer.getVarIntSize(strBytes.length) + strBytes.length]; - PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); - wrappedBuffer.writeByte(VOICE_SIGNAL_DESC); - wrappedBuffer.writeUuid(player); - wrappedBuffer.writeVarIntToBuffer(strBytes.length); - wrappedBuffer.writeBytes(strBytes); - return wrappedBuffer; - } - -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java index 8bcc2f4..b99bae1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java @@ -1,77 +1,97 @@ -package net.lax1dude.eaglercraft.v1_8.sp.socket; - -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiDisconnected; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.network.EnumConnectionState; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.login.INetHandlerLoginClient; -import net.minecraft.network.login.server.S00PacketDisconnect; -import net.minecraft.network.login.server.S01PacketEncryptionRequest; -import net.minecraft.network.login.server.S02PacketLoginSuccess; -import net.minecraft.network.login.server.S03PacketEnableCompression; -import net.minecraft.network.play.client.C17PacketCustomPayload; -import net.minecraft.util.IChatComponent; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class NetHandlerSingleplayerLogin implements INetHandlerLoginClient { - - private final Minecraft mc; - private final GuiScreen previousGuiScreen; - private final EaglercraftNetworkManager networkManager; - - public NetHandlerSingleplayerLogin(EaglercraftNetworkManager parNetworkManager, Minecraft mcIn, GuiScreen parGuiScreen) { - this.networkManager = parNetworkManager; - this.mc = mcIn; - this.previousGuiScreen = parGuiScreen; - } - - @Override - public void onDisconnect(IChatComponent var1) { - this.mc.displayGuiScreen(new GuiDisconnected(this.previousGuiScreen, "connect.failed", var1)); - } - - @Override - public void handleEncryptionRequest(S01PacketEncryptionRequest var1) { - - } - - @Override - public void handleLoginSuccess(S02PacketLoginSuccess var1) { - this.networkManager.setConnectionState(EnumConnectionState.PLAY); - this.networkManager.setNetHandler(new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager, var1.getProfile())); - byte[] b = UpdateService.getClientSignatureData(); - if(b != null) { - this.networkManager.sendPacket(new C17PacketCustomPayload("EAG|MyUpdCert-1.8", new PacketBuffer(Unpooled.buffer(b, b.length).writerIndex(b.length)))); - } - } - - @Override - public void handleDisconnect(S00PacketDisconnect var1) { - networkManager.closeChannel(var1.func_149603_c()); - } - - @Override - public void handleEnableCompression(S03PacketEnableCompression var1) { - - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.socket; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiDisconnected; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.network.EnumConnectionState; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.login.INetHandlerLoginClient; +import net.minecraft.network.login.server.S00PacketDisconnect; +import net.minecraft.network.login.server.S01PacketEncryptionRequest; +import net.minecraft.network.login.server.S02PacketLoginSuccess; +import net.minecraft.network.login.server.S03PacketEnableCompression; +import net.minecraft.network.play.client.C17PacketCustomPayload; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class NetHandlerSingleplayerLogin implements INetHandlerLoginClient { + + private final Minecraft mc; + private final GuiScreen previousGuiScreen; + private final EaglercraftNetworkManager networkManager; + + private static final Logger logger = LogManager.getLogger("NetHandlerSingleplayerLogin"); + + public NetHandlerSingleplayerLogin(EaglercraftNetworkManager parNetworkManager, Minecraft mcIn, GuiScreen parGuiScreen) { + this.networkManager = parNetworkManager; + this.mc = mcIn; + this.previousGuiScreen = parGuiScreen; + } + + @Override + public void onDisconnect(IChatComponent var1) { + this.mc.displayGuiScreen(new GuiDisconnected(this.previousGuiScreen, "connect.failed", var1)); + } + + @Override + public void handleEncryptionRequest(S01PacketEncryptionRequest var1) { + + } + + @Override + public void handleLoginSuccess(S02PacketLoginSuccess var1) { + this.networkManager.setConnectionState(EnumConnectionState.PLAY); + int p = var1.getSelectedProtocol(); + GamePluginMessageProtocol mp = GamePluginMessageProtocol.getByVersion(p); + if(mp == null) { + this.networkManager.closeChannel(new ChatComponentText("Unknown protocol selected: " + p)); + return; + } + logger.info("Server is using protocol: {}", p); + NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager, var1.getProfile()); + netHandler.setEaglerMessageController( + new GameProtocolMessageController(mp, GamePluginMessageConstants.CLIENT_TO_SERVER, + GameProtocolMessageController.createClientHandler(p, netHandler), + (ch, msg) -> netHandler.addToSendQueue(new C17PacketCustomPayload(ch, msg)))); + this.networkManager.setNetHandler(netHandler); + byte[] b = UpdateService.getClientSignatureData(); + if(b != null) { + this.networkManager.sendPacket(new C17PacketCustomPayload("EAG|MyUpdCert-1.8", new PacketBuffer(Unpooled.buffer(b, b.length).writerIndex(b.length)))); + } + } + + @Override + public void handleDisconnect(S00PacketDisconnect var1) { + networkManager.closeChannel(var1.func_149603_c()); + } + + @Override + public void handleEnableCompression(S03PacketEnableCompression var1) { + + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchControl.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchControl.java new file mode 100755 index 0000000..5f4b751 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchControl.java @@ -0,0 +1,579 @@ +package net.lax1dude.eaglercraft.v1_8.touch_gui; + +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.settings.GameSettings; + +/** + * Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumTouchControl { + + DPAD_UP(EnumTouchControlPos.BOTTOM_LEFT, 60, 109, 44, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 56, 0, 22, 22, 2); + }), + + + DPAD_LEFT(EnumTouchControlPos.BOTTOM_LEFT, 11, 60, 44, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 56, 22, 22, 22, 2); + }), + + + DPAD_RIGHT(EnumTouchControlPos.BOTTOM_LEFT, 109, 60, 44, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 56, 66, 22, 22, 2); + }), + + + DPAD_DOWN(EnumTouchControlPos.BOTTOM_LEFT, 60, 11, 44, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 56, 44, 22, 22, 2); + }), + + + DPAD_UP_LEFT(EnumTouchControlPos.BOTTOM_LEFT, 16, 112, 36, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 0, 18, 18, 2); + }), + + + DPAD_UP_RIGHT(EnumTouchControlPos.BOTTOM_LEFT, 112, 112, 36, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 18, 18, 18, 2); + }), + + + JUMP(EnumTouchControlPos.BOTTOM_RIGHT, 64, 64, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + if(TouchControls.isSneakToggled) { + TouchControls.resetSneakInvalidate(); + } + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 90, 18, 18, 2); + }), + + + SNEAK(EnumTouchControlPos.BOTTOM_LEFT, 64, 64, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + enumIn.invalid = true; + TouchControls.isSneakToggled = !TouchControls.isSneakToggled; + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, TouchControls.isSneakToggled ? 126 : 108, 18, 18, 2); + }), + + + BACK(EnumTouchControlPos.TOP, -18, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + if(Touch.isDeviceKeyboardOpenMAYBE()) { + Touch.closeDeviceKeyboard(); + }else { + Minecraft mc = Minecraft.getMinecraft(); + if(mc.thePlayer != null) { + mc.setIngameFocus(); + }else if(mc.currentScreen != null && !(mc.currentScreen instanceof GuiMainMenu)) { + mc.displayGuiScreen(null); + } + } + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 0, 36, 18, 18, 2); + }), + + + BACK_DISABLED(EnumTouchControlPos.TOP, -18, 0, 36, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 0, 54, 18, 18, 2); + }), + + + KEYBOARD(EnumTouchControlPos.TOP, 18, 0, 36, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 0, 72, 18, 18, 2); + }), + + + PAUSE(EnumTouchControlPos.TOP, -18, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + Minecraft mc = Minecraft.getMinecraft(); + mc.displayInGameMenu(); + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 0, 0, 18, 18, 2); + }), + + + CHAT(EnumTouchControlPos.TOP, 18, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + Minecraft mc = Minecraft.getMinecraft(); + mc.displayGuiScreen(new GuiChat()); + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 0, 18, 18, 18, 2); + }), + + + F3(EnumTouchControlPos.TOP, 144, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + Minecraft mc = Minecraft.getMinecraft(); + GameSettings gameSettings = mc.gameSettings; + gameSettings.showDebugInfo = !gameSettings.showDebugInfo; + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 218, 220, 18, 18, 2); + }), + + + F5(EnumTouchControlPos.TOP, 90, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + Minecraft mc = Minecraft.getMinecraft(); + mc.togglePerspective(); + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 218, 184, 18, 18, 2); + }), + + + PASTE(EnumTouchControlPos.TOP, 144, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + GuiScreen screen = Minecraft.getMinecraft().currentScreen; + if(screen != null) { + screen.fireInputEvent(EnumInputEvent.CLIPBOARD_PASTE, null); + } + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 218, 148, 18, 18, 2); + }), + + + COPY(EnumTouchControlPos.TOP, 90, 0, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + GuiScreen screen = Minecraft.getMinecraft().currentScreen; + if(screen != null) { + screen.fireInputEvent(EnumInputEvent.CLIPBOARD_COPY, null); + } + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 218, 166, 18, 18, 2); + }), + + + PICK(EnumTouchControlPos.BOTTOM_RIGHT, 62, 125, 40, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + Minecraft.getMinecraft().middleClickMouse(); + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 36, 20, 20, 20, 2); + }), + + + FLY(EnumTouchControlPos.BOTTOM_LEFT, 16, 16, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + TouchControls.resetSneak(); + EntityPlayerSP player = Minecraft.getMinecraft().thePlayer; + player.jump(); + player.capabilities.isFlying = true; + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 72, 18, 18, 2); + }), + + + FLY_UP(EnumTouchControlPos.BOTTOM_RIGHT, 12, 120, 36, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 36, 18, 18, 2); + }), + + + FLY_DOWN(EnumTouchControlPos.BOTTOM_RIGHT, 12, 75, 36, null, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 54, 18, 18, 2); + }), + + + FLY_END(EnumTouchControlPos.BOTTOM_RIGHT, 64, 64, 36, (enumIn, x, y) -> { + if(!TouchControls.isPressed(enumIn)) { + Minecraft.getMinecraft().thePlayer.capabilities.isFlying = false; + } + }, (enumIn, x, y, pressed, mc, res) -> { + mc.getTextureManager().bindTexture(TouchOverlayRenderer.spriteSheet); + int[] pos = enumIn.getLocation(res, TouchOverlayRenderer._fuck); + TouchOverlayRenderer.drawTexturedModalRect(pos[0], pos[1], 18, 72, 18, 18, 2); + }); + + public static interface TouchAction { + void call(EnumTouchControl enumIn, int x, int y); + } + + public static interface TouchRender { + void call(EnumTouchControl enumIn, int x, int y, boolean pressed, Minecraft mc, ScaledResolution res); + } + + protected final EnumTouchControlPos pos; + protected final int offX; + protected final int offY; + protected final int size; + protected final TouchAction action; + protected final TouchRender render; + + protected boolean visible = true; + protected boolean invalid = true; + + public static final EnumTouchControl[] _VALUES = values(); + + EnumTouchControl(EnumTouchControlPos pos, int offX, int offY, int size, TouchAction action, TouchRender render) { + this.pos = pos; + this.offX = offX; + this.offY = offY; + this.size = size; + this.action = action; + this.render = render; + } + + public int[] getLocation(ScaledResolution scaledResolution, int[] loc) { + if(loc == null) { + loc = new int[2]; + } + int sz = size; + switch (pos) { + case TOP_LEFT: + loc[0] = offX; + loc[1] = offY; + break; + case TOP: + loc[0] = offX + (scaledResolution.getScaledWidth() - sz) / 2; + loc[1] = offY; + break; + case TOP_RIGHT: + loc[0] = -offX + (scaledResolution.getScaledWidth() - sz); + loc[1] = offY; + break; + case LEFT: + loc[0] = offX; + loc[1] = offY + (scaledResolution.getScaledHeight() - sz) / 2; + break; + case RIGHT: + loc[0] = -offX + (scaledResolution.getScaledWidth() - sz); + loc[1] = offY + (scaledResolution.getScaledHeight() - sz) / 2; + break; + case BOTTOM_LEFT: + loc[0] = offX; + loc[1] = -offY + (scaledResolution.getScaledHeight() - sz); + break; + case BOTTOM: + loc[0] = offX + (scaledResolution.getScaledWidth() - sz) / 2; + loc[1] = -offY + (scaledResolution.getScaledHeight() - sz); + break; + case BOTTOM_RIGHT: + loc[0] = -offX + (scaledResolution.getScaledWidth() - sz); + loc[1] = -offY + (scaledResolution.getScaledHeight() - sz); + break; + } + return loc; + } + + public void setVisible(TouchOverlayRenderer renderer, boolean vis) { + if(visible != vis) { + visible = vis; + invalid = true; + if(vis) { + renderer.invalidate(); + }else { + renderer.invalidateDeep(); + } + } + } + + public int getSize() { + return size; + } + + public TouchAction getAction() { + return action; + } + + public TouchRender getRender() { + return render; + } + + protected static EnumTouchLayoutState currentLayout = null; + + public static void setLayoutState(TouchOverlayRenderer renderer, EnumTouchLayoutState layout) { + if(layout == currentLayout) return; + switch(layout) { + case IN_GUI: + DPAD_UP.setVisible(renderer, false); + DPAD_LEFT.setVisible(renderer, false); + DPAD_RIGHT.setVisible(renderer, false); + DPAD_DOWN.setVisible(renderer, false); + DPAD_UP_LEFT.setVisible(renderer, false); + DPAD_UP_RIGHT.setVisible(renderer, false); + JUMP.setVisible(renderer, false); + SNEAK.setVisible(renderer, false); + BACK.setVisible(renderer, true); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, true); + PAUSE.setVisible(renderer, false); + CHAT.setVisible(renderer, false); + F3.setVisible(renderer, false); + F5.setVisible(renderer, false); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, false); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GUI_TYPING: + DPAD_UP.setVisible(renderer, false); + DPAD_LEFT.setVisible(renderer, false); + DPAD_RIGHT.setVisible(renderer, false); + DPAD_DOWN.setVisible(renderer, false); + DPAD_UP_LEFT.setVisible(renderer, false); + DPAD_UP_RIGHT.setVisible(renderer, false); + JUMP.setVisible(renderer, false); + SNEAK.setVisible(renderer, false); + BACK.setVisible(renderer, true); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, true); + PAUSE.setVisible(renderer, false); + CHAT.setVisible(renderer, false); + F3.setVisible(renderer, false); + F5.setVisible(renderer, false); + PASTE.setVisible(renderer, true); + COPY.setVisible(renderer, true); + PICK.setVisible(renderer, false); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GUI_NO_BACK: + DPAD_UP.setVisible(renderer, false); + DPAD_LEFT.setVisible(renderer, false); + DPAD_RIGHT.setVisible(renderer, false); + DPAD_DOWN.setVisible(renderer, false); + DPAD_UP_LEFT.setVisible(renderer, false); + DPAD_UP_RIGHT.setVisible(renderer, false); + JUMP.setVisible(renderer, false); + SNEAK.setVisible(renderer, false); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, true); + KEYBOARD.setVisible(renderer, true); + PAUSE.setVisible(renderer, false); + CHAT.setVisible(renderer, false); + F3.setVisible(renderer, false); + F5.setVisible(renderer, false); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, false); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GAME: + DPAD_UP.setVisible(renderer, true); + DPAD_LEFT.setVisible(renderer, true); + DPAD_RIGHT.setVisible(renderer, true); + DPAD_DOWN.setVisible(renderer, true); + DPAD_UP_LEFT.setVisible(renderer, false); + DPAD_UP_RIGHT.setVisible(renderer, false); + JUMP.setVisible(renderer, true); + SNEAK.setVisible(renderer, true); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, false); + PAUSE.setVisible(renderer, true); + CHAT.setVisible(renderer, true); + F3.setVisible(renderer, true); + F5.setVisible(renderer, true); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, true); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GAME_WALK: + DPAD_UP.setVisible(renderer, true); + DPAD_LEFT.setVisible(renderer, true); + DPAD_RIGHT.setVisible(renderer, true); + DPAD_DOWN.setVisible(renderer, true); + DPAD_UP_LEFT.setVisible(renderer, true); + DPAD_UP_RIGHT.setVisible(renderer, true); + JUMP.setVisible(renderer, true); + SNEAK.setVisible(renderer, true); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, false); + PAUSE.setVisible(renderer, true); + CHAT.setVisible(renderer, true); + F3.setVisible(renderer, true); + F5.setVisible(renderer, true); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, true); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GAME_CAN_FLY: + DPAD_UP.setVisible(renderer, true); + DPAD_LEFT.setVisible(renderer, true); + DPAD_RIGHT.setVisible(renderer, true); + DPAD_DOWN.setVisible(renderer, true); + DPAD_UP_LEFT.setVisible(renderer, false); + DPAD_UP_RIGHT.setVisible(renderer, false); + JUMP.setVisible(renderer, true); + SNEAK.setVisible(renderer, true); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, false); + PAUSE.setVisible(renderer, true); + CHAT.setVisible(renderer, true); + F3.setVisible(renderer, true); + F5.setVisible(renderer, true); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, true); + FLY.setVisible(renderer, true); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GAME_WALK_CAN_FLY: + DPAD_UP.setVisible(renderer, true); + DPAD_LEFT.setVisible(renderer, true); + DPAD_RIGHT.setVisible(renderer, true); + DPAD_DOWN.setVisible(renderer, true); + DPAD_UP_LEFT.setVisible(renderer, true); + DPAD_UP_RIGHT.setVisible(renderer, true); + JUMP.setVisible(renderer, true); + SNEAK.setVisible(renderer, true); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, false); + PAUSE.setVisible(renderer, true); + CHAT.setVisible(renderer, true); + F3.setVisible(renderer, true); + F5.setVisible(renderer, true); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, true); + FLY.setVisible(renderer, true); + FLY_UP.setVisible(renderer, false); + FLY_DOWN.setVisible(renderer, false); + FLY_END.setVisible(renderer, false); + break; + case IN_GAME_FLYING: + DPAD_UP.setVisible(renderer, true); + DPAD_LEFT.setVisible(renderer, true); + DPAD_RIGHT.setVisible(renderer, true); + DPAD_DOWN.setVisible(renderer, true); + DPAD_UP_LEFT.setVisible(renderer, false); + DPAD_UP_RIGHT.setVisible(renderer, false); + JUMP.setVisible(renderer, false); + SNEAK.setVisible(renderer, true); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, false); + PAUSE.setVisible(renderer, true); + CHAT.setVisible(renderer, true); + F3.setVisible(renderer, true); + F5.setVisible(renderer, true); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, true); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, true); + FLY_DOWN.setVisible(renderer, true); + FLY_END.setVisible(renderer, true); + break; + case IN_GAME_WALK_FLYING: + DPAD_UP.setVisible(renderer, true); + DPAD_LEFT.setVisible(renderer, true); + DPAD_RIGHT.setVisible(renderer, true); + DPAD_DOWN.setVisible(renderer, true); + DPAD_UP_LEFT.setVisible(renderer, true); + DPAD_UP_RIGHT.setVisible(renderer, true); + JUMP.setVisible(renderer, false); + SNEAK.setVisible(renderer, true); + BACK.setVisible(renderer, false); + BACK_DISABLED.setVisible(renderer, false); + KEYBOARD.setVisible(renderer, false); + PAUSE.setVisible(renderer, true); + CHAT.setVisible(renderer, true); + F3.setVisible(renderer, true); + F5.setVisible(renderer, true); + PASTE.setVisible(renderer, false); + COPY.setVisible(renderer, false); + PICK.setVisible(renderer, true); + FLY.setVisible(renderer, false); + FLY_UP.setVisible(renderer, true); + FLY_DOWN.setVisible(renderer, true); + FLY_END.setVisible(renderer, true); + break; + default: + throw new IllegalStateException(); + } + currentLayout = layout; + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchControlPos.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchControlPos.java new file mode 100755 index 0000000..0b0771f --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchControlPos.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.touch_gui; + +/** + * Copyright (c) 2024 ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumTouchControlPos { + TOP_LEFT, TOP, TOP_RIGHT, LEFT, RIGHT, BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchLayoutState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchLayoutState.java new file mode 100755 index 0000000..86c7c71 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/EnumTouchLayoutState.java @@ -0,0 +1,28 @@ +package net.lax1dude.eaglercraft.v1_8.touch_gui; + +/** + * Copyright (c) 2024 lax1due. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumTouchLayoutState { + IN_GUI, + IN_GUI_TYPING, + IN_GUI_NO_BACK, + IN_GAME, + IN_GAME_WALK, + IN_GAME_CAN_FLY, + IN_GAME_WALK_CAN_FLY, + IN_GAME_FLYING, + IN_GAME_WALK_FLYING; +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControlInput.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControlInput.java new file mode 100755 index 0000000..29cfd29 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControlInput.java @@ -0,0 +1,27 @@ +package net.lax1dude.eaglercraft.v1_8.touch_gui; + +/** + * Copyright (c) 2024 ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TouchControlInput { + public int x; + public int y; + public final EnumTouchControl control; + public TouchControlInput(int x, int y, EnumTouchControl control) { + this.x = x; + this.y = y; + this.control = control; + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java new file mode 100755 index 0000000..94bbaa5 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java @@ -0,0 +1,164 @@ +package net.lax1dude.eaglercraft.v1_8.touch_gui; + +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.touch_gui.EnumTouchControl.TouchAction; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; + +import java.util.*; + +/** + * Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TouchControls { + + public static final Map touchControls = new HashMap<>(); + protected static Set touchControlPressed = EnumSet.noneOf(EnumTouchControl.class); + + protected static boolean isSneakToggled = false; + + public static void update(boolean screenTouched) { + Minecraft mc = Minecraft.getMinecraft(); + int h = mc.displayHeight; + final ScaledResolution sr = mc.scaledResolution; + int fac = sr.getScaleFactor(); + if(screenTouched) { + int touchPoints = Touch.touchPointCount(); + int[] loc; + for(int i = 0; i < touchPoints; ++i) { + int x = Touch.touchPointX(i); + int y = h - Touch.touchPointY(i) - 1; + int uid = Touch.touchPointUID(i); + TouchControlInput input = touchControls.get(uid); + if(input != null) { + EnumTouchControl ctrl = input.control; + loc = ctrl.getLocation(sr, TouchOverlayRenderer._fuck); + loc[0] *= fac; + loc[1] *= fac; + int size = ctrl.getSize() * fac; + if (x >= loc[0] && y >= loc[1] && x < loc[0] + size && y < loc[1] + size) { + continue; + } + EnumTouchControl[] en = EnumTouchControl._VALUES; + for (int j = 0; j < en.length; ++j) { + EnumTouchControl control = en[j]; + if(!control.visible) continue; + loc = control.getLocation(sr, TouchOverlayRenderer._fuck); + loc[0] *= fac; + loc[1] *= fac; + size = control.getSize() * fac; + if (x >= loc[0] && y >= loc[1] && x < loc[0] + size && y < loc[1] + size) { + touchControls.put(uid, new TouchControlInput(x / fac, y / fac, control)); + break; + } + } + } + } + mc.ingameGUI.updateTouchEagler(mc.currentScreen == null); + }else { + touchControls.clear(); + touchControlPressed.clear(); + mc.ingameGUI.updateTouchEagler(false); + } + } + + public static boolean handleTouchBegin(int uid, int pointX, int pointY) { + Minecraft mc = Minecraft.getMinecraft(); + pointY = mc.displayHeight - pointY - 1; + EnumTouchControl control = overlappingControl0(pointX, pointY, mc.scaledResolution); + if(control != null) { + int fac = mc.scaledResolution.getScaleFactor(); + touchControls.put(uid, new TouchControlInput(pointX / fac, pointY / fac, control)); + return true; + }else { + return mc.currentScreen == null && Minecraft.getMinecraft().ingameGUI.handleTouchBeginEagler(uid, pointX, pointY); + } + } + + public static boolean handleTouchEnd(int uid, int pointX, int pointY) { + if(touchControls.remove(uid) != null) { + return true; + }else { + Minecraft mc = Minecraft.getMinecraft(); + return mc.currentScreen == null && mc.ingameGUI.handleTouchEndEagler(uid, pointX, mc.displayHeight - pointY - 1); + } + } + + public static void resetSneak() { + isSneakToggled = false; + } + + public static void resetSneakInvalidate() { + if(isSneakToggled) { + isSneakToggled = false; + EnumTouchControl.SNEAK.invalid = true; + Minecraft.getMinecraft().touchOverlayRenderer.invalidate(); + } + } + + public static void handleInput() { + if(!touchControls.isEmpty()) { + Set newPressed = EnumSet.noneOf(EnumTouchControl.class); + TouchOverlayRenderer renderer = Minecraft.getMinecraft().touchOverlayRenderer; + for (TouchControlInput input : touchControls.values()) { + TouchAction action = input.control.getAction(); + if(action != null) { + action.call(input.control, input.x, input.y); + } + if(input.control.invalid) { + renderer.invalidate(); + } + newPressed.add(input.control); + } + touchControlPressed = newPressed; + }else { + touchControlPressed.clear(); + } + } + + public static boolean isPressed(EnumTouchControl control) { + return touchControlPressed.contains(control); + } + + public static boolean getSneakToggled() { + return isSneakToggled; + } + + public static EnumTouchControl overlappingControl(int tx, int ty) { + Minecraft mc = Minecraft.getMinecraft(); + ty = mc.displayHeight - ty - 1; + return overlappingControl0(tx, ty, mc.scaledResolution); + } + + private static EnumTouchControl overlappingControl0(int pointX, int pointY, ScaledResolution sr) { + EnumTouchControl[] en = EnumTouchControl._VALUES; + int[] loc; + int fac = sr.getScaleFactor(); + int size; + for (int j = 0; j < en.length; ++j) { + EnumTouchControl control = en[j]; + if(!control.visible) continue; + loc = control.getLocation(sr, TouchOverlayRenderer._fuck); + loc[0] *= fac; + loc[1] *= fac; + size = control.getSize() * fac; + if (pointX >= loc[0] && pointY >= loc[1] && pointX < loc[0] + size && pointY < loc[1] + size) { + return control; + } + } + return null; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java new file mode 100755 index 0000000..663196c --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java @@ -0,0 +1,197 @@ +package net.lax1dude.eaglercraft.v1_8.touch_gui; + +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.Touch; +import net.lax1dude.eaglercraft.v1_8.opengl.GameOverlayFramebuffer; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.util.Set; + +import com.google.common.collect.Sets; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TouchOverlayRenderer { + + public static final ResourceLocation spriteSheet = new ResourceLocation("eagler:gui/touch_gui.png"); + + static final int[] _fuck = new int[2]; + + private GameOverlayFramebuffer overlayFramebuffer; + private final Minecraft mc; + private boolean invalid = false; + private boolean invalidDeep = false; + private int currentWidth = -1; + private int currentHeight = -1; + + public TouchOverlayRenderer(Minecraft mc) { + this.mc = mc; + this.overlayFramebuffer = new GameOverlayFramebuffer(false); + EnumTouchControl.currentLayout = null; + EnumTouchControl.setLayoutState(this, EnumTouchLayoutState.IN_GUI); + } + + public void invalidate() { + invalid = true; + } + + public void invalidateDeep() { + invalid = true; + invalidDeep = true; + } + + public void render(int w, int h, ScaledResolution scaledResolution) { + if(PointerInputAbstraction.isTouchMode()) { + render0(w, h, scaledResolution); + if(EnumTouchControl.KEYBOARD.visible) { + int[] pos = EnumTouchControl.KEYBOARD.getLocation(scaledResolution, _fuck); + int scale = scaledResolution.getScaleFactor(); + int size = EnumTouchControl.KEYBOARD.size * scale; + Touch.touchSetOpenKeyboardZone(pos[0] * scale, + (scaledResolution.getScaledHeight() - pos[1] - 1) * scale - size, size, size); + }else { + Touch.touchSetOpenKeyboardZone(0, 0, 0, 0); + } + }else { + Touch.touchSetOpenKeyboardZone(0, 0, 0, 0); + } + } + + private void render0(int w, int h, ScaledResolution scaledResolution) { + EnumTouchControl.setLayoutState(this, hashLayoutState()); + int sw = scaledResolution.getScaledWidth(); + int sh = scaledResolution.getScaledHeight(); + if(currentWidth != sw || currentHeight != sh) { + invalidateDeep(); + } + GlStateManager.disableDepth(); + GlStateManager.disableBlend(); + GlStateManager.disableLighting(); + GlStateManager.enableAlpha(); + GlStateManager.depthMask(false); + if(invalid) { + GlStateManager.pushMatrix(); + invalidDeep |= overlayFramebuffer.beginRender(sw, sh); + GlStateManager.viewport(0, 0, sw, sh); + if(invalidDeep) { + currentWidth = sw; + currentHeight = sh; + GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 0.0f); + GlStateManager.clear(GL_COLOR_BUFFER_BIT); + } + Set controls = Sets.newHashSet(EnumTouchControl._VALUES); + for (TouchControlInput input : TouchControls.touchControls.values()) { + controls.remove(input.control); + } + for (EnumTouchControl control : controls) { + if(invalidDeep || control.invalid) { + if(control.visible) { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + control.getRender().call(control, 0, 0, false, mc, scaledResolution); + } + control.invalid = false; + } + } + for (TouchControlInput input : TouchControls.touchControls.values()) { + EnumTouchControl control = input.control; + if(invalidDeep || control.invalid) { + if(control.visible) { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + control.getRender().call(control, input.x, input.y, true, mc, scaledResolution); + } + control.invalid = false; + } + } + overlayFramebuffer.endRender(); + invalid = false; + invalidDeep = false; + GlStateManager.popMatrix(); + GlStateManager.viewport(0, 0, w, h); + } + GlStateManager.bindTexture(overlayFramebuffer.getTexture()); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.enableAlpha(); + GlStateManager.color(1.0f, 1.0f, 1.0f, MathHelper.clamp_float(mc.gameSettings.touchControlOpacity, 0.0f, 1.0f)); + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(0.0D, (double) sh, 500.0D).tex(0.0D, 0.0D).endVertex(); + worldrenderer.pos((double) sw, (double) sh, 500.0D).tex(1.0D, 0.0D).endVertex(); + worldrenderer.pos((double) sw, 0.0D, 500.0D).tex(1.0D, 1.0D).endVertex(); + worldrenderer.pos(0.0D, 0.0D, 500.0D).tex(0.0D, 1.0D).endVertex(); + tessellator.draw(); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + GlStateManager.enableDepth(); + GlStateManager.depthMask(true); + } + + private EnumTouchLayoutState hashLayoutState() { + if(mc.currentScreen != null) { + return mc.currentScreen.showCopyPasteButtons() ? EnumTouchLayoutState.IN_GUI_TYPING + : (mc.currentScreen.canCloseGui() ? EnumTouchLayoutState.IN_GUI + : EnumTouchLayoutState.IN_GUI_NO_BACK); + } + EntityPlayerSP player = mc.thePlayer; + if(player != null) { + if(player.capabilities.isFlying) { + return showDiagButtons() ? EnumTouchLayoutState.IN_GAME_WALK_FLYING : EnumTouchLayoutState.IN_GAME_FLYING; + }else { + if(player.capabilities.allowFlying) { + return showDiagButtons() ? EnumTouchLayoutState.IN_GAME_WALK_CAN_FLY : EnumTouchLayoutState.IN_GAME_CAN_FLY; + }else { + return showDiagButtons() ? EnumTouchLayoutState.IN_GAME_WALK : EnumTouchLayoutState.IN_GAME; + } + } + }else { + return showDiagButtons() ? EnumTouchLayoutState.IN_GAME_WALK : EnumTouchLayoutState.IN_GAME; + } + } + + private boolean showDiagButtons() { + return TouchControls.isPressed(EnumTouchControl.DPAD_UP) + || TouchControls.isPressed(EnumTouchControl.DPAD_UP_LEFT) + || TouchControls.isPressed(EnumTouchControl.DPAD_UP_RIGHT); + } + + protected static void drawTexturedModalRect(float xCoord, float yCoord, int minU, int minV, int maxU, int maxV, int scaleFac) { + float f = 0.00390625F; + float f1 = 0.00390625F; + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) maxV * scaleFac), 0.0) + .tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + maxV) * f1)).endVertex(); + worldrenderer.pos((double) (xCoord + (float) maxU * scaleFac), (double) (yCoord + (float) maxV * scaleFac), 0.0) + .tex((double) ((float) (minU + maxU) * f), (double) ((float) (minV + maxV) * f1)).endVertex(); + worldrenderer.pos((double) (xCoord + (float) maxU * scaleFac), (double) (yCoord + 0.0F), 0.0) + .tex((double) ((float) (minU + maxU) * f), (double) ((float) (minV + 0) * f1)).endVertex(); + worldrenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0) + .tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + 0) * f1)).endVertex(); + tessellator.draw(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateDownloadSuccess.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateDownloadSuccess.java new file mode 100755 index 0000000..c6111e8 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateDownloadSuccess.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.update; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiUpdateDownloadSuccess extends GuiScreen { + + protected final GuiScreen parent; + protected final UpdateDataObj updateData; + + public GuiUpdateDownloadSuccess(GuiScreen parent, UpdateDataObj updateData) { + this.parent = parent; + this.updateData = updateData; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 56, I18n.format("updateSuccess.downloadOffline"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 86, I18n.format("updateSuccess.installToBootMenu"))); + this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 6 + 130, I18n.format("gui.cancel"))); + } + + public void actionPerformed(GuiButton btn) { + if(btn.id == 0) { + this.mc.loadingScreen.eaglerShow(I18n.format("updateSuccess.downloading"), null); + UpdateService.quine(updateData.clientSignature, updateData.clientBundle); + this.mc.displayGuiScreen(parent); + }else if(btn.id == 1) { + this.mc.displayGuiScreen(new GuiUpdateInstallOptions(this, parent, updateData)); + }else if(btn.id == 2) { + this.mc.displayGuiScreen(parent); + } + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("updateSuccess.title"), this.width / 2, 50, 11184810); + this.drawCenteredString(fontRendererObj, + updateData.clientSignature.bundleDisplayName + " " + updateData.clientSignature.bundleDisplayVersion, + this.width / 2, 70, 0xFFFFAA); + super.drawScreen(par1, par2, par3); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateInstallOptions.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateInstallOptions.java new file mode 100755 index 0000000..bcd252d --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateInstallOptions.java @@ -0,0 +1,84 @@ +package net.lax1dude.eaglercraft.v1_8.update; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiUpdateInstallOptions extends GuiScreen { + + protected final GuiScreen parent; + protected final GuiScreen onDone; + protected final UpdateDataObj updateData; + protected boolean makeDefault; + protected boolean enableCountdown; + protected GuiButton makeDefaultBtn; + protected GuiButton enableCountdownBtn; + + public GuiUpdateInstallOptions(GuiScreen parent, GuiScreen onDone, UpdateDataObj updateData) { + this.parent = parent; + this.onDone = onDone; + this.updateData = updateData; + makeDefault = updateData.clientSignature.bundleVersionInteger > EaglercraftVersion.updateBundlePackageVersionInt; + enableCountdown = makeDefault; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(makeDefaultBtn = new GuiButton(0, this.width / 2 - 100, this.height / 6 + 46, + I18n.format("updateInstall.setDefault") + ": " + I18n.format(makeDefault ? "gui.yes" : "gui.no"))); + this.buttonList.add(enableCountdownBtn = new GuiButton(1, this.width / 2 - 100, this.height / 6 + 76, + I18n.format("updateInstall.setCountdown") + ": " + + I18n.format(enableCountdown ? "gui.yes" : "gui.no"))); + this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 6 + 110, I18n.format("updateInstall.install"))); + this.buttonList.add(new GuiButton(3, this.width / 2 - 100, this.height / 6 + 140, I18n.format("gui.cancel"))); + + } + + public void actionPerformed(GuiButton btn) { + if(btn.id == 0) { + makeDefault = !makeDefault; + makeDefaultBtn.displayString = I18n.format("updateInstall.setDefault") + ": " + I18n.format(makeDefault ? "gui.yes" : "gui.no"); + }else if(btn.id == 1) { + enableCountdown = !enableCountdown; + enableCountdownBtn.displayString = I18n.format("updateInstall.setCountdown") + ": " + I18n.format(enableCountdown ? "gui.yes" : "gui.no"); + }else if(btn.id == 2) { + mc.loadingScreen.eaglerShow(I18n.format("updateSuccess.installing"), null); + try { + UpdateService.installSignedClient(updateData.clientSignature, updateData.clientBundle, makeDefault, enableCountdown); + }catch(Throwable t) { + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("installFailed.title", t.toString(), onDone)); + return; + } + mc.displayGuiScreen(onDone); + }else if(btn.id == 3) { + mc.displayGuiScreen(parent); + } + } + + public void drawScreen(int mx, int my, float partialTicks) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("updateInstall.title"), this.width / 2, 40, 11184810); + this.drawCenteredString(fontRendererObj, + updateData.clientSignature.bundleDisplayName + " " + updateData.clientSignature.bundleDisplayVersion, + this.width / 2, 60, 0xFFFFAA); + super.drawScreen(mx, my, partialTicks); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionList.java index 905fc16..6b2df74 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionList.java @@ -1,91 +1,102 @@ -package net.lax1dude.eaglercraft.v1_8.update; - -import java.io.IOException; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class GuiUpdateVersionList extends GuiScreen { - - final GuiScreen back; - GuiUpdateVersionSlot slots; - int selected; - GuiButton downloadButton; - int mx = 0; - int my = 0; - String tooltip = null; - - public GuiUpdateVersionList(GuiScreen back) { - this.back = back; - } - - public void initGui() { - selected = -1; - buttonList.clear(); - buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 28, 100, 20, I18n.format("gui.done"))); - buttonList.add(downloadButton = new GuiButton(1, this.width / 2 - 50, this.height - 28, 100, 20, I18n.format("updateList.download"))); - buttonList.add(new GuiButton(2, this.width / 2 - 154, this.height - 28, 100, 20, I18n.format("updateList.refresh"))); - slots = new GuiUpdateVersionSlot(this); - updateButtons(); - } - - void updateButtons() { - downloadButton.enabled = selected != -1; - } - - static Minecraft getMinecraft(GuiUpdateVersionList screen) { - return screen.mc; - } - - public void actionPerformed(GuiButton btn) { - switch(btn.id) { - case 1: - if(selected != -1) { - UpdateService.startClientUpdateFrom(slots.certList.get(selected)); - } - case 0: - default: - mc.displayGuiScreen(back); - break; - case 2: - this.initGui(); - break; - } - } - - public void drawScreen(int par1, int par2, float par3) { - mx = par1; - my = par2; - slots.drawScreen(par1, par2, par3); - this.drawCenteredString(fontRendererObj, I18n.format("updateList.title"), this.width / 2, 16, 16777215); - this.drawCenteredString(fontRendererObj, I18n.format("updateList.note.0"), this.width / 2, this.height - 55, 0x888888); - this.drawCenteredString(fontRendererObj, I18n.format("updateList.note.1"), this.width / 2, this.height - 45, 0x888888); - super.drawScreen(par1, par2, par3); - if(tooltip != null) { - drawHoveringText(mc.fontRendererObj.listFormattedStringToWidth(tooltip, 180), par1, par2); - tooltip = null; - } - } - - @Override - public void handleMouseInput() throws IOException { - super.handleMouseInput(); - slots.handleMouseInput(); - } -} +package net.lax1dude.eaglercraft.v1_8.update; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiUpdateVersionList extends GuiScreen { + + final GuiScreen back; + GuiUpdateVersionSlot slots; + int selected; + GuiButton downloadButton; + int mx = 0; + int my = 0; + String tooltip = null; + + public GuiUpdateVersionList(GuiScreen back) { + this.back = back; + } + + public void initGui() { + selected = -1; + buttonList.clear(); + buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 28, 100, 20, I18n.format("gui.done"))); + buttonList.add(downloadButton = new GuiButton(1, this.width / 2 - 50, this.height - 28, 100, 20, I18n.format("updateList.download"))); + buttonList.add(new GuiButton(2, this.width / 2 - 154, this.height - 28, 100, 20, I18n.format("updateList.refresh"))); + slots = new GuiUpdateVersionSlot(this); + updateButtons(); + } + + void updateButtons() { + downloadButton.enabled = selected != -1; + } + + static Minecraft getMinecraft(GuiUpdateVersionList screen) { + return screen.mc; + } + + public void actionPerformed(GuiButton btn) { + switch(btn.id) { + case 1: + if(selected != -1) { + UpdateService.startClientUpdateFrom(slots.certList.get(selected)); + } + case 0: + mc.displayGuiScreen(back); + break; + case 2: + this.initGui(); + break; + default: + break; + } + } + + public void drawScreen(int par1, int par2, float par3) { + mx = par1; + my = par2; + slots.drawScreen(par1, par2, par3); + this.drawCenteredString(fontRendererObj, I18n.format("updateList.title"), this.width / 2, 16, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("updateList.note.0"), this.width / 2, this.height - 55, 0x888888); + this.drawCenteredString(fontRendererObj, I18n.format("updateList.note.1"), this.width / 2, this.height - 45, 0x888888); + super.drawScreen(par1, par2, par3); + if(tooltip != null) { + drawHoveringText(mc.fontRendererObj.listFormattedStringToWidth(tooltip, 180), par1, par2); + GlStateManager.disableLighting(); + tooltip = null; + } + } + + @Override + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + slots.handleMouseInput(); + } + + @Override + public void handleTouchInput() throws IOException { + super.handleTouchInput(); + slots.handleTouchInput(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionSlot.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionSlot.java index 0ed9008..37a2d49 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionSlot.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateVersionSlot.java @@ -8,7 +8,6 @@ import java.util.Collection; import java.util.Date; import java.util.List; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; @@ -35,7 +34,7 @@ public class GuiUpdateVersionSlot extends GuiSlot { private static final ResourceLocation eaglerGuiTex = new ResourceLocation("eagler:gui/eagler_gui.png"); - final List certList = new ArrayList(); + final List certList = new ArrayList<>(); final GuiUpdateVersionList screen; @@ -86,7 +85,7 @@ public class GuiUpdateVersionSlot extends GuiSlot { screen.drawBackground(0); } - public static final SimpleDateFormat dateFmt = EagRuntime.fixDateFormat(new SimpleDateFormat("M/dd/yyyy")); + public static final SimpleDateFormat dateFmt = new SimpleDateFormat("M/dd/yyyy"); private static final char[] hexChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; @Override diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java index 2670be0..f574680 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java @@ -13,27 +13,20 @@ import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket00Handshake; +import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake; import net.minecraft.client.Minecraft; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -41,19 +34,19 @@ import net.minecraft.client.Minecraft; public class RelayUpdateChecker { private static class RelayEntry { - + private final String uri; private boolean queued; private boolean handshake; private RelayServerSocket currentSocket; - + private RelayEntry(String uri) { this.uri = uri; } - + } - private static final List relaysList = new ArrayList(); + private static final List relaysList = new ArrayList<>(); private static long lastUpdateCheck = -1l; private static boolean hasInit = false; @@ -63,16 +56,16 @@ public class RelayUpdateChecker { private static final String magic = "~!REQUEST_UPDATE_CERT"; public static void runTick() { - if (!EagRuntime.getConfiguration().isCheckRelaysForUpdates()) { + if(!EagRuntime.getConfiguration().isCheckRelaysForUpdates()) { return; } - if (!hasInit) { + if(!hasInit) { hasInit = true; - for (net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry etr : EagRuntime.getConfiguration().getRelays()) { + for(net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry etr : EagRuntime.getConfiguration().getRelays()) { relaysList.add(new RelayEntry(etr.address)); } byte[] b = PlatformApplication.getLocalStorage("lastRelayUpdate", false); - if (b != null) { + if(b != null) { try { lastUpdateCheck = (new DataInputStream(new EaglerInputStream(b))).readLong(); } catch (IOException e) { @@ -81,7 +74,8 @@ public class RelayUpdateChecker { } long millis = System.currentTimeMillis(); Minecraft mc = Minecraft.getMinecraft(); - if ((mc.theWorld == null || mc.isSingleplayer()) && millis - lastUpdateCheck > updateCheckRate) { + if ((mc.theWorld == null || mc.isSingleplayer()) + && (millis - lastUpdateCheck > updateCheckRate || millis + 60000l < lastUpdateCheck)) { lastUpdateCheck = millis; try { EaglerOutputStream bao = new EaglerOutputStream(8); @@ -93,21 +87,21 @@ public class RelayUpdateChecker { relaysList.get(i).queued = true; } } - for (int i = 0, l = relaysList.size(); i < l; ++i) { + for(int i = 0, l = relaysList.size(); i < l; ++i) { RelayEntry etr = relaysList.get(i); - if (etr.currentSocket != null) { + if(etr.currentSocket != null) { updateRelay(etr); - if (etr.currentSocket != null) { + if(etr.currentSocket != null) { return; } } } - for (int i = 0, l = relaysList.size(); i < l; ++i) { + for(int i = 0, l = relaysList.size(); i < l; ++i) { RelayEntry etr = relaysList.get(i); - if (etr.queued) { + if(etr.queued) { etr.queued = false; connect(etr); - if (etr.currentSocket != null) { + if(etr.currentSocket != null) { return; } } @@ -118,31 +112,31 @@ public class RelayUpdateChecker { try { socket.handshake = false; socket.currentSocket = PlatformWebRTC.openRelayConnection(socket.uri, 10000); - if (socket.currentSocket.isClosed()) { + if(socket.currentSocket.isClosed()) { socket.currentSocket = null; } - } catch (Throwable t) { + }catch(Throwable t) { } } private static void updateRelay(RelayEntry socket) { try { - if (socket.currentSocket.isClosed()) { + socket.currentSocket.update(); + if(socket.currentSocket.isClosed()) { socket.currentSocket = null; - } else if (socket.currentSocket.isOpen()) { - if (!socket.handshake) { + }else if(socket.currentSocket.isOpen()) { + if(!socket.handshake) { socket.handshake = true; - socket.currentSocket - .writePacket(new IPacket00Handshake(0x02, RelayManager.preferredRelayVersion, magic)); - } else { + socket.currentSocket.writePacket(new RelayPacket00Handshake(0x02, RelayManager.preferredRelayVersion, magic)); + }else { // close immediately - if (socket.currentSocket.nextPacket() != null) { + if(socket.currentSocket.nextPacket() != null) { socket.currentSocket.close(); socket.currentSocket = null; } } } - } catch (Throwable t) { + }catch(Throwable t) { } } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateDataObj.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateDataObj.java new file mode 100755 index 0000000..c6a0648 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateDataObj.java @@ -0,0 +1,28 @@ +package net.lax1dude.eaglercraft.v1_8.update; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class UpdateDataObj { + + public final UpdateCertificate clientSignature; + public final byte[] clientBundle; + + public UpdateDataObj(UpdateCertificate clientSignature, byte[] clientBundle) { + this.clientSignature = clientSignature; + this.clientBundle = clientBundle; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateResultObj.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateResultObj.java new file mode 100755 index 0000000..8113ced --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateResultObj.java @@ -0,0 +1,48 @@ +package net.lax1dude.eaglercraft.v1_8.update; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class UpdateResultObj { + + private final boolean success; + private final Object dataObj; + + private UpdateResultObj(boolean success, Object dataObj) { + this.success = success; + this.dataObj = dataObj; + } + + public static UpdateResultObj createSuccess(UpdateDataObj dataObj) { + return new UpdateResultObj(true, dataObj); + } + + public static UpdateResultObj createFailure(String dataObj) { + return new UpdateResultObj(false, dataObj); + } + + public boolean isSuccess() { + return success; + } + + public UpdateDataObj getSuccess() { + return (UpdateDataObj)dataObj; + } + + public String getFailure() { + return (String)dataObj; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java index 5adf145..214ef4c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java @@ -44,9 +44,9 @@ public class UpdateService { private static boolean isBundleDataValid = false; private static UpdateCertificate latestUpdateFound = null; - private static final Set availableUpdates = new HashSet(); - private static final Set fastUpdateKnownCheckSet = new HashSet(); - private static final Set dismissedUpdates = new HashSet(); + private static final Set availableUpdates = new HashSet<>(); + private static final Set fastUpdateKnownCheckSet = new HashSet<>(); + private static final Set dismissedUpdates = new HashSet<>(); private static class RawKnownCertHolder { @@ -57,7 +57,7 @@ public class UpdateService { public RawKnownCertHolder(byte[] data) { this.data = data; this.hashcode = Arrays.hashCode(data); - this.age = System.currentTimeMillis(); + this.age = EagRuntime.steadyTimeMillis(); } public int hashCode() { @@ -196,8 +196,8 @@ public class UpdateService { } private static void freeMemory() { - if (fastUpdateKnownCheckSet.size() > 127) { - List lst = new ArrayList(fastUpdateKnownCheckSet); + if(fastUpdateKnownCheckSet.size() > 127) { + List lst = new ArrayList<>(fastUpdateKnownCheckSet); fastUpdateKnownCheckSet.clear(); lst.sort((c1, c2) -> { return (int) (c2.age - c1.age); @@ -216,6 +216,15 @@ public class UpdateService { return PlatformUpdateSvc.getUpdatingStatus(); } + public static UpdateResultObj getUpdateResult() { + return PlatformUpdateSvc.getUpdateResult(); + } + + public static void installSignedClient(UpdateCertificate clientCert, byte[] clientPayload, boolean setDefault, + boolean setTimeout) { + PlatformUpdateSvc.installSignedClient(clientCert, clientPayload, setDefault, setTimeout); + } + public static UpdateCertificate getLatestUpdateFound() { return latestUpdateFound; } @@ -244,6 +253,10 @@ public class UpdateService { } } + public static void quine(UpdateCertificate cert, byte[] payload) { + PlatformUpdateSvc.quine(cert, payload); + } + public static boolean shouldDisableDownloadButton() { return EagRuntime.getConfiguration().getDownloadOfflineButtonLink() == null && (myUpdateCert == null || (getClientBundleData() == null && PlatformUpdateSvc.getUpdatingStatus().isBusy)); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java index c28e5ee..a3228f6 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java @@ -5,6 +5,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + /** * Copyright (c) 2022 ayunami2000. All Rights Reserved. * @@ -42,7 +44,7 @@ public class ExpiringSet extends HashSet { public void checkForExpirations() { Iterator iterator = this.timestamps.keySet().iterator(); - long now = System.currentTimeMillis(); + long now = EagRuntime.steadyTimeMillis(); while (iterator.hasNext()) { T element = iterator.next(); if (super.contains(element)) { @@ -61,7 +63,7 @@ public class ExpiringSet extends HashSet { public boolean add(T o) { checkForExpirations(); boolean success = super.add(o); - if (success) timestamps.put(o, System.currentTimeMillis()); + if (success) timestamps.put(o, EagRuntime.steadyTimeMillis()); return success; } @@ -81,4 +83,4 @@ public class ExpiringSet extends HashSet { checkForExpirations(); return super.contains(o); } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java index 19e0503..caa7290 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java @@ -5,8 +5,10 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.util.List; import java.util.Set; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiSlider2; import net.minecraft.client.Minecraft; @@ -21,24 +23,16 @@ import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -91,7 +85,7 @@ public class GuiVoiceMenu extends Gui { protected int voiceScreenButtonChangeRadiusposY; protected int voiceScreenButtonChangeRadiusposW; protected int voiceScreenButtonChangeRadiusposH; - + protected int voiceScreenVolumeIndicatorX; protected int voiceScreenVolumeIndicatorY; protected int voiceScreenVolumeIndicatorW; @@ -112,16 +106,16 @@ public class GuiVoiceMenu extends Gui { protected static boolean showingCompatWarning = false; protected static boolean showCompatWarning = true; - + protected static boolean showingTrackingWarning = false; protected static boolean showTrackingWarning = true; - + protected static EnumVoiceChannelType continueChannel = null; - + public GuiVoiceMenu(GuiScreen parent) { this.parent = parent; } - + public void setResolution(Minecraft mc, int w, int h) { this.mc = mc; this.fontRendererObj = mc.fontRendererObj; @@ -129,115 +123,89 @@ public class GuiVoiceMenu extends Gui { this.height = h; initGui(); } - + public void initGui() { - this.sliderBlocks = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 20, 150, 20, - (VoiceClientController.getVoiceProximity() - 5) / 17.0f, 1.0f) { - public boolean mousePressed(Minecraft par1Minecraft, int par2, int par3) { - if (super.mousePressed(par1Minecraft, par2, par3)) { - this.displayString = "" + (int) ((sliderValue * 17.0f) + 5.0f) + " Blocks"; - return true; - } else { - return false; - } - } - - public void mouseDragged(Minecraft par1Minecraft, int par2, int par3) { - super.mouseDragged(par1Minecraft, par2, par3); - this.displayString = "" + (int) ((sliderValue * 17.0f) + 5.0f) + " Blocks"; + this.sliderBlocks = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 20, 150, 20, (VoiceClientController.getVoiceProximity() - 5) / 17.0f, 1.0f) { + protected String updateDisplayString() { + return (int)((sliderValue * 17.0f) + 5.0f) + " Blocks"; } }; sliderBlocks.displayString = "" + VoiceClientController.getVoiceProximity() + " Blocks"; - this.sliderListenVolume = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 10, 150, 20, - VoiceClientController.getVoiceListenVolume(), 1.0f); - this.sliderSpeakVolume = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 56, 150, 20, - VoiceClientController.getVoiceSpeakVolume(), 1.0f); - + this.sliderListenVolume = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 10, 150, 20, VoiceClientController.getVoiceListenVolume(), 1.0f); + this.sliderSpeakVolume = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 56, 150, 20, VoiceClientController.getVoiceSpeakVolume(), 1.0f); + applyRadiusButton = new GuiButton(2, (width - 150) / 2, height / 3 + 49, 150, 20, I18n.format("voice.apply")); applyVolumeButton = new GuiButton(3, (width - 150) / 2, height / 3 + 90, 150, 20, I18n.format("voice.apply")); - noticeContinueButton = new GuiButton(5, (width - 150) / 2, height / 3 + 60, 150, 20, - I18n.format("voice.unsupportedWarning10")); - noticeCancelButton = new GuiButton(6, (width - 150) / 2, height / 3 + 90, 150, 20, - I18n.format("voice.unsupportedWarning11")); + noticeContinueButton = new GuiButton(5, (width - 150) / 2, height / 3 + 60, 150, 20, I18n.format("voice.unsupportedWarning10")); + noticeCancelButton = new GuiButton(6, (width - 150) / 2, height / 3 + 90, 150, 20, I18n.format("voice.unsupportedWarning11")); applyRadiusButton.visible = applyVolumeButton.visible = noticeContinueButton.visible = noticeCancelButton.visible = false; } - + private void drawButtons(int mx, int my, float partialTicks) { applyRadiusButton.drawButton(mc, mx, my); applyVolumeButton.drawButton(mc, mx, my); noticeContinueButton.drawButton(mc, mx, my); noticeCancelButton.drawButton(mc, mx, my); } - + public void drawScreen(int mx, int my, float partialTicks) { String txt = I18n.format("voice.title"); drawString(fontRendererObj, txt, width - 5 - fontRendererObj.getStringWidth(txt), 5, 0xFFCC22); - + applyRadiusButton.visible = showSliderBlocks; applyVolumeButton.visible = showSliderVolume; - - if (showSliderBlocks || showSliderVolume || showPTTKeyConfig) { - + + if(showSliderBlocks || showSliderVolume || showPTTKeyConfig) { + drawRect(0, 0, this.width, this.height, 0xB0101010); - - if (showSliderBlocks) { - - drawRect(width / 2 - 86, height / 4 - 1, this.width / 2 + 86, height / 3 + 64 + height / 16, - 0xFFDDDDDD); - drawRect(width / 2 - 85, height / 4 + 0, this.width / 2 + 85, height / 3 + 63 + height / 16, - 0xFF333333); - - drawCenteredString(this.fontRendererObj, I18n.format("voice.radiusTitle"), this.width / 2, - height / 4 + 9, 16777215); - drawString(this.fontRendererObj, I18n.format("voice.radiusLabel"), (this.width - 150) / 2 + 3, - height / 3 + 6, 0xCCCCCC); + + if(showSliderBlocks) { + + drawRect(width / 2 - 86, height / 4 - 1, this.width / 2 + 86, height / 3 + 64 + height / 16, 0xFFDDDDDD); + drawRect(width / 2 - 85, height / 4 + 0, this.width / 2 + 85, height / 3 + 63 + height / 16, 0xFF333333); + + drawCenteredString(this.fontRendererObj, I18n.format("voice.radiusTitle"), this.width / 2, height / 4 + 9, 16777215); + drawString(this.fontRendererObj, I18n.format("voice.radiusLabel"), (this.width - 150) / 2 + 3, height / 3 + 6, 0xCCCCCC); sliderBlocks.drawButton(mc, mx, my); - - } else if (showSliderVolume) { - - drawRect(width / 2 - 86, height / 4 - 11, this.width / 2 + 86, height / 3 + 104 + height / 16, - 0xFFDDDDDD); - drawRect(width / 2 - 85, height / 4 - 10, this.width / 2 + 85, height / 3 + 103 + height / 16, - 0xFF333333); - - drawCenteredString(this.fontRendererObj, I18n.format("voice.volumeTitle"), this.width / 2, - height / 4 - 1, 16777215); - drawString(this.fontRendererObj, I18n.format("voice.volumeListen"), (this.width - 150) / 2 + 3, - height / 3 - 4, 0xCCCCCC); + + }else if(showSliderVolume) { + + drawRect(width / 2 - 86, height / 4 - 11, this.width / 2 + 86, height / 3 + 104 + height / 16, 0xFFDDDDDD); + drawRect(width / 2 - 85, height / 4 - 10, this.width / 2 + 85, height / 3 + 103 + height / 16, 0xFF333333); + + drawCenteredString(this.fontRendererObj, I18n.format("voice.volumeTitle"), this.width / 2, height / 4 - 1, 16777215); + drawString(this.fontRendererObj, I18n.format("voice.volumeListen"), (this.width - 150) / 2 + 3, height / 3 - 4, 0xCCCCCC); sliderListenVolume.drawButton(mc, mx, my); - - drawString(this.fontRendererObj, I18n.format("voice.volumeSpeak"), (this.width - 150) / 2 + 3, - height / 3 + 42, 0xCCCCCC); + + drawString(this.fontRendererObj, I18n.format("voice.volumeSpeak"), (this.width - 150) / 2 + 3, height / 3 + 42, 0xCCCCCC); sliderSpeakVolume.drawButton(mc, mx, my); - - } else if (showPTTKeyConfig) { - + + }else if(showPTTKeyConfig) { + drawRect(width / 2 - 86, height / 3 - 10, this.width / 2 + 86, height / 3 + 35, 0xFFDDDDDD); drawRect(width / 2 - 85, height / 3 - 9, this.width / 2 + 85, height / 3 + 34, 0xFF333333); - - if (showNewPTTKey > 0) { + + if(showNewPTTKey > 0) { GlStateManager.pushMatrix(); GlStateManager.translate(this.width / 2, height / 3 + 5, 0.0f); GlStateManager.scale(2.0f, 2.0f, 2.0f); - drawCenteredString(this.fontRendererObj, Keyboard.getKeyName(mc.gameSettings.voicePTTKey), 0, 0, - 0xFFCC11); + drawCenteredString(this.fontRendererObj, Keyboard.getKeyName(mc.gameSettings.voicePTTKey), 0, 0, 0xFFCC11); GlStateManager.popMatrix(); - } else { - drawCenteredString(this.fontRendererObj, I18n.format("voice.pttChangeDesc"), this.width / 2, - height / 3 + 8, 16777215); + }else { + drawCenteredString(this.fontRendererObj, I18n.format("voice.pttChangeDesc"), this.width / 2, height / 3 + 8, 16777215); } } - + drawButtons(mx, my, partialTicks); throw new AbortedException(); } - + GlStateManager.pushMatrix(); - + GlStateManager.translate(width - 6, 15, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); - - if (!VoiceClientController.isClientSupported()) { + + if(!VoiceClientController.isClientSupported()) { txt = I18n.format("voice.titleVoiceUnavailable"); drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 6, 0xFF7777); txt = I18n.format("voice.titleVoiceBrowserError"); @@ -245,8 +213,8 @@ public class GuiVoiceMenu extends Gui { GlStateManager.popMatrix(); return; } - - if (!VoiceClientController.isServerSupported()) { + + if(!VoiceClientController.isServerSupported()) { txt = I18n.format("voice.titleNoVoice"); drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0xFF7777); GlStateManager.popMatrix(); @@ -255,33 +223,29 @@ public class GuiVoiceMenu extends Gui { int xo = 0; // this feature is optional - // if(VoiceClientController.voiceRelayed()) { - // txt = I18n.format("voice.warning1"); - // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 8, - // 0xBB9999); - // txt = I18n.format("voice.warning2"); - // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 18, - // 0xBB9999); - // txt = I18n.format("voice.warning3"); - // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 28, - // 0xBB9999); - // xo = 43; - // GlStateManager.translate(0.0f, xo, 0.0f); - // } - + //if(VoiceClientController.voiceRelayed()) { + // txt = I18n.format("voice.warning1"); + // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 8, 0xBB9999); + // txt = I18n.format("voice.warning2"); + // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 18, 0xBB9999); + // txt = I18n.format("voice.warning3"); + // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 28, 0xBB9999); + // xo = 43; + // GlStateManager.translate(0.0f, xo, 0.0f); + //} + EnumVoiceChannelStatus status = VoiceClientController.getVoiceStatus(); EnumVoiceChannelType channel = VoiceClientController.getVoiceChannel(); - + boolean flag = false; - - if (channel == EnumVoiceChannelType.NONE) { + + if(channel == EnumVoiceChannelType.NONE) { flag = true; - } else { - if (status == EnumVoiceChannelStatus.CONNECTED) { - - if (channel == EnumVoiceChannelType.PROXIMITY) { - txt = I18n.format("voice.connectedRadius") - .replace("$radius$", "" + VoiceClientController.getVoiceProximity()).replace("$f$", ""); + }else { + if(status == EnumVoiceChannelStatus.CONNECTED) { + + if(channel == EnumVoiceChannelType.PROXIMITY) { + txt = I18n.format("voice.connectedRadius").replace("$radius$", "" + VoiceClientController.getVoiceProximity()).replace("$f$", ""); int w = fontRendererObj.getStringWidth(txt); int xx = width - 5 - (w * 3 / 4); int yy = 15 + (xo * 3 / 4); @@ -289,65 +253,63 @@ public class GuiVoiceMenu extends Gui { voiceScreenButtonChangeRadiusposY = yy; voiceScreenButtonChangeRadiusposW = width - 3 - xx; voiceScreenButtonChangeRadiusposH = 12; - if (mx >= xx && my >= yy && mx < xx + voiceScreenButtonChangeRadiusposW && my < yy + 12) { - txt = I18n.format("voice.connectedRadius") - .replace("$radius$", "" + VoiceClientController.getVoiceProximity()) + if(mx >= xx && my >= yy && mx < xx + voiceScreenButtonChangeRadiusposW && my < yy + 12) { + txt = I18n.format("voice.connectedRadius").replace("$radius$", "" + VoiceClientController.getVoiceProximity()) .replace("$f$", "" + EnumChatFormatting.UNDERLINE) + EnumChatFormatting.RESET; } - } else { + }else { txt = I18n.format("voice.connectedGlobal"); } - + voiceScreenVolumeIndicatorX = width - 15 - (104 * 3 / 4); voiceScreenVolumeIndicatorY = 15 + (xo * 3 / 4) + 30; voiceScreenVolumeIndicatorW = width - voiceScreenVolumeIndicatorX - 4; voiceScreenVolumeIndicatorH = 23; - + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0x66DD66); drawRect(-90, 42, 2, 52, 0xFFAAAAAA); drawRect(-89, 43, 1, 51, 0xFF222222); - + float vol = VoiceClientController.getVoiceListenVolume(); - drawRect(-89, 43, -89 + (int) (vol * 90), 51, 0xFF993322); - - for (float f = 0.07f; f < vol; f += 0.08f) { - int ww = (int) (f * 90); + drawRect(-89, 43, -89 + (int)(vol * 90), 51, 0xFF993322); + + for(float f = 0.07f; f < vol; f += 0.08f) { + int ww = (int)(f * 90); drawRect(-89 + ww, 43, -89 + ww + 1, 51, 0xFF999999); } drawRect(-90, 57, 2, 67, 0xFFAAAAAA); drawRect(-89, 58, 1, 66, 0xFF222222); - + vol = VoiceClientController.getVoiceSpeakVolume(); - drawRect(-89, 58, -89 + (int) (vol * 90), 66, 0xFF993322); - - for (float f = 0.07f; f < vol; f += 0.08f) { - int ww = (int) (f * 90); + drawRect(-89, 58, -89 + (int)(vol * 90), 66, 0xFF993322); + + for(float f = 0.07f; f < vol; f += 0.08f) { + int ww = (int)(f * 90); drawRect(-89 + ww, 58, -89 + ww + 1, 66, 0xFF999999); } - + mc.getTextureManager().bindTexture(voiceGuiIcons); GlStateManager.color(0.7f, 0.7f, 0.7f, 1.0f); - + GlStateManager.pushMatrix(); GlStateManager.translate(-104.0f, 41.5f, 0.0f); GlStateManager.scale(0.7f, 0.7f, 0.7f); drawTexturedModalRect(0, 0, 64, 144, 16, 16); GlStateManager.popMatrix(); - + GlStateManager.pushMatrix(); GlStateManager.translate(-104.0f, 56.5f, 0.0f); GlStateManager.scale(0.7f, 0.7f, 0.7f); - if ((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) - && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)) { + if((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)) { GlStateManager.color(0.9f, 0.4f, 0.4f, 1.0f); drawTexturedModalRect(0, 0, 64, 64, 16, 16); - } else { + }else { drawTexturedModalRect(0, 0, 64, 32, 16, 16); } GlStateManager.popMatrix(); - + txt = I18n.format("voice.ptt", Keyboard.getKeyName(mc.gameSettings.voicePTTKey)); drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt) - 10, 76, 0x66DD66); @@ -358,9 +320,9 @@ public class GuiVoiceMenu extends Gui { GlStateManager.scale(0.35f, 0.35f, 0.35f); drawTexturedModalRect(0, 0, 32, 224, 32, 32); GlStateManager.popMatrix(); - + txt = I18n.format("voice.playersListening"); - + GlStateManager.pushMatrix(); GlStateManager.translate(0.0f, 98.0f, 0.0f); GlStateManager.scale(1.2f, 1.2f, 1.2f); @@ -368,73 +330,70 @@ public class GuiVoiceMenu extends Gui { GlStateManager.popMatrix(); List playersToRender = VoiceClientController.getVoiceRecent(); - - if (playersToRender.size() > 0) { + + if(playersToRender.size() > 0) { EaglercraftUUID uuid; Set playersSpeaking = VoiceClientController.getVoiceSpeaking(); Set playersMuted = VoiceClientController.getVoiceMuted(); - for (int i = 0, l = playersToRender.size(); i < l; ++i) { + for(int i = 0, l = playersToRender.size(); i < l; ++i) { uuid = playersToRender.get(i); txt = VoiceClientController.getVoiceUsername(uuid); - + boolean muted = playersMuted.contains(uuid); boolean speaking = !muted && playersSpeaking.contains(uuid); - + int mhy = voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 33 + i * 9; - boolean hovered = mx >= voiceScreenVolumeIndicatorX - 3 && my >= mhy - && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && my < mhy + 9; + boolean hovered = mx >= voiceScreenVolumeIndicatorX - 3 && my >= mhy && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && my < mhy + 9; float cm = hovered ? 1.5f : 1.0f; mc.getTextureManager().bindTexture(voiceGuiIcons); GlStateManager.pushMatrix(); GlStateManager.translate(-100.0f, 115.0f + i * 12.0f, 0.0f); GlStateManager.scale(0.78f, 0.78f, 0.78f); - - if (muted) { + + if(muted) { GlStateManager.color(1.0f * cm, 0.2f * cm, 0.2f * cm, 1.0f); drawTexturedModalRect(0, 0, 64, 208, 16, 16); - } else if (speaking) { + }else if(speaking) { GlStateManager.color(1.0f * cm, 1.0f * cm, 1.0f * cm, 1.0f); drawTexturedModalRect(0, 0, 64, 176, 16, 16); - } else { + }else { GlStateManager.color(0.65f * cm, 0.65f * cm, 0.65f * cm, 1.0f); drawTexturedModalRect(0, 0, 64, 144, 16, 16); } - + GlStateManager.popMatrix(); - if (muted) { + if(muted) { drawString(fontRendererObj, txt, -84, 117 + i * 12, attenuate(0xCC4444, cm)); - } else if (speaking) { + }else if(speaking) { drawString(fontRendererObj, txt, -84, 117 + i * 12, attenuate(0xCCCCCC, cm)); - } else { + }else { drawString(fontRendererObj, txt, -84, 117 + i * 12, attenuate(0x999999, cm)); } - + } - } else { + }else { txt = "(none)"; drawString(fontRendererObj, txt, -fontRendererObj.getStringWidth(txt), 112, 0xAAAAAA); } - - } else if (status == EnumVoiceChannelStatus.CONNECTING) { - float fadeTimer = MathHelper.sin((float) ((System.currentTimeMillis() % 700l) * 0.0014d) * 3.14159f) - * 0.35f + 0.3f; + + }else if(status == EnumVoiceChannelStatus.CONNECTING) { + float fadeTimer = MathHelper.sin((float)((EagRuntime.steadyTimeMillis() % 700l) * 0.0014d) * 3.14159f) * 0.35f + 0.3f; txt = I18n.format("voice.connecting"); GlStateManager.enableBlend(); GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, - (0xFFDD77 | ((int) (Math.pow(fadeTimer, 1.0d / 2.2d) * 255.0f) << 24))); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, (0xFFDD77 | ((int)(Math.pow(fadeTimer, 1.0d / 2.2d) * 255.0f) << 24))); GlStateManager.disableBlend(); - } else if (status == EnumVoiceChannelStatus.UNAVAILABLE) { + }else if(status == EnumVoiceChannelStatus.UNAVAILABLE) { txt = I18n.format("voice.unavailable"); drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0xFF3333); - } else { + }else { flag = true; } } - - if (flag) { + + if(flag) { txt = I18n.format("voice.notConnected"); drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0xBB9999); } @@ -446,7 +405,7 @@ public class GuiVoiceMenu extends Gui { int OFFwidth = fontRendererObj.getStringWidth(OFFstring); int RADIUSwidth = fontRendererObj.getStringWidth(RADIUSstring); int GLOBALwidth = fontRendererObj.getStringWidth(GLOBALstring); - + voiceButtonOFFposX = 0 - OFFwidth - 8 - RADIUSwidth - 8 - GLOBALwidth; voiceButtonOFFposY = 20; voiceButtonOFFposW = OFFwidth + 5; @@ -476,111 +435,88 @@ public class GuiVoiceMenu extends Gui { voiceScreenButtonGLOBALposY = 15 + (voiceButtonGLOBALposY + xo) * 3 / 4; voiceScreenButtonGLOBALposW = voiceButtonGLOBALposW * 3 / 4; voiceScreenButtonGLOBALposH = voiceButtonGLOBALposH * 3 / 4; - - if (channel == EnumVoiceChannelType.NONE) { + + if(channel == EnumVoiceChannelType.NONE) { drawOutline(voiceButtonOFFposX, voiceButtonOFFposY, voiceButtonOFFposW, voiceButtonOFFposH, 0xFFCCCCCC); drawRect(voiceButtonOFFposX + 1, voiceButtonOFFposY + 1, voiceButtonOFFposX + voiceButtonOFFposW - 2, voiceButtonOFFposY + voiceButtonOFFposH - 1, 0xFF222222); - } else if (mx >= voiceScreenButtonOFFposX && my >= voiceScreenButtonOFFposY && mx < voiceScreenButtonOFFposX + + }else if(mx >= voiceScreenButtonOFFposX && my >= voiceScreenButtonOFFposY && mx < voiceScreenButtonOFFposX + voiceScreenButtonOFFposW && my < voiceScreenButtonOFFposY + voiceScreenButtonOFFposH) { drawOutline(voiceButtonOFFposX, voiceButtonOFFposY, voiceButtonOFFposW, voiceButtonOFFposH, 0xFF777777); } - if (channel == EnumVoiceChannelType.PROXIMITY) { - drawOutline(voiceButtonRADIUSposX, voiceButtonRADIUSposY, voiceButtonRADIUSposW, voiceButtonRADIUSposH, - 0xFFCCCCCC); - drawRect(voiceButtonRADIUSposX + 1, voiceButtonRADIUSposY + 1, - voiceButtonRADIUSposX + voiceButtonRADIUSposW - 2, + if(channel == EnumVoiceChannelType.PROXIMITY) { + drawOutline(voiceButtonRADIUSposX, voiceButtonRADIUSposY, voiceButtonRADIUSposW, voiceButtonRADIUSposH, 0xFFCCCCCC); + drawRect(voiceButtonRADIUSposX + 1, voiceButtonRADIUSposY + 1, voiceButtonRADIUSposX + voiceButtonRADIUSposW - 2, voiceButtonRADIUSposY + voiceButtonRADIUSposH - 1, 0xFF222222); - } else if (mx >= voiceScreenButtonRADIUSposX && my >= voiceScreenButtonRADIUSposY - && mx < voiceScreenButtonRADIUSposX + - voiceScreenButtonRADIUSposW - && my < voiceScreenButtonRADIUSposY + voiceScreenButtonRADIUSposH) { - drawOutline(voiceButtonRADIUSposX, voiceButtonRADIUSposY, voiceButtonRADIUSposW, voiceButtonRADIUSposH, - 0xFF777777); + }else if(mx >= voiceScreenButtonRADIUSposX && my >= voiceScreenButtonRADIUSposY && mx < voiceScreenButtonRADIUSposX + + voiceScreenButtonRADIUSposW && my < voiceScreenButtonRADIUSposY + voiceScreenButtonRADIUSposH) { + drawOutline(voiceButtonRADIUSposX, voiceButtonRADIUSposY, voiceButtonRADIUSposW, voiceButtonRADIUSposH, 0xFF777777); } - if (channel == EnumVoiceChannelType.GLOBAL) { - drawOutline(voiceButtonGLOBALposX, voiceButtonGLOBALposY, voiceButtonGLOBALposW, voiceButtonGLOBALposH, - 0xFFCCCCCC); - drawRect(voiceButtonGLOBALposX + 1, voiceButtonGLOBALposY + 1, - voiceButtonGLOBALposX + voiceButtonGLOBALposW - 2, + if(channel == EnumVoiceChannelType.GLOBAL) { + drawOutline(voiceButtonGLOBALposX, voiceButtonGLOBALposY, voiceButtonGLOBALposW, voiceButtonGLOBALposH, 0xFFCCCCCC); + drawRect(voiceButtonGLOBALposX + 1, voiceButtonGLOBALposY + 1, voiceButtonGLOBALposX + voiceButtonGLOBALposW - 2, voiceButtonGLOBALposY + voiceButtonGLOBALposH - 1, 0xFF222222); - } else if (mx >= voiceScreenButtonGLOBALposX && my >= voiceScreenButtonGLOBALposY - && mx < voiceScreenButtonGLOBALposX + - voiceScreenButtonGLOBALposW - && my < voiceScreenButtonGLOBALposY + voiceScreenButtonGLOBALposH) { - drawOutline(voiceButtonGLOBALposX, voiceButtonGLOBALposY, voiceButtonGLOBALposW, voiceButtonGLOBALposH, - 0xFF777777); + }else if(mx >= voiceScreenButtonGLOBALposX && my >= voiceScreenButtonGLOBALposY && mx < voiceScreenButtonGLOBALposX + + voiceScreenButtonGLOBALposW && my < voiceScreenButtonGLOBALposY + voiceScreenButtonGLOBALposH) { + drawOutline(voiceButtonGLOBALposX, voiceButtonGLOBALposY, voiceButtonGLOBALposW, voiceButtonGLOBALposH, 0xFF777777); } - int enabledColor = (status == EnumVoiceChannelStatus.CONNECTED || channel == EnumVoiceChannelType.NONE) - ? 0x66DD66 - : 0xDDCC66; + int enabledColor = (status == EnumVoiceChannelStatus.CONNECTED || channel == EnumVoiceChannelType.NONE) ? 0x66DD66 : 0xDDCC66; int disabledColor = 0xDD4444; - - if (channel != EnumVoiceChannelType.NONE && status == EnumVoiceChannelStatus.UNAVAILABLE) { + + if(channel != EnumVoiceChannelType.NONE && status == EnumVoiceChannelStatus.UNAVAILABLE) { enabledColor = disabledColor; } - - drawString(fontRendererObj, OFFstring, 3 - OFFwidth - 8 - RADIUSwidth - 8 - GLOBALwidth, 24, - channel == EnumVoiceChannelType.NONE ? enabledColor : disabledColor); - drawString(fontRendererObj, RADIUSstring, 3 - RADIUSwidth - 8 - GLOBALwidth, 24, - channel == EnumVoiceChannelType.PROXIMITY ? enabledColor : disabledColor); - drawString(fontRendererObj, GLOBALstring, 3 - GLOBALwidth, 24, - channel == EnumVoiceChannelType.GLOBAL ? enabledColor : disabledColor); - + + drawString(fontRendererObj, OFFstring, 3 - OFFwidth - 8 - RADIUSwidth - 8 - GLOBALwidth, 24, channel == EnumVoiceChannelType.NONE ? enabledColor : disabledColor); + drawString(fontRendererObj, RADIUSstring, 3 - RADIUSwidth - 8 - GLOBALwidth, 24, channel == EnumVoiceChannelType.PROXIMITY ? enabledColor : disabledColor); + drawString(fontRendererObj, GLOBALstring, 3 - GLOBALwidth, 24, channel == EnumVoiceChannelType.GLOBAL ? enabledColor : disabledColor); + GlStateManager.popMatrix(); - - if (showingCompatWarning) { - - drawNotice(I18n.format("voice.unsupportedWarning1"), false, I18n.format("voice.unsupportedWarning2"), - I18n.format("voice.unsupportedWarning3"), - "", I18n.format("voice.unsupportedWarning4"), I18n.format("voice.unsupportedWarning5"), - I18n.format("voice.unsupportedWarning6"), - I18n.format("voice.unsupportedWarning7"), I18n.format("voice.unsupportedWarning8"), - I18n.format("voice.unsupportedWarning9")); - + + if(showingCompatWarning) { + + drawNotice(I18n.format("voice.unsupportedWarning1"), false, I18n.format("voice.unsupportedWarning2"), I18n.format("voice.unsupportedWarning3"), + "", I18n.format("voice.unsupportedWarning4"), I18n.format("voice.unsupportedWarning5"), I18n.format("voice.unsupportedWarning6"), + I18n.format("voice.unsupportedWarning7"), "", I18n.format("voice.unsupportedWarning8"), I18n.format("voice.unsupportedWarning9")); + noticeContinueButton.visible = true; noticeCancelButton.visible = false; - } else if (showingTrackingWarning) { - - drawNotice(I18n.format("voice.ipGrabWarning1"), true, I18n.format("voice.ipGrabWarning2"), - I18n.format("voice.ipGrabWarning3"), - I18n.format("voice.ipGrabWarning4"), "", I18n.format("voice.ipGrabWarning5"), - I18n.format("voice.ipGrabWarning6"), - I18n.format("voice.ipGrabWarning7"), I18n.format("voice.ipGrabWarning8"), - I18n.format("voice.ipGrabWarning9"), - I18n.format("voice.ipGrabWarning10"), I18n.format("voice.ipGrabWarning11"), - I18n.format("voice.ipGrabWarning12")); - + }else if(showingTrackingWarning) { + + drawNotice(I18n.format("voice.ipGrabWarning1"), true, I18n.format("voice.ipGrabWarning2"), I18n.format("voice.ipGrabWarning3"), + I18n.format("voice.ipGrabWarning4"), "", I18n.format("voice.ipGrabWarning5"), I18n.format("voice.ipGrabWarning6"), + I18n.format("voice.ipGrabWarning7")); + noticeContinueButton.visible = true; noticeCancelButton.visible = true; - } else { + }else { noticeContinueButton.visible = false; noticeCancelButton.visible = false; } - + drawButtons(mx, my, partialTicks); - if (showingCompatWarning || showingTrackingWarning) { + if(showingCompatWarning || showingTrackingWarning) { throw new AbortedException(); } } - + private void drawNotice(String title, boolean showCancel, String... lines) { - + int widthAccum = 0; - - for (int i = 0; i < lines.length; ++i) { + + for(int i = 0; i < lines.length; ++i) { int w = fontRendererObj.getStringWidth(lines[i]); - if (widthAccum < w) { + if(widthAccum < w) { widthAccum = w; } } - + int margin = 15; - + int x = (width - widthAccum) / 2; int y = (height - lines.length * 10 - 60 - margin) / 2; @@ -588,18 +524,18 @@ public class GuiVoiceMenu extends Gui { y + lines.length * 10 + 49 + margin, 0xFFCCCCCC); drawRect(x - margin, y - margin, x + widthAccum + margin, y + lines.length * 10 + 48 + margin, 0xFF111111); - + drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + title, width / 2, y, 0xFF7766); - - for (int i = 0; i < lines.length; ++i) { + + for(int i = 0; i < lines.length; ++i) { drawString(fontRendererObj, lines[i], x, y + i * 10 + 18, 0xDDAAAA); } - - if (!showCancel) { + + if(!showCancel) { noticeContinueButton.width = 150; noticeContinueButton.xPosition = (width - 150) / 2; noticeContinueButton.yPosition = y + lines.length * 10 + 29; - } else { + }else { noticeContinueButton.width = widthAccum / 2 - 10; noticeContinueButton.xPosition = (width - widthAccum) / 2 + widthAccum / 2 + 3; noticeContinueButton.yPosition = y + lines.length * 10 + 28; @@ -607,13 +543,13 @@ public class GuiVoiceMenu extends Gui { noticeCancelButton.xPosition = (width - widthAccum) / 2 + 4; noticeCancelButton.yPosition = y + lines.length * 10 + 28; } - + } - + public static int attenuate(int cin, float f) { return attenuate(cin, f, f, f, 1.0f); } - + public static int attenuate(int cin, float r, float g, float b, float a) { float var10 = (float) (cin >>> 24 & 255) / 255.0F; float var6 = (float) (cin >>> 16 & 255) / 255.0F; @@ -623,41 +559,44 @@ public class GuiVoiceMenu extends Gui { var6 *= r; var7 *= g; var8 *= b; - if (var10 > 1.0f) { + if(var10 > 1.0f) { var10 = 1.0f; } - if (var6 > 1.0f) { + if(var6 > 1.0f) { var6 = 1.0f; } - if (var7 > 1.0f) { + if(var7 > 1.0f) { var7 = 1.0f; } - if (var8 > 1.0f) { + if(var8 > 1.0f) { var8 = 1.0f; } - return (((int) (var10 * 255.0f) << 24) | ((int) (var6 * 255.0f) << 16) | ((int) (var7 * 255.0f) << 8) - | (int) (var8 * 255.0f)); + return (((int)(var10 * 255.0f) << 24) | ((int)(var6 * 255.0f) << 16) | ((int)(var7 * 255.0f) << 8) | (int)(var8 * 255.0f)); } - + private void drawOutline(int x, int y, int w, int h, int color) { drawRect(x, y, x + w, y + 1, color); drawRect(x + w - 1, y + 1, x + w, y + h - 1, color); drawRect(x, y + h - 1, x + w, y + h, color); drawRect(x, y + 1, x + 1, y + h - 1, color); } - + public void mouseReleased(int par1, int par2, int par3) { - applyRadiusButton.mouseReleased(par1, par2); - applyVolumeButton.mouseReleased(par1, par2); - noticeContinueButton.mouseReleased(par1, par2); - noticeCancelButton.mouseReleased(par1, par2); - if (showSliderBlocks || showSliderVolume) { - if (showSliderBlocks) { - if (par3 == 0) { + if(par3 != 0 && par3 != 12345) return; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if(!touchMode || par3 == 0) { + applyRadiusButton.mouseReleased(par1, par2); + applyVolumeButton.mouseReleased(par1, par2); + noticeContinueButton.mouseReleased(par1, par2); + noticeCancelButton.mouseReleased(par1, par2); + } + if(showSliderBlocks || showSliderVolume) { + if(showSliderBlocks) { + if(!touchMode || par3 == 12345) { sliderBlocks.mouseReleased(par1, par2); } - } else if (showSliderVolume) { - if (par3 == 0) { + }else if(showSliderVolume) { + if(!touchMode || par3 == 12345) { sliderListenVolume.mouseReleased(par1, par2); sliderSpeakVolume.mouseReleased(par1, par2); } @@ -665,13 +604,13 @@ public class GuiVoiceMenu extends Gui { throw new AbortedException(); } } - + public void keyTyped(char par1, int par2) { - if (showSliderBlocks || showSliderVolume || showPTTKeyConfig) { - if (showPTTKeyConfig) { - if (par2 == 1) { + if(showSliderBlocks || showSliderVolume || showPTTKeyConfig) { + if(showPTTKeyConfig) { + if(par2 == 1) { showPTTKeyConfig = false; - } else { + }else { mc.gameSettings.voicePTTKey = par2; showNewPTTKey = 10; } @@ -679,119 +618,98 @@ public class GuiVoiceMenu extends Gui { throw new AbortedException(); } } - + public void mouseClicked(int mx, int my, int button) { - if (showSliderBlocks || showSliderVolume || showPTTKeyConfig || showingCompatWarning - || showingTrackingWarning) { - if (showSliderBlocks) { - sliderBlocks.mousePressed(mc, mx, my); - } else if (showSliderVolume) { - sliderListenVolume.mousePressed(mc, mx, my); - sliderSpeakVolume.mousePressed(mc, mx, my); - } - if (button == 0) { - if (applyRadiusButton.mousePressed(mc, mx, my)) - actionPerformed(applyRadiusButton); - if (applyVolumeButton.mousePressed(mc, mx, my)) - actionPerformed(applyVolumeButton); - if (noticeContinueButton.mousePressed(mc, mx, my)) - actionPerformed(noticeContinueButton); - if (noticeCancelButton.mousePressed(mc, mx, my)) - actionPerformed(noticeCancelButton); + if(button != 0 && button != 12345) return; + boolean touchMode = PointerInputAbstraction.isTouchMode(); + if(showSliderBlocks || showSliderVolume || showPTTKeyConfig || showingCompatWarning || showingTrackingWarning) { + if(showSliderBlocks) { + if(!touchMode || button == 12345) { + sliderBlocks.mousePressed(mc, mx, my); + } + }else if(showSliderVolume) { + if(!touchMode || button == 12345) { + sliderListenVolume.mousePressed(mc, mx, my); + sliderSpeakVolume.mousePressed(mc, mx, my); + } } + if((!touchMode || button == 0) && applyRadiusButton.mousePressed(mc, mx, my)) actionPerformed(applyRadiusButton); + if((!touchMode || button == 0) && applyVolumeButton.mousePressed(mc, mx, my)) actionPerformed(applyVolumeButton); + if((!touchMode || button == 0) && noticeContinueButton.mousePressed(mc, mx, my)) actionPerformed(noticeContinueButton); + if((!touchMode || button == 0) && noticeCancelButton.mousePressed(mc, mx, my)) actionPerformed(noticeCancelButton); throw new AbortedException(); } - + EnumVoiceChannelStatus status = VoiceClientController.getVoiceStatus(); EnumVoiceChannelType channel = VoiceClientController.getVoiceChannel(); - - if (button == 0) { - if (VoiceClientController.isSupported()) { - if (mx >= voiceScreenButtonOFFposX && my >= voiceScreenButtonOFFposY && mx < voiceScreenButtonOFFposX + + + if(button == 0) { + if(VoiceClientController.isSupported()) { + if(mx >= voiceScreenButtonOFFposX && my >= voiceScreenButtonOFFposY && mx < voiceScreenButtonOFFposX + voiceScreenButtonOFFposW && my < voiceScreenButtonOFFposY + voiceScreenButtonOFFposH) { VoiceClientController.setVoiceChannel(EnumVoiceChannelType.NONE); - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - } else if (mx >= voiceScreenButtonRADIUSposX && my >= voiceScreenButtonRADIUSposY - && mx < voiceScreenButtonRADIUSposX + - voiceScreenButtonRADIUSposW - && my < voiceScreenButtonRADIUSposY + voiceScreenButtonRADIUSposH) { - - if (showCompatWarning) { + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(mx >= voiceScreenButtonRADIUSposX && my >= voiceScreenButtonRADIUSposY && mx < voiceScreenButtonRADIUSposX + + voiceScreenButtonRADIUSposW && my < voiceScreenButtonRADIUSposY + voiceScreenButtonRADIUSposH) { + + if(showCompatWarning) { continueChannel = EnumVoiceChannelType.PROXIMITY; showingCompatWarning = true; - } else if (showTrackingWarning) { + }else if(showTrackingWarning) { continueChannel = EnumVoiceChannelType.PROXIMITY; showingTrackingWarning = true; - } else { + }else { VoiceClientController.setVoiceChannel(EnumVoiceChannelType.PROXIMITY); } - - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - - } else if (mx >= voiceScreenButtonGLOBALposX && my >= voiceScreenButtonGLOBALposY - && mx < voiceScreenButtonGLOBALposX + - voiceScreenButtonGLOBALposW - && my < voiceScreenButtonGLOBALposY + voiceScreenButtonGLOBALposH) { - - if (showCompatWarning) { + + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + + }else if(mx >= voiceScreenButtonGLOBALposX && my >= voiceScreenButtonGLOBALposY && mx < voiceScreenButtonGLOBALposX + + voiceScreenButtonGLOBALposW && my < voiceScreenButtonGLOBALposY + voiceScreenButtonGLOBALposH) { + + if(showCompatWarning) { continueChannel = EnumVoiceChannelType.GLOBAL; showingCompatWarning = true; - } else if (showTrackingWarning) { + }else if(showTrackingWarning) { continueChannel = EnumVoiceChannelType.GLOBAL; showingTrackingWarning = true; - } else { + }else { VoiceClientController.setVoiceChannel(EnumVoiceChannelType.GLOBAL); } - - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - } else if (channel == EnumVoiceChannelType.PROXIMITY && status == EnumVoiceChannelStatus.CONNECTED - && mx >= voiceScreenButtonChangeRadiusposX && - my >= voiceScreenButtonChangeRadiusposY - && mx < voiceScreenButtonChangeRadiusposX + voiceScreenButtonChangeRadiusposW && + + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(channel == EnumVoiceChannelType.PROXIMITY && status == EnumVoiceChannelStatus.CONNECTED && mx >= voiceScreenButtonChangeRadiusposX && + my >= voiceScreenButtonChangeRadiusposY && mx < voiceScreenButtonChangeRadiusposX + voiceScreenButtonChangeRadiusposW && my < voiceScreenButtonChangeRadiusposY + voiceScreenButtonChangeRadiusposH) { showSliderBlocks = true; sliderBlocks.sliderValue = (VoiceClientController.getVoiceProximity() - 5) / 17.0f; - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - } else if (status == EnumVoiceChannelStatus.CONNECTED && channel != EnumVoiceChannelType.NONE - && mx >= voiceScreenVolumeIndicatorX && - my >= voiceScreenVolumeIndicatorY - && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW && + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(status == EnumVoiceChannelStatus.CONNECTED && channel != EnumVoiceChannelType.NONE && mx >= voiceScreenVolumeIndicatorX && + my >= voiceScreenVolumeIndicatorY && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW && my < voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH) { showSliderVolume = true; sliderListenVolume.sliderValue = VoiceClientController.getVoiceListenVolume(); sliderSpeakVolume.sliderValue = VoiceClientController.getVoiceSpeakVolume(); - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - } else if (status == EnumVoiceChannelStatus.CONNECTED && channel != EnumVoiceChannelType.NONE - && mx >= voiceScreenVolumeIndicatorX - 1 && - my >= voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 2 - && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(status == EnumVoiceChannelStatus.CONNECTED && channel != EnumVoiceChannelType.NONE && mx >= voiceScreenVolumeIndicatorX - 1 && + my >= voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 2 && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && my < voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 12) { showPTTKeyConfig = true; - this.mc.getSoundHandler() - .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - } else if (status == EnumVoiceChannelStatus.CONNECTED) { + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(status == EnumVoiceChannelStatus.CONNECTED) { List playersToRender = VoiceClientController.getVoiceRecent(); - if (playersToRender.size() > 0) { + if(playersToRender.size() > 0) { Set playersMuted = VoiceClientController.getVoiceMuted(); - for (int i = 0, l = playersToRender.size(); i < l; ++i) { + for(int i = 0, l = playersToRender.size(); i < l; ++i) { EaglercraftUUID uuid = playersToRender.get(i); String txt = VoiceClientController.getVoiceUsername(uuid); boolean muted = playersMuted.contains(uuid); int mhy = voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 33 + i * 9; - if (mx >= voiceScreenVolumeIndicatorX - 3 && my >= mhy - && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 - && my < mhy + 9) { + if(mx >= voiceScreenVolumeIndicatorX - 3 && my >= mhy && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && my < mhy + 9) { VoiceClientController.setVoiceMuted(uuid, !muted); - this.mc.getSoundHandler().playSound( - PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); break; } } @@ -799,50 +717,48 @@ public class GuiVoiceMenu extends Gui { } } } - + } - + private void actionPerformed(GuiButton btn) { - if (btn.id == 2) { + if(btn.id == 2) { showSliderBlocks = false; - VoiceClientController.setVoiceProximity( - mc.gameSettings.voiceListenRadius = (int) ((sliderBlocks.sliderValue * 17.0f) + 5.0f)); + VoiceClientController.setVoiceProximity(mc.gameSettings.voiceListenRadius = (int)((sliderBlocks.sliderValue * 17.0f) + 5.0f)); mc.gameSettings.saveOptions(); - } else if (btn.id == 3) { + }else if(btn.id == 3) { showSliderVolume = false; - VoiceClientController - .setVoiceListenVolume(mc.gameSettings.voiceListenVolume = sliderListenVolume.sliderValue); + VoiceClientController.setVoiceListenVolume(mc.gameSettings.voiceListenVolume = sliderListenVolume.sliderValue); VoiceClientController.setVoiceSpeakVolume(mc.gameSettings.voiceSpeakVolume = sliderSpeakVolume.sliderValue); mc.gameSettings.saveOptions(); - } else if (btn.id == 4) { + }else if(btn.id == 4) { showPTTKeyConfig = false; mc.gameSettings.saveOptions(); - } else if (btn.id == 5) { - if (showingCompatWarning) { + }else if(btn.id == 5) { + if(showingCompatWarning) { showingCompatWarning = false; showCompatWarning = false; - if (showTrackingWarning) { + if(showTrackingWarning) { showingTrackingWarning = true; - } else { + }else { VoiceClientController.setVoiceChannel(continueChannel); } - } else if (showingTrackingWarning) { + }else if(showingTrackingWarning) { showingTrackingWarning = false; showTrackingWarning = false; VoiceClientController.setVoiceChannel(continueChannel); } - } else if (btn.id == 6) { - if (showingTrackingWarning) { + }else if(btn.id == 6) { + if(showingTrackingWarning) { showingTrackingWarning = false; VoiceClientController.setVoiceChannel(EnumVoiceChannelType.NONE); } } } - + public void updateScreen() { - if (showNewPTTKey > 0) { + if(showNewPTTKey > 0) { --showNewPTTKey; - if (showNewPTTKey == 0) { + if(showNewPTTKey == 0) { showPTTKeyConfig = false; mc.gameSettings.saveOptions(); } @@ -850,8 +766,7 @@ public class GuiVoiceMenu extends Gui { } public boolean isBlockingInput() { - return showSliderBlocks || showSliderVolume || showPTTKeyConfig || showingCompatWarning - || showingTrackingWarning; + return showSliderBlocks || showSliderVolume || showPTTKeyConfig || showingCompatWarning || showingTrackingWarning; } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java index cf9f393..b6534cb 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; @@ -16,24 +17,16 @@ import net.minecraft.client.gui.GuiIngameMenu; import net.minecraft.util.ResourceLocation; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -43,73 +36,71 @@ public class GuiVoiceOverlay extends Gui { public final Minecraft mc; public int width; public int height; - + private long pttTimer = 0l; - + public GuiVoiceOverlay(Minecraft mc) { this.mc = mc; } - + public void setResolution(int w, int h) { this.width = w; this.height = h; } - + private static final ResourceLocation voiceGuiIcons = new ResourceLocation("eagler:gui/eagler_gui.png"); public void drawOverlay() { - if (mc.theWorld != null && VoiceClientController.getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED - && VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.NONE && + if(mc.theWorld != null && VoiceClientController.getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED && VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.NONE && !(mc.currentScreen != null && (mc.currentScreen instanceof GuiIngameMenu))) { - - if (mc.currentScreen != null && mc.currentScreen.doesGuiPauseGame()) { + + if(mc.currentScreen != null && mc.currentScreen.doesGuiPauseGame()) { return; } - + GlStateManager.disableLighting(); GlStateManager.disableBlend(); GlStateManager.enableAlpha(); GlStateManager.alphaFunc(GL_GREATER, 0.1F); GlStateManager.pushMatrix(); - - if (mc.currentScreen == null || (mc.currentScreen instanceof GuiChat)) { + + if(mc.currentScreen == null || (mc.currentScreen instanceof GuiChat)) { GlStateManager.translate(width / 2 + 77, height - 56, 0.0f); - if (mc.thePlayer == null || mc.thePlayer.capabilities.isCreativeMode) { + if(mc.thePlayer == null || mc.thePlayer.capabilities.isCreativeMode) { GlStateManager.translate(0.0f, 16.0f, 0.0f); } - } else { + }else { GlStateManager.translate(width / 2 + 10, 4, 0.0f); } GlStateManager.scale(0.75f, 0.75f, 0.75f); - + String txxt = "press '" + Keyboard.getKeyName(mc.gameSettings.voicePTTKey) + "'"; drawString(mc.fontRendererObj, txxt, -3 - mc.fontRendererObj.getStringWidth(txxt), 9, 0xDDDDDD); GlStateManager.scale(0.66f, 0.66f, 0.66f); - + mc.getTextureManager().bindTexture(voiceGuiIcons); - - if ((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) - && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)) { - long millis = System.currentTimeMillis(); - if (pttTimer == 0l) { + + if((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)) { + long millis = EagRuntime.steadyTimeMillis(); + if(pttTimer == 0l) { pttTimer = millis; } GlStateManager.color(0.2f, 0.2f, 0.2f, 1.0f); drawTexturedModalRect(0, 0, 0, 64, 32, 32); GlStateManager.translate(-1.5f, -1.5f, 0.0f); - if (millis - pttTimer < 1050l) { - if ((millis - pttTimer) % 300l < 150l) { + if(millis - pttTimer < 1050l) { + if((millis - pttTimer) % 300l < 150l) { GlStateManager.color(0.9f, 0.2f, 0.2f, 1.0f); - } else { + }else { GlStateManager.color(0.9f, 0.7f, 0.7f, 1.0f); } - } else { + }else { GlStateManager.color(0.9f, 0.3f, 0.3f, 1.0f); } drawTexturedModalRect(0, 0, 0, 64, 32, 32); - } else { + }else { pttTimer = 0l; GlStateManager.color(0.2f, 0.2f, 0.2f, 1.0f); drawTexturedModalRect(0, 0, 0, 32, 32, 32); @@ -119,55 +110,55 @@ public class GuiVoiceOverlay extends Gui { GlStateManager.translate(-0.5f, -0.5f, 0.0f); drawTexturedModalRect(0, 0, 0, 32, 32, 32); } - + GlStateManager.popMatrix(); - - if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) { + + if(VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) { Set listeners = VoiceClientController.getVoiceListening(); - if (listeners.size() > 0) { + if(listeners.size() > 0) { Set speakers = VoiceClientController.getVoiceSpeaking(); Set muted = VoiceClientController.getVoiceMuted(); - - List listenerList = new ArrayList(); + + List listenerList = new ArrayList<>(); listenerList.addAll(listeners); listenerList.removeAll(muted); - - while (listenerList.size() > 5) { + + while(listenerList.size() > 5) { boolean flag = false; - for (int i = 0, l = listenerList.size(); i < l; ++i) { - if (!speakers.contains(listenerList.get(i))) { + for(int i = 0, l = listenerList.size(); i < l; ++i) { + if(!speakers.contains(listenerList.get(i))) { listenerList.remove(i); flag = true; break; } } - if (!flag) { + if(!flag) { break; } } - + int more = listenerList.size() - 5; - + int ww = width; int hh = height; - - if (mc.currentScreen != null && (mc.currentScreen instanceof GuiChat)) { + + if(mc.currentScreen != null && (mc.currentScreen instanceof GuiChat)) { hh -= 15; } - - List listenerListStr = new ArrayList(Math.min(5, listenerList.size())); - + + List listenerListStr = new ArrayList<>(Math.min(5, listenerList.size())); + int left = 50; - for (int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { String txt = VoiceClientController.getVoiceUsername(listenerList.get(i)); listenerListStr.add(txt); int j = mc.fontRendererObj.getStringWidth(txt) + 4; - if (j > left) { + if(j > left) { left = j; } } - - if (more > 0) { + + if(more > 0) { GlStateManager.pushMatrix(); GlStateManager.translate(ww - left + 3, hh - 10, left); GlStateManager.scale(0.75f, 0.75f, 0.75f); @@ -175,64 +166,63 @@ public class GuiVoiceOverlay extends Gui { GlStateManager.popMatrix(); hh -= 9; } - - for (int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { boolean speaking = speakers.contains(listenerList.get(i)); float speakf = speaking ? 1.0f : 0.75f; - - drawString(mc.fontRendererObj, listenerListStr.get(i), ww - left, hh - 13 - i * 11, - speaking ? 0xEEEEEE : 0xBBBBBB); - + + drawString(mc.fontRendererObj, listenerListStr.get(i), ww - left, hh - 13 - i * 11, speaking ? 0xEEEEEE : 0xBBBBBB); + mc.getTextureManager().bindTexture(voiceGuiIcons); - + GlStateManager.pushMatrix(); GlStateManager.translate(ww - left - 14, hh - 14 - i * 11, 0.0f); - + GlStateManager.scale(0.75f, 0.75f, 0.75f); GlStateManager.color(speakf * 0.2f, speakf * 0.2f, speakf * 0.2f, 1.0f); drawTexturedModalRect(0, 0, 64, speaking ? 176 : 208, 16, 16); GlStateManager.translate(0.25f, 0.25f, 0.0f); drawTexturedModalRect(0, 0, 64, speaking ? 176 : 208, 16, 16); - + GlStateManager.translate(-1.25f, -1.25f, 0.0f); GlStateManager.color(speakf, speakf, speakf, 1.0f); drawTexturedModalRect(0, 0, 64, speaking ? 176 : 208, 16, 16); - + GlStateManager.popMatrix(); - + } - + } - } else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) { + }else if(VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) { Set speakers = VoiceClientController.getVoiceSpeaking(); Set muted = VoiceClientController.getVoiceMuted(); - - List listenerList = new ArrayList(); + + List listenerList = new ArrayList<>(); listenerList.addAll(speakers); listenerList.removeAll(muted); - + int more = listenerList.size() - 5; - + int ww = width; int hh = height; - - if (mc.currentScreen != null && (mc.currentScreen instanceof GuiChat)) { + + if(mc.currentScreen != null && (mc.currentScreen instanceof GuiChat)) { hh -= 15; } - - List listenerListStr = new ArrayList(Math.min(5, listenerList.size())); - + + List listenerListStr = new ArrayList<>(Math.min(5, listenerList.size())); + int left = 50; - for (int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { String txt = VoiceClientController.getVoiceUsername(listenerList.get(i)); listenerListStr.add(txt); int j = mc.fontRendererObj.getStringWidth(txt) + 4; - if (j > left) { + if(j > left) { left = j; } } - - if (more > 0) { + + if(more > 0) { GlStateManager.pushMatrix(); GlStateManager.translate(ww - left + 3, hh - 10, left); GlStateManager.scale(0.75f, 0.75f, 0.75f); @@ -240,30 +230,30 @@ public class GuiVoiceOverlay extends Gui { GlStateManager.popMatrix(); hh -= 9; } - - for (int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { drawString(mc.fontRendererObj, listenerListStr.get(i), ww - left, hh - 13 - i * 11, 0xEEEEEE); - + mc.getTextureManager().bindTexture(voiceGuiIcons); - + GlStateManager.pushMatrix(); GlStateManager.translate(ww - left - 14, hh - 14 - i * 11, 0.0f); - + GlStateManager.scale(0.75f, 0.75f, 0.75f); GlStateManager.color(0.2f, 0.2f, 0.2f, 1.0f); drawTexturedModalRect(0, 0, 64, 176, 16, 16); GlStateManager.translate(0.25f, 0.25f, 0.0f); drawTexturedModalRect(0, 0, 64, 176, 16, 16); - + GlStateManager.translate(-1.25f, -1.25f, 0.0f); GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); drawTexturedModalRect(0, 0, 64, 176, 16, 16); - + GlStateManager.popMatrix(); - + } } } } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java index ed35854..4e177ad 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java @@ -1,6 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.voice; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -16,29 +17,23 @@ import net.lax1dude.eaglercraft.v1_8.internal.PlatformVoiceClient; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketVoiceSignalGlobalEAG; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -51,7 +46,8 @@ public class VoiceClientController { private static boolean clientSupport = false; private static boolean serverSupport = false; - private static Consumer packetSendCallback = null; + private static Consumer packetSendCallback = null; + private static int protocolVersion = -1; private static EnumVoiceChannelType voiceChannel = EnumVoiceChannelType.NONE; private static final HashSet nearbyPlayers = new HashSet<>(); private static final ExpiringSet recentlyNearbyPlayers = new ExpiringSet<>(5000, uuid -> { @@ -79,30 +75,36 @@ public class VoiceClientController { return serverSupport; } - public static void initializeVoiceClient(Consumer signalSendCallbackIn) { + public static void initializeVoiceClient(Consumer signalSendCallbackIn, int proto) { packetSendCallback = signalSendCallbackIn; + protocolVersion = proto; uuidToNameLookup.clear(); - if (getVoiceChannel() != EnumVoiceChannelType.NONE) - sendInitialVoice(); + if (getVoiceChannel() != EnumVoiceChannelType.NONE) sendInitialVoice(); } - public static void handleVoiceSignalPacket(PacketBuffer packetData) { - VoiceSignalPackets.handleVoiceSignal(packetData); - } - - static void handleVoiceSignalPacketTypeGlobal(EaglercraftUUID[] voicePlayers, String[] voiceNames) { + public static void handleVoiceSignalPacketTypeGlobal(EaglercraftUUID[] voicePlayers, String[] voiceNames) { uuidToNameLookup.clear(); for (int i = 0; i < voicePlayers.length; i++) { - if (voiceNames != null) { + if(voiceNames != null) { uuidToNameLookup.put(voicePlayers[i], voiceNames[i]); } sendPacketRequestIfNeeded(voicePlayers[i]); } } + public static void handleVoiceSignalPacketTypeGlobalNew(Collection voicePlayers) { + uuidToNameLookup.clear(); + for (SPacketVoiceSignalGlobalEAG.UserData player : voicePlayers) { + EaglercraftUUID uuid = new EaglercraftUUID(player.uuidMost, player.uuidLeast); + if(player.username != null) { + uuidToNameLookup.put(uuid, player.username); + } + sendPacketRequestIfNeeded(uuid); + } + } + public static void handleServerDisconnect() { - if (!isClientSupported()) - return; + if(!isClientSupported()) return; serverSupport = false; uuidToNameLookup.clear(); for (EaglercraftUUID uuid : nearbyPlayers) { @@ -120,61 +122,53 @@ public class VoiceClientController { activateVoice(false); } - static void handleVoiceSignalPacketTypeAllowed(boolean voiceAvailableStat, String[] servs) { + public static void handleVoiceSignalPacketTypeAllowed(boolean voiceAvailableStat, String[] servs) { serverSupport = voiceAvailableStat; PlatformVoiceClient.setICEServers(servs); - if (isSupported()) { + if(isSupported()) { EnumVoiceChannelType ch = getVoiceChannel(); - setVoiceChannel(EnumVoiceChannelType.NONE); - setVoiceChannel(ch); + setVoiceChannel(EnumVoiceChannelType.NONE); + setVoiceChannel(ch); } } - static void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) { + public static void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) { PlatformVoiceClient.signalConnect(user, offer); } - static void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) { + public static void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) { sendPacketRequest(user); } - static void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) { + public static void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) { PlatformVoiceClient.signalDisconnect(user, true); } - static void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) { + public static void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) { PlatformVoiceClient.signalICECandidate(user, ice); } - static void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) { + public static void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) { PlatformVoiceClient.signalDescription(user, desc); } public static void tickVoiceClient(Minecraft mc) { - if (!isClientSupported()) - return; + if(!isClientSupported()) return; recentlyNearbyPlayers.checkForExpirations(); speakingSet.clear(); PlatformVoiceClient.tickVoiceClient(); - if (getVoiceChannel() != EnumVoiceChannelType.NONE && (getVoiceStatus() == EnumVoiceChannelStatus.CONNECTING - || getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED)) { - activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) - && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)); + if (getVoiceChannel() != EnumVoiceChannelType.NONE && (getVoiceStatus() == EnumVoiceChannelStatus.CONNECTING || getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED)) { + activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)); if (mc.theWorld != null && mc.thePlayer != null) { HashSet seenPlayers = new HashSet<>(); for (EntityPlayer player : mc.theWorld.playerEntities) { - if (player == mc.thePlayer) - continue; - if (getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) - updateVoicePosition(player.getUniqueID(), player.posX, player.posY + player.getEyeHeight(), - player.posZ); + if (player == mc.thePlayer) continue; + if (getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) updateVoicePosition(player.getUniqueID(), player.posX, player.posY + player.getEyeHeight(), player.posZ); int prox = 22; // cube - if (Math.abs(mc.thePlayer.posX - player.posX) <= prox - && Math.abs(mc.thePlayer.posY - player.posY) <= prox - && Math.abs(mc.thePlayer.posZ - player.posZ) <= prox) { + if (Math.abs(mc.thePlayer.posX - player.posX) <= prox && Math.abs(mc.thePlayer.posY - player.posY) <= prox && Math.abs(mc.thePlayer.posZ - player.posZ) <= prox) { if (!uuidToNameLookup.containsKey(player.getUniqueID())) { uuidToNameLookup.put(player.getUniqueID(), player.getName()); } @@ -199,17 +193,13 @@ public class VoiceClientController { public static final void removeNearbyPlayer(EaglercraftUUID uuid) { if (nearbyPlayers.remove(uuid)) { - if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED - || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) - return; - if (voiceChannel == EnumVoiceChannelType.PROXIMITY) - recentlyNearbyPlayers.add(uuid); + if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return; + if (voiceChannel == EnumVoiceChannelType.PROXIMITY) recentlyNearbyPlayers.add(uuid); } } public static final void cleanupNearbyPlayers(HashSet existingPlayers) { - nearbyPlayers.stream().filter(ud -> !existingPlayers.contains(ud)).collect(Collectors.toSet()) - .forEach(VoiceClientController::removeNearbyPlayer); + nearbyPlayers.stream().filter(ud -> !existingPlayers.contains(ud)).collect(Collectors.toSet()).forEach(VoiceClientController::removeNearbyPlayer); } public static final void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { @@ -217,10 +207,8 @@ public class VoiceClientController { } public static void setVoiceChannel(EnumVoiceChannelType channel) { - if (voiceChannel == channel) - return; - if (channel != EnumVoiceChannelType.NONE) - PlatformVoiceClient.initializeDevices(); + if (voiceChannel == channel) return; + if (channel != EnumVoiceChannelType.NONE) PlatformVoiceClient.initializeDevices(); PlatformVoiceClient.resetPeerStates(); if (channel == EnumVoiceChannelType.NONE) { for (EaglercraftUUID uuid : nearbyPlayers) { @@ -235,7 +223,7 @@ public class VoiceClientController { for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { PlatformVoiceClient.signalDisconnect(uuid, false); } - sendPacketDisconnect(null); + sendPacketDisconnect(); activateVoice(false); } else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) { for (EaglercraftUUID uuid : nearbyPlayers) { @@ -246,15 +234,15 @@ public class VoiceClientController { } nearbyPlayers.clear(); recentlyNearbyPlayers.clear(); - sendPacketDisconnect(null); - } else if (voiceChannel == EnumVoiceChannelType.GLOBAL) { + sendPacketDisconnect(); + } else if(voiceChannel == EnumVoiceChannelType.GLOBAL) { Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); antiConcurrentModificationUUIDs.removeAll(nearbyPlayers); antiConcurrentModificationUUIDs.removeAll(recentlyNearbyPlayers); for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { PlatformVoiceClient.signalDisconnect(uuid, false); } - sendPacketDisconnect(null); + sendPacketDisconnect(); } voiceChannel = channel; if (channel != EnumVoiceChannelType.NONE) { @@ -274,18 +262,12 @@ public class VoiceClientController { } private static boolean voicePeerErrored() { - return PlatformVoiceClient.getPeerState() == EnumVoiceChannelPeerState.FAILED - || PlatformVoiceClient.getPeerStateConnect() == EnumVoiceChannelPeerState.FAILED - || PlatformVoiceClient.getPeerStateInitial() == EnumVoiceChannelPeerState.FAILED - || PlatformVoiceClient.getPeerStateDesc() == EnumVoiceChannelPeerState.FAILED - || PlatformVoiceClient.getPeerStateIce() == EnumVoiceChannelPeerState.FAILED; + return PlatformVoiceClient.getPeerState() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateConnect() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateInitial() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateDesc() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateIce() == EnumVoiceChannelPeerState.FAILED; } - public static EnumVoiceChannelStatus getVoiceStatus() { - return (!isClientSupported() || !isServerSupported()) ? EnumVoiceChannelStatus.UNAVAILABLE - : (PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED - ? EnumVoiceChannelStatus.CONNECTING - : (voicePeerErrored() ? EnumVoiceChannelStatus.UNAVAILABLE : EnumVoiceChannelStatus.CONNECTED)); + return (!isClientSupported() || !isServerSupported()) ? EnumVoiceChannelStatus.UNAVAILABLE : + (PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED ? + EnumVoiceChannelStatus.CONNECTING : (voicePeerErrored() ? EnumVoiceChannelStatus.UNAVAILABLE : EnumVoiceChannelStatus.CONNECTED)); } private static boolean talkStatus = false; @@ -362,7 +344,7 @@ public class VoiceClientController { } public static String getVoiceUsername(EaglercraftUUID uuid) { - if (uuid == null) { + if(uuid == null) { return "null"; } String ret = uuidToNameLookup.get(uuid); @@ -370,32 +352,40 @@ public class VoiceClientController { } public static void sendPacketICE(EaglercraftUUID peerId, String candidate) { - packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketICE(peerId, candidate)); + packetSendCallback.accept(new CPacketVoiceSignalICEEAG(peerId.msb, peerId.lsb, candidate)); } public static void sendPacketDesc(EaglercraftUUID peerId, String desc) { - packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketDesc(peerId, desc)); + packetSendCallback.accept(new CPacketVoiceSignalDescEAG(peerId.msb, peerId.lsb, desc)); } - public static void sendPacketDisconnect(EaglercraftUUID peerId) { - packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketDisconnect(peerId)); + public static void sendPacketDisconnect() { + if(protocolVersion <= 3) { + packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG()); + }else { + packetSendCallback.accept(new CPacketVoiceSignalDisconnectV4EAG()); + } + } + + public static void sendPacketDisconnectPeer(EaglercraftUUID peerId) { + if(protocolVersion <= 3) { + packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG(true, peerId.msb, peerId.lsb)); + }else { + packetSendCallback.accept(new CPacketVoiceSignalDisconnectPeerV4EAG(peerId.msb, peerId.lsb)); + } } public static void sendPacketConnect() { - packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketConnect()); + packetSendCallback.accept(new CPacketVoiceSignalConnectEAG()); } public static void sendPacketRequest(EaglercraftUUID peerId) { - packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketRequest(peerId)); + packetSendCallback.accept(new CPacketVoiceSignalRequestEAG(peerId.msb, peerId.lsb)); } private static void sendPacketRequestIfNeeded(EaglercraftUUID uuid) { - if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED - || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) - return; - if (uuid.equals(EaglerProfile.getPlayerUUID())) - return; - if (!getVoiceListening().contains(uuid)) - sendPacketRequest(uuid); + if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return; + if(uuid.equals(EaglerProfile.getPlayerUUID())) return; + if (!getVoiceListening().contains(uuid)) sendPacketRequest(uuid); } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceSignalPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceSignalPackets.java deleted file mode 100644 index 010d965..0000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceSignalPackets.java +++ /dev/null @@ -1,155 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.voice; - -import java.nio.charset.StandardCharsets; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.minecraft.network.PacketBuffer; - -/** - * Copyright (c) 2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class VoiceSignalPackets { - - static final int VOICE_SIGNAL_ALLOWED = 0; - static final int VOICE_SIGNAL_REQUEST = 0; - static final int VOICE_SIGNAL_CONNECT = 1; - static final int VOICE_SIGNAL_DISCONNECT = 2; - static final int VOICE_SIGNAL_ICE = 3; - static final int VOICE_SIGNAL_DESC = 4; - static final int VOICE_SIGNAL_GLOBAL = 5; - - static void handleVoiceSignal(PacketBuffer streamIn) { - try { - int sig = streamIn.readUnsignedByte(); - switch (sig) { - case VOICE_SIGNAL_ALLOWED: { - boolean voiceAvailableStat = streamIn.readUnsignedByte() == 1; - String[] servs = null; - if (voiceAvailableStat) { - servs = new String[streamIn.readVarIntFromBuffer()]; - for (int i = 0; i < servs.length; i++) { - servs[i] = streamIn.readStringFromBuffer(1024); - } - } - VoiceClientController.handleVoiceSignalPacketTypeAllowed(voiceAvailableStat, servs); - break; - } - case VOICE_SIGNAL_GLOBAL: { - if (VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.GLOBAL) - return; - EaglercraftUUID[] voiceIds = new EaglercraftUUID[streamIn.readVarIntFromBuffer()]; - for (int i = 0; i < voiceIds.length; i++) { - voiceIds[i] = streamIn.readUuid(); - } - String[] voiceNames = null; - if (streamIn.isReadable()) { - voiceNames = new String[voiceIds.length]; - for (int i = 0; i < voiceNames.length; i++) { - voiceNames[i] = streamIn.readStringFromBuffer(16); - } - } - VoiceClientController.handleVoiceSignalPacketTypeGlobal(voiceIds, voiceNames); - break; - } - case VOICE_SIGNAL_CONNECT: { - EaglercraftUUID uuid = streamIn.readUuid(); - if (streamIn.isReadable()) { - VoiceClientController.handleVoiceSignalPacketTypeConnect(uuid, streamIn.readBoolean()); - } else if (VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.PROXIMITY - || VoiceClientController.getVoiceListening().contains(uuid)) { - VoiceClientController.handleVoiceSignalPacketTypeConnectAnnounce(uuid); - } - break; - } - case VOICE_SIGNAL_DISCONNECT: { - VoiceClientController.handleVoiceSignalPacketTypeDisconnect( - streamIn.readableBytes() > 0 ? streamIn.readUuid() : null); - break; - } - case VOICE_SIGNAL_ICE: { - VoiceClientController.handleVoiceSignalPacketTypeICECandidate(streamIn.readUuid(), - streamIn.readStringFromBuffer(32767)); - break; - } - case VOICE_SIGNAL_DESC: { - VoiceClientController.handleVoiceSignalPacketTypeDescription(streamIn.readUuid(), - streamIn.readStringFromBuffer(32767)); - break; - } - default: { - VoiceClientController.logger.error("Unknown voice signal packet '{}'!", sig); - break; - } - } - } catch (Throwable ex) { - VoiceClientController.logger.error("Failed to handle signal packet!"); - VoiceClientController.logger.error(ex); - } - } - - static PacketBuffer makeVoiceSignalPacketRequest(EaglercraftUUID user) { - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); - ret.writeByte(VOICE_SIGNAL_REQUEST); - ret.writeUuid(user); - return ret; - } - - static PacketBuffer makeVoiceSignalPacketICE(EaglercraftUUID user, String icePacket) { - byte[] str = icePacket.getBytes(StandardCharsets.UTF_8); - int estLen = 17 + PacketBuffer.getVarIntSize(str.length) + str.length; - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(estLen, estLen)); - ret.writeByte(VOICE_SIGNAL_ICE); - ret.writeUuid(user); - ret.writeByteArray(str); - return ret; - } - - static PacketBuffer makeVoiceSignalPacketDesc(EaglercraftUUID user, String descPacket) { - byte[] str = descPacket.getBytes(StandardCharsets.UTF_8); - int estLen = 17 + PacketBuffer.getVarIntSize(str.length) + str.length; - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(estLen, estLen)); - ret.writeByte(VOICE_SIGNAL_DESC); - ret.writeUuid(user); - ret.writeByteArray(str); - return ret; - } - - static PacketBuffer makeVoiceSignalPacketDisconnect(EaglercraftUUID user) { - if (user == null) { - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(1, 1)); - ret.writeByte(VOICE_SIGNAL_DISCONNECT); - return ret; - } - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); - ret.writeByte(VOICE_SIGNAL_DISCONNECT); - ret.writeUuid(user); - return ret; - } - - public static PacketBuffer makeVoiceSignalPacketConnect() { - PacketBuffer ret = new PacketBuffer(Unpooled.buffer(1, 1)); - ret.writeByte(VOICE_SIGNAL_CONNECT); - return ret; - } -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java index 85e42d9..b04bf21 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java @@ -33,7 +33,7 @@ public class VoiceTagRenderer { private static final ResourceLocation voiceGuiIcons = new ResourceLocation("eagler:gui/eagler_gui.png"); - private static final Set voiceTagsDrawnThisFrame = new HashSet(); + private static final Set voiceTagsDrawnThisFrame = new HashSet<>(); public static void renderVoiceNameTag(Minecraft mc, EntityOtherPlayerMP player, int offset) { EaglercraftUUID uuid = player.getUniqueID(); @@ -115,4 +115,4 @@ public class VoiceTagRenderer { voiceTagsDrawnThisFrame.clear(); } -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWaring.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWaring.java new file mode 100755 index 0000000..3ca323f --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWaring.java @@ -0,0 +1,104 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenPhishingWaring extends GuiScreen { + + public static boolean hasShownMessage = false; + + private static final ResourceLocation beaconGuiTexture = new ResourceLocation("textures/gui/container/beacon.png"); + + private GuiScreen cont; + private boolean mouseOverCheck; + private boolean hasCheckedBox; + + public GuiScreenPhishingWaring(GuiScreen cont) { + this.cont = cont; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 134, I18n.format("webviewPhishingWaring.continue"))); + } + + public void drawScreen(int mx, int my, float pt) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + I18n.format("webviewPhishingWaring.title"), this.width / 2, 70, 0xFF4444); + this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWaring.text0"), this.width / 2, 90, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWaring.text1"), this.width / 2, 102, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWaring.text2"), this.width / 2, 114, 16777215); + + String dontShowAgain = I18n.format("webviewPhishingWaring.dontShowAgain"); + int w = fontRendererObj.getStringWidth(dontShowAgain) + 20; + int ww = (this.width - w) / 2; + this.drawString(fontRendererObj, dontShowAgain, ww + 20, 137, 0xCCCCCC); + + mouseOverCheck = ww < mx && ww + 17 > mx && 133 < my && 150 > my; + + if(mouseOverCheck) { + GlStateManager.color(0.7f, 0.7f, 1.0f, 1.0f); + }else { + GlStateManager.color(0.6f, 0.6f, 0.6f, 1.0f); + } + + mc.getTextureManager().bindTexture(beaconGuiTexture); + + GlStateManager.pushMatrix(); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + drawTexturedModalRect(ww * 4 / 3, 133 * 4 / 3, 22, 219, 22, 22); + GlStateManager.popMatrix(); + + if(hasCheckedBox) { + GlStateManager.pushMatrix(); + GlStateManager.color(1.1f, 1.1f, 1.1f, 1.0f); + GlStateManager.translate(0.5f, 0.5f, 0.0f); + drawTexturedModalRect(ww, 133, 90, 222, 16, 16); + GlStateManager.popMatrix(); + } + + super.drawScreen(mx, my, pt); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + if(hasCheckedBox && !mc.gameSettings.hasHiddenPhishWarning) { + mc.gameSettings.hasHiddenPhishWarning = true; + mc.gameSettings.saveOptions(); + } + hasShownMessage = true; + mc.displayGuiScreen(cont); + } + } + + @Override + protected void mouseClicked(int mx, int my, int btn) { + if(btn == 0 && mouseOverCheck) { + hasCheckedBox = !hasCheckedBox; + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + super.mouseClicked(mx, my, btn); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java new file mode 100755 index 0000000..42dafd9 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java @@ -0,0 +1,203 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; +import net.lax1dude.eaglercraft.v1_8.IOUtils; +import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketRequestServerInfoV4EAG; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenRecieveServerInfo extends GuiScreen { + + private static final Logger logger = LogManager.getLogger("GuiScreenRecieveServerInfo"); + + protected final GuiScreen parent; + protected final byte[] expectHash; + protected int timer; + protected int timer2; + protected String statusString = "recieveServerInfo.checkingCache"; + + public GuiScreenRecieveServerInfo(GuiScreen parent, byte[] expectHash) { + this.parent = parent; + this.expectHash = expectHash; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 106, I18n.format("gui.cancel"))); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("recieveServerInfo.title"), this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, I18n.format(statusString), this.width / 2, 90, 16777215); + if(Arrays.equals(ServerInfoCache.chunkRecieveHash, expectHash) && ServerInfoCache.chunkFinalSize > 0) { + int progress = ServerInfoCache.chunkCurrentSize * 100 / ServerInfoCache.chunkFinalSize; + if(progress < 0) progress = 0; + if(progress > 100) progress = 100; + if(ServerInfoCache.hasLastChunk) { + progress = 100; + } + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + byte b0 = 100; + byte b1 = 2; + int i1 = width / 2 - b0 / 2; + int j1 = 103; + GlStateManager.disableTexture2D(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR); + worldrenderer.pos((double) i1, (double) j1, 0.0D).color(128, 128, 128, 255).endVertex(); + worldrenderer.pos((double) i1, (double) (j1 + b1), 0.0D).color(128, 128, 128, 255).endVertex(); + worldrenderer.pos((double) (i1 + b0), (double) (j1 + b1), 0.0D).color(128, 128, 128, 255) + .endVertex(); + worldrenderer.pos((double) (i1 + b0), (double) j1, 0.0D).color(128, 128, 128, 255).endVertex(); + worldrenderer.pos((double) i1, (double) j1, 0.0D).color(128, 255, 128, 255).endVertex(); + worldrenderer.pos((double) i1, (double) (j1 + b1), 0.0D).color(128, 255, 128, 255).endVertex(); + worldrenderer.pos((double) (i1 + progress), (double) (j1 + b1), 0.0D).color(128, 255, 128, 255) + .endVertex(); + worldrenderer.pos((double) (i1 + progress), (double) j1, 0.0D).color(128, 255, 128, 255) + .endVertex(); + tessellator.draw(); + GlStateManager.enableTexture2D(); + } + super.drawScreen(par1, par2, par3); + } + + public void actionPerformed(GuiButton button) { + if(button.id == 0) { + mc.displayGuiScreen(parent); + } + } + + public void updateScreen() { + if(mc.thePlayer == null) { + mc.displayGuiScreen(parent); + return; + } + ++timer; + if(timer == 1) { + byte[] data = ServerInfoCache.loadFromCache(expectHash); + if(data != null) { + mc.displayGuiScreen(GuiScreenServerInfo.createForCurrentState(parent, data, WebViewOptions.getEmbedOriginUUID(expectHash))); + }else { + byte[] b = mc.thePlayer.sendQueue.cachedServerInfoData; + if(b != null) { + if(b.length == 0) { + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); + }else { + ServerInfoCache.storeInCache(expectHash, b); + mc.displayGuiScreen(GuiScreenServerInfo.createForCurrentState(parent, b, WebViewOptions.getEmbedOriginUUID(expectHash))); + } + }else { + statusString = "recieveServerInfo.contactingServer"; + if(!mc.thePlayer.sendQueue.hasRequestedServerInfo) { + if(!ServerInfoCache.hasLastChunk || !Arrays.equals(ServerInfoCache.chunkRecieveHash, expectHash)) { + ServerInfoCache.clearDownload(); + mc.thePlayer.sendQueue.sendEaglerMessage(new CPacketRequestServerInfoV4EAG(expectHash)); + mc.thePlayer.sendQueue.hasRequestedServerInfo = true; + } + } + } + } + }else if(timer > 1) { + if(Arrays.equals(ServerInfoCache.chunkRecieveHash, expectHash)) { + if(ServerInfoCache.hasLastChunk) { + statusString = "recieveServerInfo.decompressing"; + ++timer2; + if(timer2 == 2) { + byte[] finalData = new byte[ServerInfoCache.chunkCurrentSize]; + int i = 0; + for(byte[] b : ServerInfoCache.chunkRecieveBuffer) { + System.arraycopy(b, 0, finalData, i, b.length); + i += b.length; + } + if(i != ServerInfoCache.chunkCurrentSize) { + logger.error("An unknown error occured!"); + mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); + return; + } + ServerInfoCache.clearDownload(); + try { + EaglerInputStream bis = new EaglerInputStream(finalData); + int finalSize = (new DataInputStream(bis)).readInt(); + if(finalSize < 0) { + logger.error("The response data was corrupt, decompressed size is negative!"); + mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); + return; + } + if(finalSize > ServerInfoCache.CACHE_MAX_SIZE * 2) { + logger.error("Failed to decompress/verify server info response! Size is massive, {} " + finalSize + " bytes reported!"); + logger.error("Aborting decompression. Rejoin the server to try again."); + mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); + return; + } + byte[] decompressed = new byte[finalSize]; + try(InputStream is = EaglerZLIB.newGZIPInputStream(bis)) { + IOUtils.readFully(is, decompressed); + } + SHA1Digest digest = new SHA1Digest(); + digest.update(decompressed, 0, decompressed.length); + byte[] csum = new byte[20]; + digest.doFinal(csum, 0); + if(Arrays.equals(csum, expectHash)) { + ServerInfoCache.storeInCache(csum, decompressed); + mc.thePlayer.sendQueue.cachedServerInfoData = decompressed; + mc.displayGuiScreen(GuiScreenServerInfo.createForCurrentState(parent, decompressed, WebViewOptions.getEmbedOriginUUID(expectHash))); + }else { + logger.error("The data recieved from the server did not have the correct SHA1 checksum! Rejoin the server to try again."); + mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); + } + }catch(IOException ex) { + logger.error("Failed to decompress/verify server info response! Rejoin the server to try again."); + logger.error(ex); + mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); + } + } + }else { + statusString = "recieveServerInfo.recievingData"; + } + }else { + statusString = "recieveServerInfo.contactingServer"; + } + } + } + + protected boolean isPartOfPauseMenu() { + return true; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java new file mode 100755 index 0000000..6da3305 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java @@ -0,0 +1,128 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import java.net.URI; +import java.net.URISyntaxException; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.internal.EnumWebViewContentMode; +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenServerInfo extends GuiScreen { + + private static final Logger logger = LogManager.getLogger("GuiScreenServerInfo"); + + private final GuiScreen parent; + private final WebViewOptions opts; + private boolean isShowing = false; + + public GuiScreenServerInfo(GuiScreen parent, WebViewOptions opts) { + this.parent = parent; + this.opts = opts; + } + + public static GuiScreen createForCurrentState(GuiScreen parent, String url) { + URI urlObj; + try { + urlObj = new URI(url); + }catch(URISyntaxException ex) { + logger.error("Refusing to iframe an invalid URL: {}", url); + logger.error(ex); + return new GuiScreenGenericErrorMessage("webviewInvalidURL.title", "webviewInvalidURL.desc", parent); + } + return createForCurrentState(parent, urlObj); + } + + public static GuiScreen createForCurrentState(GuiScreen parent, URI url) { + boolean support = WebViewOverlayController.supported(); + boolean fallbackSupport = WebViewOverlayController.fallbackSupported(); + if(!support && !fallbackSupport) { + return new GuiScreenGenericErrorMessage("webviewNotSupported.title", "webviewNotSupported.desc", parent); + } + WebViewOptions opts = new WebViewOptions(); + opts.contentMode = EnumWebViewContentMode.URL_BASED; + opts.url = url; + setupState(opts); + opts.permissionsOriginUUID = WebViewOptions.getURLOriginUUID(url); + return support ? new GuiScreenServerInfo(parent, opts) : new GuiScreenServerInfoDesktop(parent, opts); + } + + public static GuiScreen createForCurrentState(GuiScreen parent, byte[] blob, EaglercraftUUID permissionsOriginUUID) { + boolean support = WebViewOverlayController.supported(); + boolean fallbackSupport = WebViewOverlayController.fallbackSupported(); + if(!support && !fallbackSupport) { + return new GuiScreenGenericErrorMessage("webviewNotSupported.title", "webviewNotSupported.desc", parent); + } + WebViewOptions opts = new WebViewOptions(); + opts.contentMode = EnumWebViewContentMode.BLOB_BASED; + opts.blob = blob; + setupState(opts); + opts.permissionsOriginUUID = permissionsOriginUUID; + return support ? new GuiScreenServerInfo(parent, opts) : new GuiScreenServerInfoDesktop(parent, opts); + } + + public static void setupState(WebViewOptions opts) { + opts.scriptEnabled = (PauseMenuCustomizeState.serverInfoEmbedPerms & PauseMenuCustomizeState.SERVER_INFO_EMBED_PERMS_JAVASCRIPT) != 0; + opts.strictCSPEnable = (PauseMenuCustomizeState.serverInfoEmbedPerms & PauseMenuCustomizeState.SERVER_INFO_EMBED_PERMS_STRICT_CSP) != 0; + opts.serverMessageAPIEnabled = (PauseMenuCustomizeState.serverInfoEmbedPerms & PauseMenuCustomizeState.SERVER_INFO_EMBED_PERMS_MESSAGE_API) != 0; + opts.fallbackTitle = PauseMenuCustomizeState.serverInfoEmbedTitle; + } + + public void initGui() { + ScaledResolution res = mc.scaledResolution; + if(!isShowing) { + isShowing = true; + WebViewOverlayController.beginShowingSmart(opts, res, 30, 30, width - 60, height - 60); + }else { + WebViewOverlayController.resizeSmart(res, 30, 30, width - 60, height - 60); + } + buttonList.clear(); + buttonList.add(new GuiButton(0, (width - 200) / 2, height - 25, I18n.format("gui.done"))); + } + + public void onGuiClosed() { + if(isShowing) { + isShowing = false; + WebViewOverlayController.endShowing(); + } + } + + public void actionPerformed(GuiButton btn) { + if(btn.id == 0) { + mc.displayGuiScreen(parent); + } + } + + public void drawScreen(int mx, int my, float pt) { + drawDefaultBackground(); + drawCenteredString(fontRendererObj, PauseMenuCustomizeState.serverInfoEmbedTitle == null ? "Server Info" + : PauseMenuCustomizeState.serverInfoEmbedTitle, width / 2, 13, 0xFFFFFF); + super.drawScreen(mx, my, pt); + } + + protected boolean isPartOfPauseMenu() { + return true; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java new file mode 100755 index 0000000..6bcd667 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java @@ -0,0 +1,92 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenServerInfoDesktop extends GuiScreen { + + private final GuiScreen parent; + private final WebViewOptions opts; + + private int timer = 0; + private boolean hasStarted = false; + + private GuiButton btnOpen; + + public GuiScreenServerInfoDesktop(GuiScreen parent, WebViewOptions opts) { + this.parent = parent; + this.opts = opts; + } + + public void initGui() { + buttonList.clear(); + buttonList.add(btnOpen = new GuiButton(0, (width - 200) / 2, height / 6 + 110, I18n.format("fallbackWebViewScreen.openButton"))); + btnOpen.enabled = false; + buttonList.add(new GuiButton(1, (width - 200) / 2, height / 6 + 140, I18n.format("fallbackWebViewScreen.exitButton"))); + } + + public void updateScreen() { + ++timer; + if(timer == 2) { + WebViewOverlayController.endFallbackServer(); + WebViewOverlayController.launchFallback(opts); + }else if(timer > 2) { + if(WebViewOverlayController.fallbackRunning()) { + btnOpen.enabled = WebViewOverlayController.getFallbackURL() != null; + hasStarted = true; + }else { + btnOpen.enabled = false; + } + } + } + + public void actionPerformed(GuiButton button) { + if(button.id == 0) { + String link = WebViewOverlayController.getFallbackURL(); + if(link != null) { + EagRuntime.openLink(link); + } + }else if(button.id == 1) { + mc.displayGuiScreen(parent); + } + } + + public void onGuiClosed() { + WebViewOverlayController.endFallbackServer(); + } + + public void drawScreen(int mx, int my, float pt) { + drawDefaultBackground(); + drawCenteredString(fontRendererObj, PauseMenuCustomizeState.serverInfoEmbedTitle, this.width / 2, 70, 16777215); + drawCenteredString(fontRendererObj, I18n.format("fallbackWebViewScreen.text0"), this.width / 2, 90, 11184810); + String link = WebViewOverlayController.fallbackRunning() ? WebViewOverlayController.getFallbackURL() + : I18n.format(hasStarted ? "fallbackWebViewScreen.exited" : "fallbackWebViewScreen.startingUp"); + drawCenteredString(fontRendererObj, link != null ? link : I18n.format("fallbackWebViewScreen.pleaseWait"), + width / 2, 110, 16777215); + super.drawScreen(mx, my, pt); + } + + protected boolean isPartOfPauseMenu() { + return true; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/PermissionsCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/PermissionsCache.java new file mode 100755 index 0000000..5892205 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/PermissionsCache.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import java.util.HashMap; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PermissionsCache { + + public static class Permission { + + public final int perm; + public final boolean choice; + + private Permission(int perm, boolean choice) { + this.perm = perm; + this.choice = choice; + } + + } + + private static final Map javaScriptAllowed = new HashMap<>(); + + public static Permission getJavaScriptAllowed(EaglercraftUUID uuid, int flags) { + synchronized(javaScriptAllowed) { + if(uuid == null) { + return null; + } + Permission p = javaScriptAllowed.get(uuid); + if(p == null) { + return null; + } + return (p.perm | flags) != p.perm ? null : p; + } + } + + public static void setJavaScriptAllowed(EaglercraftUUID uuid, int flags, boolean allowed) { + synchronized(javaScriptAllowed) { + if(uuid != null) javaScriptAllowed.put(uuid, new Permission(flags, allowed)); + } + } + + public static void clearJavaScriptAllowed(EaglercraftUUID uuid) { + synchronized(javaScriptAllowed) { + if(uuid != null) javaScriptAllowed.remove(uuid); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/ServerInfoCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/ServerInfoCache.java new file mode 100755 index 0000000..018d94e --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/ServerInfoCache.java @@ -0,0 +1,130 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.HashKey; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketServerInfoDataChunkV4EAG; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ServerInfoCache { + + public static final int CACHE_MAX_SIZE = 0x200000; // 2 MB + + private static final Map cache = new HashMap<>(); + private static int cacheSize = 0; + + private static class CacheEntry { + + private final byte[] data; + private final HashKey hash; + private long lastHit; + + private CacheEntry(byte[] data, HashKey hash) { + this.data = data; + this.hash = hash; + this.lastHit = EagRuntime.steadyTimeMillis(); + } + + } + + protected static final List chunkRecieveBuffer = new LinkedList<>(); + protected static byte[] chunkRecieveHash = null; + protected static int chunkCurrentSize = 0; + protected static int chunkFinalSize = 0; + protected static boolean hasLastChunk = false; + + public static void handleChunk(SPacketServerInfoDataChunkV4EAG chunk) { + //System.out.println("p: " + chunk.seqId + " " + chunk.finalSize + " " + Base64.encodeBase64String(chunk.finalHash) + " " + chunk.lastChunk); + if (chunkRecieveHash == null || hasLastChunk || !Arrays.equals(chunk.finalHash, chunkRecieveHash) + || chunk.seqId != chunkRecieveBuffer.size()) { + chunkRecieveBuffer.clear(); + hasLastChunk = false; + chunkRecieveHash = null; + chunkCurrentSize = 0; + chunkFinalSize = 0; + if(chunk.seqId != 0) { + return; + } + chunkRecieveHash = chunk.finalHash; + } + chunkRecieveBuffer.add(chunk.data); + chunkCurrentSize += chunk.data.length; + chunkFinalSize = chunk.finalSize; + hasLastChunk = chunk.lastChunk; + } + + public static void clearDownload() { + chunkRecieveBuffer.clear(); + hasLastChunk = false; + chunkRecieveHash = null; + chunkCurrentSize = 0; + chunkFinalSize = 0; + } + + public static byte[] loadFromCache(byte[] hash) { + if(hash == null || hash.length != 20) { + return null; + } + CacheEntry etr = cache.get(new HashKey(hash)); + if(etr != null) { + etr.lastHit = EagRuntime.steadyTimeMillis(); + return etr.data; + }else { + return null; + } + } + + public static void storeInCache(byte[] hash, byte[] data) { + if(hash == null || hash.length != 20 || data == null) { + return; + } + HashKey hashObj = new HashKey(hash); + if(cache.containsKey(hashObj)) { + return; + } + shrink(data.length); + cache.put(hashObj, new CacheEntry(data, hashObj)); + cacheSize += data.length; + } + + private static void shrink(int toAdd) { + if(toAdd > CACHE_MAX_SIZE) { + cache.clear(); + cacheSize = 0; + return; + } + while(!cache.isEmpty() && cacheSize + toAdd > CACHE_MAX_SIZE) { + CacheEntry oldest = null; + for(CacheEntry e : cache.values()) { + if(oldest == null || e.lastHit < oldest.lastHit) { + oldest = e; + } + } + if(cache.remove(oldest.hash) != null) { + cacheSize -= oldest.data.length; + }else { + break; //wtf? + } + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/WebViewOverlayController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/WebViewOverlayController.java new file mode 100755 index 0000000..6779441 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/WebViewOverlayController.java @@ -0,0 +1,92 @@ +package net.lax1dude.eaglercraft.v1_8.webview; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebView; +import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG; +import net.minecraft.client.gui.ScaledResolution; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class WebViewOverlayController { + + public static boolean supported() { + return PlatformWebView.supported(); + } + + public static boolean isShowing() { + return PlatformWebView.isShowing(); + } + + public static void beginShowing(WebViewOptions options, int x, int y, int w, int h) { + PlatformWebView.beginShowing(options, x, y, w, h); + } + + public static void resize(int x, int y, int w, int h) { + PlatformWebView.resize(x, y, w, h); + } + + public static void beginShowingSmart(WebViewOptions options, ScaledResolution res, int x, int y, int w, int h) { + int fac = res.getScaleFactor(); + PlatformWebView.beginShowing(options, x * fac, y * fac, w * fac, h * fac); + } + + public static void resizeSmart(ScaledResolution res, int x, int y, int w, int h) { + int fac = res.getScaleFactor(); + PlatformWebView.resize(x * fac, y * fac, w * fac, h * fac); + } + + public static void endShowing() { + PlatformWebView.endShowing(); + } + + public static boolean fallbackSupported() { + return PlatformWebView.fallbackSupported(); + } + + public static void launchFallback(WebViewOptions options) { + PlatformWebView.launchFallback(options); + } + + public static boolean fallbackRunning() { + return PlatformWebView.fallbackRunning(); + } + + public static String getFallbackURL() { + return PlatformWebView.getFallbackURL(); + } + + public static void endFallbackServer() { + PlatformWebView.endFallbackServer(); + } + + public static void handleMessagePacket(SPacketWebViewMessageV4EAG packet) { + PlatformWebView.handleMessageFromServer(packet); + } + + public static interface IPacketSendCallback { + boolean sendPacket(GameMessagePacket packet); + } + + public static void setPacketSendCallback(IPacketSendCallback callback) { + PlatformWebView.setPacketSendCallback(callback); + } + + public static void runTick() { + PlatformWebView.runTick(); + } + +} diff --git a/src/main/java/net/minecraft/profiler/Profiler.java b/src/main/java/net/minecraft/profiler/Profiler.java deleted file mode 100644 index ca9bda3..0000000 --- a/src/main/java/net/minecraft/profiler/Profiler.java +++ /dev/null @@ -1,211 +0,0 @@ -package net.minecraft.profiler; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * + - * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. - * - * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" - * Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team - * - * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, hoosiertransfer, - * ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class Profiler { - private static final Logger logger = LogManager.getLogger(); - /** - * + - * List of parent sections - */ - private final List sectionList = Lists.newArrayList(); - /** - * + - * List of timestamps (System.nanoTime) - */ - private final List timestampList = Lists.newArrayList(); - public boolean profilingEnabled; - /** - * + - * Current profiling section - */ - private String profilingSection = ""; - private final Map profilingMap = Maps.newHashMap(); - - /** - * + - * Clear profiling. - */ - public void clearProfiling() { - this.profilingMap.clear(); - this.profilingSection = ""; - this.sectionList.clear(); - } - - /** - * + - * Start section - */ - public void startSection(String name) { - if (this.profilingEnabled) { - if (this.profilingSection.length() > 0) { - this.profilingSection = this.profilingSection + "."; - } - - this.profilingSection = this.profilingSection + name; - this.sectionList.add(this.profilingSection); - this.timestampList.add(Long.valueOf(System.nanoTime())); - } - } - - /** - * + - * End section - */ - public void endSection() { - if (this.profilingEnabled) { - long i = System.nanoTime(); - long j = ((Long) this.timestampList.remove(this.timestampList.size() - 1)).longValue(); - this.sectionList.remove(this.sectionList.size() - 1); - long k = i - j; - if (this.profilingMap.containsKey(this.profilingSection)) { - this.profilingMap.put(this.profilingSection, - Long.valueOf(((Long) this.profilingMap.get(this.profilingSection)).longValue() + k)); - } else { - this.profilingMap.put(this.profilingSection, Long.valueOf(k)); - } - - if (k > 100000000L) { - logger.warn("Something\'s taking too long! \'" + this.profilingSection + "\' took aprox " - + (double) k / 1000000.0D + " ms"); - } - - this.profilingSection = !this.sectionList.isEmpty() - ? (String) this.sectionList.get(this.sectionList.size() - 1) - : ""; - } - } - - /** - * + - * Get profiling data - */ - public List getProfilingData(String parString1) { - if (!this.profilingEnabled) { - return null; - } else { - long i = this.profilingMap.containsKey("root") ? ((Long) this.profilingMap.get("root")).longValue() : 0L; - long j = this.profilingMap.containsKey(parString1) ? ((Long) this.profilingMap.get(parString1)).longValue() - : -1L; - ArrayList arraylist = Lists.newArrayList(); - if (parString1.length() > 0) { - parString1 = parString1 + "."; - } - - long k = 0L; - - for (String s : this.profilingMap.keySet()) { - if (s.length() > parString1.length() && s.startsWith(parString1) - && s.indexOf(".", parString1.length() + 1) < 0) { - k += ((Long) this.profilingMap.get(s)).longValue(); - } - } - - float f = (float) k; - if (k < j) { - k = j; - } - - if (i < k) { - i = k; - } - - for (String s1 : this.profilingMap.keySet()) { - if (s1.length() > parString1.length() && s1.startsWith(parString1) - && s1.indexOf(".", parString1.length() + 1) < 0) { - long l = ((Long) this.profilingMap.get(s1)).longValue(); - double d0 = (double) l * 100.0D / (double) k; - double d1 = (double) l * 100.0D / (double) i; - String s2 = s1.substring(parString1.length()); - arraylist.add(new Profiler.Result(s2, d0, d1)); - } - } - - for (String s3 : this.profilingMap.keySet()) { - this.profilingMap.put(s3, Long.valueOf(((Long) this.profilingMap.get(s3)).longValue() * 999L / 1000L)); - } - - if ((float) k > f) { - arraylist.add(new Profiler.Result("unspecified", (double) ((float) k - f) * 100.0D / (double) k, - (double) ((float) k - f) * 100.0D / (double) i)); - } - - Collections.sort(arraylist); - arraylist.add(0, new Profiler.Result(parString1, 100.0D, (double) k * 100.0D / (double) i)); - return arraylist; - } - } - - /** - * + - * End current section and start a new section - */ - public void endStartSection(String name) { - this.endSection(); - this.startSection(name); - } - - public String getNameOfLastSection() { - return this.sectionList.size() == 0 ? "[UNKNOWN]" : (String) this.sectionList.get(this.sectionList.size() - 1); - } - - public static final class Result implements Comparable { - public double field_76332_a; - public double field_76330_b; - public String field_76331_c; - - public Result(String parString1, double parDouble1, double parDouble2) { - this.field_76331_c = parString1; - this.field_76332_a = parDouble1; - this.field_76330_b = parDouble2; - } - - public int compareTo(Profiler.Result profiler$result) { - return profiler$result.field_76332_a < this.field_76332_a ? -1 - : (profiler$result.field_76332_a > this.field_76332_a ? 1 - : profiler$result.field_76331_c.compareTo(this.field_76331_c)); - } - - public int func_76329_a() { - return (this.field_76331_c.hashCode() & 11184810) + 4473924; - } - } -} \ No newline at end of file diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java index 3be3e14..14f88e8 100644 --- a/src/main/java/org/json/JSONArray.java +++ b/src/main/java/org/json/JSONArray.java @@ -1,1697 +1,1977 @@ -package org.json; - -/* -Public Domain. - */ - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Array; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - - -/** - * A JSONArray is an ordered sequence of values. Its external text form is a - * string wrapped in square brackets with commas separating the values. The - * internal form is an object having get and opt - * methods for accessing the values by index, and put methods for - * adding or replacing values. The values can be any of these types: - * Boolean, JSONArray, JSONObject, - * Number, String, or the - * JSONObject.NULL object. - *

- * The constructor can convert a JSON text into a Java object. The - * toString method converts to JSON text. - *

- * A get method returns a value if one can be found, and throws an - * exception if one cannot be found. An opt method returns a - * default value instead of throwing an exception, and so is useful for - * obtaining optional values. - *

- * The generic get() and opt() methods return an - * object which you can cast or query for type. There are also typed - * get and opt methods that do type checking and type - * coercion for you. - *

- * The texts produced by the toString methods strictly conform to - * JSON syntax rules. The constructors are more forgiving in the texts they will - * accept: - *

    - *
  • An extra , (comma) may appear just - * before the closing bracket.
  • - *
  • The null value will be inserted when there is , - *  (comma) elision.
  • - *
  • Strings may be quoted with ' (single - * quote).
  • - *
  • Strings do not need to be quoted at all if they do not begin with a quote - * or single quote, and if they do not contain leading or trailing spaces, and - * if they do not contain any of these characters: - * { } [ ] / \ : , # and if they do not look like numbers and - * if they are not the reserved words true, false, or - * null.
  • - *
- * - * @author JSON.org - * @version 2016-08/15 - */ -public class JSONArray implements Iterable { - - /** - * The arrayList where the JSONArray's properties are kept. - */ - private final ArrayList myArrayList; - - /** - * Construct an empty JSONArray. - */ - public JSONArray() { - this.myArrayList = new ArrayList(); - } - - /** - * Construct a JSONArray from a JSONTokener. - * - * @param x - * A JSONTokener - * @throws JSONException - * If there is a syntax error. - */ - public JSONArray(JSONTokener x) throws JSONException { - this(); - if (x.nextClean() != '[') { - throw x.syntaxError("A JSONArray text must start with '['"); - } - - char nextChar = x.nextClean(); - if (nextChar == 0) { - // array is unclosed. No ']' found, instead EOF - throw x.syntaxError("Expected a ',' or ']'"); - } - if (nextChar != ']') { - x.back(); - for (;;) { - if (x.nextClean() == ',') { - x.back(); - this.myArrayList.add(JSONObject.NULL); - } else { - x.back(); - this.myArrayList.add(x.nextValue()); - } - switch (x.nextClean()) { - case 0: - // array is unclosed. No ']' found, instead EOF - throw x.syntaxError("Expected a ',' or ']'"); - case ',': - nextChar = x.nextClean(); - if (nextChar == 0) { - // array is unclosed. No ']' found, instead EOF - throw x.syntaxError("Expected a ',' or ']'"); - } - if (nextChar == ']') { - return; - } - x.back(); - break; - case ']': - return; - default: - throw x.syntaxError("Expected a ',' or ']'"); - } - } - } - } - - /** - * Construct a JSONArray from a source JSON text. - * - * @param source - * A string that begins with [ (left - * bracket) and ends with ] - *  (right bracket). - * @throws JSONException - * If there is a syntax error. - */ - public JSONArray(String source) throws JSONException { - this(new JSONTokener(source)); - } - - /** - * Construct a JSONArray from a Collection. - * - * @param collection - * A Collection. - */ - public JSONArray(Collection collection) { - if (collection == null) { - this.myArrayList = new ArrayList(); - } else { - this.myArrayList = new ArrayList(collection.size()); - this.addAll(collection, true); - } - } - - /** - * Construct a JSONArray from an Iterable. This is a shallow copy. - * - * @param iter - * A Iterable collection. - */ - public JSONArray(Iterable iter) { - this(); - if (iter == null) { - return; - } - this.addAll(iter, true); - } - - /** - * Construct a JSONArray from another JSONArray. This is a shallow copy. - * - * @param array - * A array. - */ - public JSONArray(JSONArray array) { - if (array == null) { - this.myArrayList = new ArrayList(); - } else { - // shallow copy directly the internal array lists as any wrapping - // should have been done already in the original JSONArray - this.myArrayList = new ArrayList(array.myArrayList); - } - } - - /** - * Construct a JSONArray from an array. - * - * @param array - * Array. If the parameter passed is null, or not an array, an - * exception will be thrown. - * - * @throws JSONException - * If not an array or if an array value is non-finite number. - * @throws NullPointerException - * Thrown if the array parameter is null. - */ - public JSONArray(Object array) throws JSONException { - this(); - if (!array.getClass().isArray()) { - throw new JSONException( - "JSONArray initial value should be a string or collection or array."); - } - this.addAll(array, true); - } - - /** - * Construct a JSONArray with the specified initial capacity. - * - * @param initialCapacity - * the initial capacity of the JSONArray. - * @throws JSONException - * If the initial capacity is negative. - */ - public JSONArray(int initialCapacity) throws JSONException { - if (initialCapacity < 0) { - throw new JSONException( - "JSONArray initial capacity cannot be negative."); - } - this.myArrayList = new ArrayList(initialCapacity); - } - - @Override - public Iterator iterator() { - return this.myArrayList.iterator(); - } - - /** - * Get the object value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return An object value. - * @throws JSONException - * If there is no value for the index. - */ - public Object get(int index) throws JSONException { - Object object = this.opt(index); - if (object == null) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - return object; - } - - /** - * Get the boolean value associated with an index. The string values "true" - * and "false" are converted to boolean. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The truth. - * @throws JSONException - * If there is no value for the index or if the value is not - * convertible to boolean. - */ - public boolean getBoolean(int index) throws JSONException { - Object object = this.get(index); - if (object.equals(Boolean.FALSE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("false"))) { - return false; - } else if (object.equals(Boolean.TRUE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("true"))) { - return true; - } - throw wrongValueFormatException(index, "boolean", object, null); - } - - /** - * Get the double value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. - */ - public double getDouble(int index) throws JSONException { - final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).doubleValue(); - } - try { - return Double.parseDouble(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(index, "double", object, e); - } - } - - /** - * Get the float value associated with a key. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public float getFloat(int index) throws JSONException { - final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).floatValue(); - } - try { - return Float.parseFloat(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(index, "float", object, e); - } - } - - /** - * Get the Number value associated with a key. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public Number getNumber(int index) throws JSONException { - Object object = this.get(index); - try { - if (object instanceof Number) { - return (Number)object; - } - return JSONObject.stringToNumber(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(index, "number", object, e); - } - } - - /** - * Get the enum value associated with an index. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. - * @return The enum value at the index location - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an enum. - */ - public > E getEnum(Class clazz, int index) throws JSONException { - E val = optEnum(clazz, index); - if(val==null) { - // JSONException should really take a throwable argument. - // If it did, I would re-implement this with the Enum.valueOf - // method and place any thrown exception in the JSONException - throw wrongValueFormatException(index, "enum of type " - + JSONObject.quote(clazz.getSimpleName()), opt(index), null); - } - return val; - } - - /** - * Get the BigDecimal value associated with an index. If the value is float - * or double, the {@link BigDecimal#BigDecimal(double)} constructor - * will be used. See notes on the constructor for conversion issues that - * may arise. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a BigDecimal. - */ - public BigDecimal getBigDecimal (int index) throws JSONException { - Object object = this.get(index); - BigDecimal val = JSONObject.objectToBigDecimal(object, null); - if(val == null) { - throw wrongValueFormatException(index, "BigDecimal", object, null); - } - return val; - } - - /** - * Get the BigInteger value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a BigInteger. - */ - public BigInteger getBigInteger (int index) throws JSONException { - Object object = this.get(index); - BigInteger val = JSONObject.objectToBigInteger(object, null); - if(val == null) { - throw wrongValueFormatException(index, "BigInteger", object, null); - } - return val; - } - - /** - * Get the int value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value is not a number. - */ - public int getInt(int index) throws JSONException { - final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).intValue(); - } - try { - return Integer.parseInt(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(index, "int", object, e); - } - } - - /** - * Get the JSONArray associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A JSONArray value. - * @throws JSONException - * If there is no value for the index. or if the value is not a - * JSONArray - */ - public JSONArray getJSONArray(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof JSONArray) { - return (JSONArray) object; - } - throw wrongValueFormatException(index, "JSONArray", object, null); - } - - /** - * Get the JSONObject associated with an index. - * - * @param index - * subscript - * @return A JSONObject value. - * @throws JSONException - * If there is no value for the index or if the value is not a - * JSONObject - */ - public JSONObject getJSONObject(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof JSONObject) { - return (JSONObject) object; - } - throw wrongValueFormatException(index, "JSONObject", object, null); - } - - /** - * Get the long value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. - */ - public long getLong(int index) throws JSONException { - final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).longValue(); - } - try { - return Long.parseLong(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(index, "long", object, e); - } - } - - /** - * Get the string associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A string value. - * @throws JSONException - * If there is no string value for the index. - */ - public String getString(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof String) { - return (String) object; - } - throw wrongValueFormatException(index, "String", object, null); - } - - /** - * Determine if the value is null. - * - * @param index - * The index must be between 0 and length() - 1. - * @return true if the value at the index is null, or if there is no value. - */ - public boolean isNull(int index) { - return JSONObject.NULL.equals(this.opt(index)); - } - - /** - * Make a string from the contents of this JSONArray. The - * separator string is inserted between each element. Warning: - * This method assumes that the data structure is acyclical. - * - * @param separator - * A string that will be inserted between the elements. - * @return a string. - * @throws JSONException - * If the array contains an invalid number. - */ - public String join(String separator) throws JSONException { - int len = this.length(); - if (len == 0) { - return ""; - } - - StringBuilder sb = new StringBuilder( - JSONObject.valueToString(this.myArrayList.get(0))); - - for (int i = 1; i < len; i++) { - sb.append(separator) - .append(JSONObject.valueToString(this.myArrayList.get(i))); - } - return sb.toString(); - } - - /** - * Get the number of elements in the JSONArray, included nulls. - * - * @return The length (or size). - */ - public int length() { - return this.myArrayList.size(); - } - - /** - * Removes all of the elements from this JSONArray. - * The JSONArray will be empty after this call returns. - */ - public void clear() { - this.myArrayList.clear(); - } - - /** - * Get the optional object value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. If not, null is returned. - * @return An object value, or null if there is no object at that index. - */ - public Object opt(int index) { - return (index < 0 || index >= this.length()) ? null : this.myArrayList - .get(index); - } - - /** - * Get the optional boolean value associated with an index. It returns false - * if there is no value at that index, or if the value is not Boolean.TRUE - * or the String "true". - * - * @param index - * The index must be between 0 and length() - 1. - * @return The truth. - */ - public boolean optBoolean(int index) { - return this.optBoolean(index, false); - } - - /** - * Get the optional boolean value associated with an index. It returns the - * defaultValue if there is no value at that index or if it is not a Boolean - * or the String "true" or "false" (case insensitive). - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * A boolean default. - * @return The truth. - */ - public boolean optBoolean(int index, boolean defaultValue) { - try { - return this.getBoolean(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional double value associated with an index. NaN is returned - * if there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public double optDouble(int index) { - return this.optDouble(index, Double.NaN); - } - - /** - * Get the optional double value associated with an index. The defaultValue - * is returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * subscript - * @param defaultValue - * The default value. - * @return The value. - */ - public double optDouble(int index, double defaultValue) { - final Number val = this.optNumber(index, null); - if (val == null) { - return defaultValue; - } - final double doubleValue = val.doubleValue(); - // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { - // return defaultValue; - // } - return doubleValue; - } - - /** - * Get the optional float value associated with an index. NaN is returned - * if there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public float optFloat(int index) { - return this.optFloat(index, Float.NaN); - } - - /** - * Get the optional float value associated with an index. The defaultValue - * is returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * subscript - * @param defaultValue - * The default value. - * @return The value. - */ - public float optFloat(int index, float defaultValue) { - final Number val = this.optNumber(index, null); - if (val == null) { - return defaultValue; - } - final float floatValue = val.floatValue(); - // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { - // return floatValue; - // } - return floatValue; - } - - /** - * Get the optional int value associated with an index. Zero is returned if - * there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public int optInt(int index) { - return this.optInt(index, 0); - } - - /** - * Get the optional int value associated with an index. The defaultValue is - * returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public int optInt(int index, int defaultValue) { - final Number val = this.optNumber(index, null); - if (val == null) { - return defaultValue; - } - return val.intValue(); - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. - * @return The enum value at the index location or null if not found - */ - public > E optEnum(Class clazz, int index) { - return this.optEnum(clazz, index, null); - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default in case the value is not found - * @return The enum value at the index location or defaultValue if - * the value is not found or cannot be assigned to clazz - */ - public > E optEnum(Class clazz, int index, E defaultValue) { - try { - Object val = this.opt(index); - if (JSONObject.NULL.equals(val)) { - return defaultValue; - } - if (clazz.isAssignableFrom(val.getClass())) { - // we just checked it! - @SuppressWarnings("unchecked") - E myE = (E) val; - return myE; - } - return Enum.valueOf(clazz, val.toString()); - } catch (IllegalArgumentException e) { - return defaultValue; - } catch (NullPointerException e) { - return defaultValue; - } - } - - /** - * Get the optional BigInteger value associated with an index. The - * defaultValue is returned if there is no value for the index, or if the - * value is not a number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public BigInteger optBigInteger(int index, BigInteger defaultValue) { - Object val = this.opt(index); - return JSONObject.objectToBigInteger(val, defaultValue); - } - - /** - * Get the optional BigDecimal value associated with an index. The - * defaultValue is returned if there is no value for the index, or if the - * value is not a number and cannot be converted to a number. If the value - * is float or double, the {@link BigDecimal#BigDecimal(double)} - * constructor will be used. See notes on the constructor for conversion - * issues that may arise. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { - Object val = this.opt(index); - return JSONObject.objectToBigDecimal(val, defaultValue); - } - - /** - * Get the optional JSONArray associated with an index. - * - * @param index - * subscript - * @return A JSONArray value, or null if the index has no value, or if the - * value is not a JSONArray. - */ - public JSONArray optJSONArray(int index) { - Object o = this.opt(index); - return o instanceof JSONArray ? (JSONArray) o : null; - } - - /** - * Get the optional JSONObject associated with an index. Null is returned if - * the key is not found, or null if the index has no value, or if the value - * is not a JSONObject. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A JSONObject value. - */ - public JSONObject optJSONObject(int index) { - Object o = this.opt(index); - return o instanceof JSONObject ? (JSONObject) o : null; - } - - /** - * Get the optional long value associated with an index. Zero is returned if - * there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public long optLong(int index) { - return this.optLong(index, 0); - } - - /** - * Get the optional long value associated with an index. The defaultValue is - * returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public long optLong(int index, long defaultValue) { - final Number val = this.optNumber(index, null); - if (val == null) { - return defaultValue; - } - return val.longValue(); - } - - /** - * Get an optional {@link Number} value associated with a key, or null - * if there is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method - * would be used in cases where type coercion of the number value is unwanted. - * - * @param index - * The index must be between 0 and length() - 1. - * @return An object which is the value. - */ - public Number optNumber(int index) { - return this.optNumber(index, null); - } - - /** - * Get an optional {@link Number} value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method - * would be used in cases where type coercion of the number value is unwanted. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public Number optNumber(int index, Number defaultValue) { - Object val = this.opt(index); - if (JSONObject.NULL.equals(val)) { - return defaultValue; - } - if (val instanceof Number){ - return (Number) val; - } - - if (val instanceof String) { - try { - return JSONObject.stringToNumber((String) val); - } catch (Exception e) { - return defaultValue; - } - } - return defaultValue; - } - - /** - * Get the optional string value associated with an index. It returns an - * empty string if there is no value at that index. If the value is not a - * string and is not null, then it is converted to a string. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A String value. - */ - public String optString(int index) { - return this.optString(index, ""); - } - - /** - * Get the optional string associated with an index. The defaultValue is - * returned if the key is not found. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return A String value. - */ - public String optString(int index, String defaultValue) { - Object object = this.opt(index); - return JSONObject.NULL.equals(object) ? defaultValue : object - .toString(); - } - - /** - * Append a boolean value. This increases the array's length by one. - * - * @param value - * A boolean value. - * @return this. - */ - public JSONArray put(boolean value) { - return this.put(value ? Boolean.TRUE : Boolean.FALSE); - } - - /** - * Put a value in the JSONArray, where the value will be a JSONArray which - * is produced from a Collection. - * - * @param value - * A Collection value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - */ - public JSONArray put(Collection value) { - return this.put(new JSONArray(value)); - } - - /** - * Append a double value. This increases the array's length by one. - * - * @param value - * A double value. - * @return this. - * @throws JSONException - * if the value is not finite. - */ - public JSONArray put(double value) throws JSONException { - return this.put(Double.valueOf(value)); - } - - /** - * Append a float value. This increases the array's length by one. - * - * @param value - * A float value. - * @return this. - * @throws JSONException - * if the value is not finite. - */ - public JSONArray put(float value) throws JSONException { - return this.put(Float.valueOf(value)); - } - - /** - * Append an int value. This increases the array's length by one. - * - * @param value - * An int value. - * @return this. - */ - public JSONArray put(int value) { - return this.put(Integer.valueOf(value)); - } - - /** - * Append an long value. This increases the array's length by one. - * - * @param value - * A long value. - * @return this. - */ - public JSONArray put(long value) { - return this.put(Long.valueOf(value)); - } - - /** - * Put a value in the JSONArray, where the value will be a JSONObject which - * is produced from a Map. - * - * @param value - * A Map value. - * @return this. - * @throws JSONException - * If a value in the map is non-finite number. - * @throws NullPointerException - * If a key in the map is null - */ - public JSONArray put(Map value) { - return this.put(new JSONObject(value)); - } - - /** - * Append an object value. This increases the array's length by one. - * - * @param value - * An object value. The value should be a Boolean, Double, - * Integer, JSONArray, JSONObject, Long, or String, or the - * JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the value is non-finite number. - */ - public JSONArray put(Object value) { - JSONObject.testValidity(value); - this.myArrayList.add(value); - return this; - } - - /** - * Put or replace a boolean value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * - * @param index - * The subscript. - * @param value - * A boolean value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, boolean value) throws JSONException { - return this.put(index, value ? Boolean.TRUE : Boolean.FALSE); - } - - /** - * Put a value in the JSONArray, where the value will be a JSONArray which - * is produced from a Collection. - * - * @param index - * The subscript. - * @param value - * A Collection value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is non-finite. - */ - public JSONArray put(int index, Collection value) throws JSONException { - return this.put(index, new JSONArray(value)); - } - - /** - * Put or replace a double value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A double value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is non-finite. - */ - public JSONArray put(int index, double value) throws JSONException { - return this.put(index, Double.valueOf(value)); - } - - /** - * Put or replace a float value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A float value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is non-finite. - */ - public JSONArray put(int index, float value) throws JSONException { - return this.put(index, Float.valueOf(value)); - } - - /** - * Put or replace an int value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * An int value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, int value) throws JSONException { - return this.put(index, Integer.valueOf(value)); - } - - /** - * Put or replace a long value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A long value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, long value) throws JSONException { - return this.put(index, Long.valueOf(value)); - } - - /** - * Put a value in the JSONArray, where the value will be a JSONObject that - * is produced from a Map. - * - * @param index - * The subscript. - * @param value - * The Map value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is an invalid - * number. - * @throws NullPointerException - * If a key in the map is null - */ - public JSONArray put(int index, Map value) throws JSONException { - this.put(index, new JSONObject(value)); - return this; - } - - /** - * Put or replace an object value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * - * @param index - * The subscript. - * @param value - * The value to put into the array. The value should be a - * Boolean, Double, Integer, JSONArray, JSONObject, Long, or - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the index is negative or if the value is an invalid - * number. - */ - public JSONArray put(int index, Object value) throws JSONException { - if (index < 0) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - if (index < this.length()) { - JSONObject.testValidity(value); - this.myArrayList.set(index, value); - return this; - } - if(index == this.length()){ - // simple append - return this.put(value); - } - // if we are inserting past the length, we want to grow the array all at once - // instead of incrementally. - this.myArrayList.ensureCapacity(index + 1); - while (index != this.length()) { - // we don't need to test validity of NULL objects - this.myArrayList.add(JSONObject.NULL); - } - return this.put(value); - } - - /** - * Put a collection's elements in to the JSONArray. - * - * @param collection - * A Collection. - * @return this. - */ - public JSONArray putAll(Collection collection) { - this.addAll(collection, false); - return this; - } - - /** - * Put an Iterable's elements in to the JSONArray. - * - * @param iter - * An Iterable. - * @return this. - */ - public JSONArray putAll(Iterable iter) { - this.addAll(iter, false); - return this; - } - - /** - * Put a JSONArray's elements in to the JSONArray. - * - * @param array - * A JSONArray. - * @return this. - */ - public JSONArray putAll(JSONArray array) { - // directly copy the elements from the source array to this one - // as all wrapping should have been done already in the source. - this.myArrayList.addAll(array.myArrayList); - return this; - } - - /** - * Put an array's elements in to the JSONArray. - * - * @param array - * Array. If the parameter passed is null, or not an array or Iterable, an - * exception will be thrown. - * @return this. - * - * @throws JSONException - * If not an array, JSONArray, Iterable or if an value is non-finite number. - * @throws NullPointerException - * Thrown if the array parameter is null. - */ - public JSONArray putAll(Object array) throws JSONException { - this.addAll(array, false); - return this; - } - - /** - * Creates a JSONPointer using an initialization string and tries to - * match it to an item within this JSONArray. For example, given a - * JSONArray initialized with this document: - *
-     * [
-     *     {"b":"c"}
-     * ]
-     * 
- * and this JSONPointer string: - *
-     * "/0/b"
-     * 
- * Then this method will return the String "c" - * A JSONPointerException may be thrown from code called by this method. - * - * @param jsonPointer string that can be used to create a JSONPointer - * @return the item matched by the JSONPointer, otherwise null - */ - public Object query(String jsonPointer) { - return query(new JSONPointer(jsonPointer)); - } - - /** - * Uses a user initialized JSONPointer and tries to - * match it to an item within this JSONArray. For example, given a - * JSONArray initialized with this document: - *
-     * [
-     *     {"b":"c"}
-     * ]
-     * 
- * and this JSONPointer: - *
-     * "/0/b"
-     * 
- * Then this method will return the String "c" - * A JSONPointerException may be thrown from code called by this method. - * - * @param jsonPointer string that can be used to create a JSONPointer - * @return the item matched by the JSONPointer, otherwise null - */ - public Object query(JSONPointer jsonPointer) { - return jsonPointer.queryFrom(this); - } - - /** - * Queries and returns a value from this object using {@code jsonPointer}, or - * returns null if the query fails due to a missing key. - * - * @param jsonPointer the string representation of the JSON pointer - * @return the queried value or {@code null} - * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax - */ - public Object optQuery(String jsonPointer) { - return optQuery(new JSONPointer(jsonPointer)); - } - - /** - * Queries and returns a value from this object using {@code jsonPointer}, or - * returns null if the query fails due to a missing key. - * - * @param jsonPointer The JSON pointer - * @return the queried value or {@code null} - * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax - */ - public Object optQuery(JSONPointer jsonPointer) { - try { - return jsonPointer.queryFrom(this); - } catch (JSONPointerException e) { - return null; - } - } - - /** - * Remove an index and close the hole. - * - * @param index - * The index of the element to be removed. - * @return The value that was associated with the index, or null if there - * was no value. - */ - public Object remove(int index) { - return index >= 0 && index < this.length() - ? this.myArrayList.remove(index) - : null; - } - - /** - * Determine if two JSONArrays are similar. - * They must contain similar sequences. - * - * @param other The other JSONArray - * @return true if they are equal - */ - public boolean similar(Object other) { - if (!(other instanceof JSONArray)) { - return false; - } - int len = this.length(); - if (len != ((JSONArray)other).length()) { - return false; - } - for (int i = 0; i < len; i += 1) { - Object valueThis = this.myArrayList.get(i); - Object valueOther = ((JSONArray)other).myArrayList.get(i); - if(valueThis == valueOther) { - continue; - } - if(valueThis == null) { - return false; - } - if (valueThis instanceof JSONObject) { - if (!((JSONObject)valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof JSONArray) { - if (!((JSONArray)valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof Number && valueOther instanceof Number) { - if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) { - return false; - } - } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { - if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { - return false; - } - } else if (!valueThis.equals(valueOther)) { - return false; - } - } - return true; - } - - /** - * Produce a JSONObject by combining a JSONArray of names with the values of - * this JSONArray. - * - * @param names - * A JSONArray containing a list of key strings. These will be - * paired with the values. - * @return A JSONObject, or null if there are no names or if this JSONArray - * has no values. - * @throws JSONException - * If any of the names are null. - */ - public JSONObject toJSONObject(JSONArray names) throws JSONException { - if (names == null || names.isEmpty() || this.isEmpty()) { - return null; - } - JSONObject jo = new JSONObject(names.length()); - for (int i = 0; i < names.length(); i += 1) { - jo.put(names.getString(i), this.opt(i)); - } - return jo; - } - - /** - * Make a JSON text of this JSONArray. For compactness, no unnecessary - * whitespace is added. If it is not possible to produce a syntactically - * correct JSON text then null will be returned instead. This could occur if - * the array contains an invalid number. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @return a printable, displayable, transmittable representation of the - * array. - */ - @Override - public String toString() { - try { - return this.toString(0); - } catch (Exception e) { - return null; - } - } - - /** - * Make a pretty-printed JSON text of this JSONArray. - * - *

If

 {@code indentFactor > 0}
and the {@link JSONArray} has only - * one element, then the array will be output on a single line: - *
{@code [1]}
- * - *

If an array has 2 or more elements, then it will be output across - * multiple lines:

{@code
-     * [
-     * 1,
-     * "value 2",
-     * 3
-     * ]
-     * }
- *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @return a printable, displayable, transmittable representation of the - * object, beginning with [ (left - * bracket) and ending with ] - *  (right bracket). - * @throws JSONException if a called function fails - */ - @SuppressWarnings("resource") - public String toString(int indentFactor) throws JSONException { - StringWriter sw = new StringWriter(); - synchronized (sw.getBuffer()) { - return this.write(sw, indentFactor, 0).toString(); - } - } - - /** - * Write the contents of the JSONArray as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @param writer the writer object - * @return The writer. - * @throws JSONException if a called function fails - */ - public Writer write(Writer writer) throws JSONException { - return this.write(writer, 0, 0); - } - - /** - * Write the contents of the JSONArray as JSON text to a writer. - * - *

If

{@code indentFactor > 0}
and the {@link JSONArray} has only - * one element, then the array will be output on a single line: - *
{@code [1]}
- * - *

If an array has 2 or more elements, then it will be output across - * multiple lines:

{@code
-     * [
-     * 1,
-     * "value 2",
-     * 3
-     * ]
-     * }
- *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @param writer - * Writes the serialized JSON - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @param indent - * The indentation of the top level. - * @return The writer. - * @throws JSONException if a called function fails or unable to write - */ - @SuppressWarnings("resource") - public Writer write(Writer writer, int indentFactor, int indent) - throws JSONException { - try { - boolean needsComma = false; - int length = this.length(); - writer.write('['); - - if (length == 1) { - try { - JSONObject.writeValue(writer, this.myArrayList.get(0), - indentFactor, indent); - } catch (Exception e) { - throw new JSONException("Unable to write JSONArray value at index: 0", e); - } - } else if (length != 0) { - final int newIndent = indent + indentFactor; - - for (int i = 0; i < length; i += 1) { - if (needsComma) { - writer.write(','); - } - if (indentFactor > 0) { - writer.write('\n'); - } - JSONObject.indent(writer, newIndent); - try { - JSONObject.writeValue(writer, this.myArrayList.get(i), - indentFactor, newIndent); - } catch (Exception e) { - throw new JSONException("Unable to write JSONArray value at index: " + i, e); - } - needsComma = true; - } - if (indentFactor > 0) { - writer.write('\n'); - } - JSONObject.indent(writer, indent); - } - writer.write(']'); - return writer; - } catch (IOException e) { - throw new JSONException(e); - } - } - - /** - * Returns a java.util.List containing all of the elements in this array. - * If an element in the array is a JSONArray or JSONObject it will also - * be converted to a List and a Map respectively. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @return a java.util.List containing the elements of this array - */ - public List toList() { - List results = new ArrayList(this.myArrayList.size()); - for (Object element : this.myArrayList) { - if (element == null || JSONObject.NULL.equals(element)) { - results.add(null); - } else if (element instanceof JSONArray) { - results.add(((JSONArray) element).toList()); - } else if (element instanceof JSONObject) { - results.add(((JSONObject) element).toMap()); - } else { - results.add(element); - } - } - return results; - } - - /** - * Check if JSONArray is empty. - * - * @return true if JSONArray is empty, otherwise false. - */ - public boolean isEmpty() { - return this.myArrayList.isEmpty(); - } - - /** - * Add a collection's elements to the JSONArray. - * - * @param collection - * A Collection. - * @param wrap - * {@code true} to call {@link JSONObject#wrap(Object)} for each item, - * {@code false} to add the items directly - * - */ - private void addAll(Collection collection, boolean wrap) { - this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size()); - if (wrap) { - for (Object o: collection){ - this.put(JSONObject.wrap(o)); - } - } else { - for (Object o: collection){ - this.put(o); - } - } - } - - /** - * Add an Iterable's elements to the JSONArray. - * - * @param iter - * An Iterable. - * @param wrap - * {@code true} to call {@link JSONObject#wrap(Object)} for each item, - * {@code false} to add the items directly - */ - private void addAll(Iterable iter, boolean wrap) { - if (wrap) { - for (Object o: iter){ - this.put(JSONObject.wrap(o)); - } - } else { - for (Object o: iter){ - this.put(o); - } - } - } - - /** - * Add an array's elements to the JSONArray. - * - * @param array - * Array. If the parameter passed is null, or not an array, - * JSONArray, Collection, or Iterable, an exception will be - * thrown. - * @param wrap - * {@code true} to call {@link JSONObject#wrap(Object)} for each item, - * {@code false} to add the items directly - * - * @throws JSONException - * If not an array or if an array value is non-finite number. - * @throws NullPointerException - * Thrown if the array parameter is null. - */ - private void addAll(Object array, boolean wrap) throws JSONException { - if (array.getClass().isArray()) { - int length = Array.getLength(array); - this.myArrayList.ensureCapacity(this.myArrayList.size() + length); - if (wrap) { - for (int i = 0; i < length; i += 1) { - this.put(JSONObject.wrap(Array.get(array, i))); - } - } else { - for (int i = 0; i < length; i += 1) { - this.put(Array.get(array, i)); - } - } - } else if (array instanceof JSONArray) { - // use the built in array list `addAll` as all object - // wrapping should have been completed in the original - // JSONArray - this.myArrayList.addAll(((JSONArray)array).myArrayList); - } else if (array instanceof Collection) { - this.addAll((Collection)array, wrap); - } else if (array instanceof Iterable) { - this.addAll((Iterable)array, wrap); - } else { - throw new JSONException( - "JSONArray initial value should be a string or collection or array."); - } - } - - /** - * Create a new JSONException in a common format for incorrect conversions. - * @param idx index of the item - * @param valueType the type of value being coerced to - * @param cause optional cause of the coercion failure - * @return JSONException that can be thrown. - */ - private static JSONException wrongValueFormatException( - int idx, - String valueType, - Object value, - Throwable cause) { - if(value == null) { - return new JSONException( - "JSONArray[" + idx + "] is not a " + valueType + " (null)." - , cause); - } - // don't try to toString collections or known object types that could be large. - if(value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { - return new JSONException( - "JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + ")." - , cause); - } - return new JSONException( - "JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ")." - , cause); - } - -} +package org.json; + +/* +Public Domain. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * A JSONArray is an ordered sequence of values. Its external text form is a + * string wrapped in square brackets with commas separating the values. The + * internal form is an object having get and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing bracket.
  • + *
  • The null value will be inserted when there is , + *  (comma) elision.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, and + * if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and + * if they are not the reserved words true, false, or + * null.
  • + *
+ * + * @author JSON.org + * @version 2016-08/15 + */ +public class JSONArray implements Iterable { + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private final ArrayList myArrayList; + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * + * @param x + * A JSONTokener + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + + char nextChar = x.nextClean(); + if (nextChar == 0) { + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar != ']') { + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case 0: + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + case ',': + nextChar = x.nextClean(); + if (nextChar == 0) { + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + } + + /** + * Construct a JSONArray from a source JSON text. + * + * @param source + * A string that begins with [ (left + * bracket) and ends with ] + *  (right bracket). + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONArray from a Collection. + * + * @param collection + * A Collection. + */ + public JSONArray(Collection collection) { + this(collection, 0, new JSONParserConfiguration()); + } + + /** + * Construct a JSONArray from a Collection. + * + * @param collection + * A Collection. + * @param jsonParserConfiguration + * Configuration object for the JSON parser + */ + public JSONArray(Collection collection, JSONParserConfiguration jsonParserConfiguration) { + this(collection, 0, jsonParserConfiguration); + } + + /** + * Construct a JSONArray from a collection with recursion depth. + * + * @param collection + * A Collection. + * @param recursionDepth + * Variable for tracking the count of nested object creations. + * @param jsonParserConfiguration + * Configuration object for the JSON parser + */ + JSONArray(Collection collection, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) { + throw new JSONException("JSONArray has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth()); + } + if (collection == null) { + this.myArrayList = new ArrayList(); + } else { + this.myArrayList = new ArrayList(collection.size()); + this.addAll(collection, true, recursionDepth, jsonParserConfiguration); + } + } + + /** + * Construct a JSONArray from an Iterable. This is a shallow copy. + * + * @param iter + * A Iterable collection. + */ + public JSONArray(Iterable iter) { + this(); + if (iter == null) { + return; + } + this.addAll(iter, true); + } + + /** + * Construct a JSONArray from another JSONArray. This is a shallow copy. + * + * @param array + * A array. + */ + public JSONArray(JSONArray array) { + if (array == null) { + this.myArrayList = new ArrayList(); + } else { + // shallow copy directly the internal array lists as any wrapping + // should have been done already in the original JSONArray + this.myArrayList = new ArrayList(array.myArrayList); + } + } + + /** + * Construct a JSONArray from an array. + * + * @param array + * Array. If the parameter passed is null, or not an array, an + * exception will be thrown. + * + * @throws JSONException + * If not an array or if an array value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (!array.getClass().isArray()) { + throw new JSONException( + "JSONArray initial value should be a string or collection or array."); + } + this.addAll(array, true, 0); + } + + /** + * Construct a JSONArray with the specified initial capacity. + * + * @param initialCapacity + * the initial capacity of the JSONArray. + * @throws JSONException + * If the initial capacity is negative. + */ + public JSONArray(int initialCapacity) throws JSONException { + if (initialCapacity < 0) { + throw new JSONException( + "JSONArray initial capacity cannot be negative."); + } + this.myArrayList = new ArrayList(initialCapacity); + } + + @Override + public Iterator iterator() { + return this.myArrayList.iterator(); + } + + /** + * Get the object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException + * If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with an index. The string values "true" + * and "false" are converted to boolean. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException + * If there is no value for the index or if the value is not + * convertible to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw wrongValueFormatException(index, "boolean", object, null); + } + + /** + * Get the double value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public double getDouble(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "double", object, e); + } + } + + /** + * Get the float value associated with a key. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public float getFloat(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "float", object, e); + } + } + + /** + * Get the Number value associated with a key. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public Number getNumber(int index) throws JSONException { + Object object = this.get(index); + try { + if (object instanceof Number) { + return (Number)object; + } + return JSONObject.stringToNumber(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "number", object, e); + } + } + + /** + * Get the enum value associated with an index. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @return The enum value at the index location + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an enum. + */ + public > E getEnum(Class clazz, int index) throws JSONException { + E val = optEnum(clazz, index); + if(val==null) { + // JSONException should really take a throwable argument. + // If it did, I would re-implement this with the Enum.valueOf + // method and place any thrown exception in the JSONException + throw wrongValueFormatException(index, "enum of type " + + JSONObject.quote(clazz.getSimpleName()), opt(index), null); + } + return val; + } + + /** + * Get the BigDecimal value associated with an index. If the value is float + * or double, the {@link BigDecimal#BigDecimal(double)} constructor + * will be used. See notes on the constructor for conversion issues that + * may arise. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a BigDecimal. + */ + public BigDecimal getBigDecimal (int index) throws JSONException { + Object object = this.get(index); + BigDecimal val = JSONObject.objectToBigDecimal(object, null); + if(val == null) { + throw wrongValueFormatException(index, "BigDecimal", object, null); + } + return val; + } + + /** + * Get the BigInteger value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a BigInteger. + */ + public BigInteger getBigInteger (int index) throws JSONException { + Object object = this.get(index); + BigInteger val = JSONObject.objectToBigInteger(object, null); + if(val == null) { + throw wrongValueFormatException(index, "BigInteger", object, null); + } + return val; + } + + /** + * Get the int value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value is not a number. + */ + public int getInt(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "int", object, e); + } + } + + /** + * Get the JSONArray associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException + * If there is no value for the index. or if the value is not a + * JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw wrongValueFormatException(index, "JSONArray", object, null); + } + + /** + * Get the JSONObject associated with an index. + * + * @param index + * subscript + * @return A JSONObject value. + * @throws JSONException + * If there is no value for the index or if the value is not a + * JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw wrongValueFormatException(index, "JSONObject", object, null); + } + + /** + * Get the long value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public long getLong(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "long", object, e); + } + } + + /** + * Get the string associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException + * If there is no string value for the index. + */ + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String) object; + } + throw wrongValueFormatException(index, "String", object, null); + } + + /** + * Determine if the value is null. + * + * @param index + * The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. Warning: + * This method assumes that the data structure is acyclical. + * + * @param separator + * A string that will be inserted between the elements. + * @return a string. + * @throws JSONException + * If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = this.length(); + if (len == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder( + JSONObject.valueToString(this.myArrayList.get(0))); + + for (int i = 1; i < len; i++) { + sb.append(separator) + .append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + /** + * Removes all of the elements from this JSONArray. + * The JSONArray will be empty after this call returns. + */ + public void clear() { + this.myArrayList.clear(); + } + + /** + * Get the optional object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. If not, null is returned. + * @return An object value, or null if there is no object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= this.length()) ? null : this.myArrayList + .get(index); + } + + /** + * Get the optional boolean value associated with an index. It returns false + * if there is no value at that index, or if the value is not Boolean.TRUE + * or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + /** + * Get the optional boolean value associated with an index. It returns the + * defaultValue if there is no value at that index or if it is not a Boolean + * or the String "true" or "false" (case insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional Boolean object associated with an index. It returns false + * if there is no value at that index, or if the value is not Boolean.TRUE + * or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + public Boolean optBooleanObject(int index) { + return this.optBooleanObject(index, false); + } + + /** + * Get the optional Boolean object associated with an index. It returns the + * defaultValue if there is no value at that index or if it is not a Boolean + * or the String "true" or "false" (case insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + public Boolean optBooleanObject(int index, Boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + final double doubleValue = val.doubleValue(); + // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { + // return defaultValue; + // } + return doubleValue; + } + + /** + * Get the optional Double object associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The object. + */ + public Double optDoubleObject(int index) { + return this.optDoubleObject(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default object. + * @return The object. + */ + public Double optDoubleObject(int index, Double defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + final Double doubleValue = val.doubleValue(); + // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { + // return defaultValue; + // } + return doubleValue; + } + + /** + * Get the optional float value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public float optFloat(int index) { + return this.optFloat(index, Float.NaN); + } + + /** + * Get the optional float value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public float optFloat(int index, float defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + final float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return floatValue; + // } + return floatValue; + } + + /** + * Get the optional Float object associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The object. + */ + public Float optFloatObject(int index) { + return this.optFloatObject(index, Float.NaN); + } + + /** + * Get the optional Float object associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default object. + * @return The object. + */ + public Float optFloatObject(int index, Float defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + final Float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return floatValue; + // } + return floatValue; + } + + /** + * Get the optional int value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return this.optInt(index, 0); + } + + /** + * Get the optional int value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get the optional Integer object associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The object. + */ + public Integer optIntegerObject(int index) { + return this.optIntegerObject(index, 0); + } + + /** + * Get the optional Integer object associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default object. + * @return The object. + */ + public Integer optIntegerObject(int index, Integer defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @return The enum value at the index location or null if not found + */ + public > E optEnum(Class clazz, int index) { + return this.optEnum(clazz, index, null); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default in case the value is not found + * @return The enum value at the index location or defaultValue if + * the value is not found or cannot be assigned to clazz + */ + public > E optEnum(Class clazz, int index, E defaultValue) { + try { + Object val = this.opt(index); + if (JSONObject.NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + // we just checked it! + @SuppressWarnings("unchecked") + E myE = (E) val; + return myE; + } + return Enum.valueOf(clazz, val.toString()); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (NullPointerException e) { + return defaultValue; + } + } + + /** + * Get the optional BigInteger value associated with an index. The + * defaultValue is returned if there is no value for the index, or if the + * value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public BigInteger optBigInteger(int index, BigInteger defaultValue) { + Object val = this.opt(index); + return JSONObject.objectToBigInteger(val, defaultValue); + } + + /** + * Get the optional BigDecimal value associated with an index. The + * defaultValue is returned if there is no value for the index, or if the + * value is not a number and cannot be converted to a number. If the value + * is float or double, the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { + Object val = this.opt(index); + return JSONObject.objectToBigDecimal(val, defaultValue); + } + + /** + * Get the optional JSONArray associated with an index. Null is returned if + * there is no value at that index or if the value is not a JSONArray. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + */ + public JSONArray optJSONArray(int index) { + return this.optJSONArray(index, null); + } + + /** + * Get the optional JSONArray associated with an index. The defaultValue is returned if + * there is no value at that index or if the value is not a JSONArray. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default. + * @return A JSONArray value. + */ + public JSONArray optJSONArray(int index, JSONArray defaultValue) { + Object object = this.opt(index); + return object instanceof JSONArray ? (JSONArray) object : defaultValue; + } + + /** + * Get the optional JSONObject associated with an index. Null is returned if + * there is no value at that index or if the value is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + return this.optJSONObject(index, null); + } + + /** + * Get the optional JSONObject associated with an index. The defaultValue is returned if + * there is no value at that index or if the value is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index, JSONObject defaultValue) { + Object object = this.opt(index); + return object instanceof JSONObject ? (JSONObject) object : defaultValue; + } + + /** + * Get the optional long value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return this.optLong(index, 0); + } + + /** + * Get the optional long value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + /** + * Get the optional Long object associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The object. + */ + public Long optLongObject(int index) { + return this.optLongObject(index, 0L); + } + + /** + * Get the optional Long object associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default object. + * @return The object. + */ + public Long optLongObject(int index, Long defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + /** + * Get an optional {@link Number} value associated with a key, or null + * if there is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object which is the value. + */ + public Number optNumber(int index) { + return this.optNumber(index, null); + } + + /** + * Get an optional {@link Number} value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Number optNumber(int index, Number defaultValue) { + Object val = this.opt(index); + if (JSONObject.NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number){ + return (Number) val; + } + + if (val instanceof String) { + try { + return JSONObject.stringToNumber((String) val); + } catch (Exception e) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value is not a + * string and is not null, then it is converted to a string. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return this.optString(index, ""); + } + + /** + * Get the optional string associated with an index. The defaultValue is + * returned if the key is not found. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object + .toString(); + } + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value + * A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + return this.put(value ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + */ + public JSONArray put(Collection value) { + return this.put(new JSONArray(value)); + } + + /** + * Append a double value. This increases the array's length by one. + * + * @param value + * A double value. + * @return this. + * @throws JSONException + * if the value is not finite. + */ + public JSONArray put(double value) throws JSONException { + return this.put(Double.valueOf(value)); + } + + /** + * Append a float value. This increases the array's length by one. + * + * @param value + * A float value. + * @return this. + * @throws JSONException + * if the value is not finite. + */ + public JSONArray put(float value) throws JSONException { + return this.put(Float.valueOf(value)); + } + + /** + * Append an int value. This increases the array's length by one. + * + * @param value + * An int value. + * @return this. + */ + public JSONArray put(int value) { + return this.put(Integer.valueOf(value)); + } + + /** + * Append an long value. This increases the array's length by one. + * + * @param value + * A long value. + * @return this. + */ + public JSONArray put(long value) { + return this.put(Long.valueOf(value)); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject which + * is produced from a Map. + * + * @param value + * A Map value. + * @return this. + * @throws JSONException + * If a value in the map is non-finite number. + * @throws NullPointerException + * If a key in the map is null + */ + public JSONArray put(Map value) { + return this.put(new JSONObject(value)); + } + + /** + * Append an object value. This increases the array's length by one. + * + * @param value + * An object value. The value should be a Boolean, Double, + * Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is non-finite number. + */ + public JSONArray put(Object value) { + JSONObject.testValidity(value); + this.myArrayList.add(value); + return this; + } + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A boolean value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + return this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param index + * The subscript. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is non-finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + return this.put(index, new JSONArray(value)); + } + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A double value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is non-finite. + */ + public JSONArray put(int index, double value) throws JSONException { + return this.put(index, Double.valueOf(value)); + } + + /** + * Put or replace a float value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A float value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is non-finite. + */ + public JSONArray put(int index, float value) throws JSONException { + return this.put(index, Float.valueOf(value)); + } + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * An int value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + return this.put(index, Integer.valueOf(value)); + } + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A long value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + return this.put(index, Long.valueOf(value)); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject that + * is produced from a Map. + * + * @param index + * The subscript. + * @param value + * The Map value. + * @return + * reference to self + * @throws JSONException + * If the index is negative or if the value is an invalid + * number. + * @throws NullPointerException + * If a key in the map is null + */ + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value, new JSONParserConfiguration())); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject that + * is produced from a Map. + * + * @param index + * The subscript + * @param value + * The Map value. + * @param jsonParserConfiguration + * Configuration object for the JSON parser + * @return reference to self + * @throws JSONException + * If the index is negative or if the value is an invalid + * number. + */ + public JSONArray put(int index, Map value, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + this.put(index, new JSONObject(value, jsonParserConfiguration)); + return this; + } + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the index is negative or if the value is an invalid + * number. + */ + public JSONArray put(int index, Object value) throws JSONException { + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + JSONObject.testValidity(value); + this.myArrayList.set(index, value); + return this; + } + if(index == this.length()){ + // simple append + return this.put(value); + } + // if we are inserting past the length, we want to grow the array all at once + // instead of incrementally. + this.myArrayList.ensureCapacity(index + 1); + while (index != this.length()) { + // we don't need to test validity of NULL objects + this.myArrayList.add(JSONObject.NULL); + } + return this.put(value); + } + + /** + * Put a collection's elements in to the JSONArray. + * + * @param collection + * A Collection. + * @return this. + */ + public JSONArray putAll(Collection collection) { + this.addAll(collection, false); + return this; + } + + /** + * Put an Iterable's elements in to the JSONArray. + * + * @param iter + * An Iterable. + * @return this. + */ + public JSONArray putAll(Iterable iter) { + this.addAll(iter, false); + return this; + } + + /** + * Put a JSONArray's elements in to the JSONArray. + * + * @param array + * A JSONArray. + * @return this. + */ + public JSONArray putAll(JSONArray array) { + // directly copy the elements from the source array to this one + // as all wrapping should have been done already in the source. + this.myArrayList.addAll(array.myArrayList); + return this; + } + + /** + * Put an array's elements in to the JSONArray. + * + * @param array + * Array. If the parameter passed is null, or not an array or Iterable, an + * exception will be thrown. + * @return this. + * + * @throws JSONException + * If not an array, JSONArray, Iterable or if an value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. + */ + public JSONArray putAll(Object array) throws JSONException { + this.addAll(array, false); + return this; + } + + /** + * Creates a JSONPointer using an initialization string and tries to + * match it to an item within this JSONArray. For example, given a + * JSONArray initialized with this document: + *
+     * [
+     *     {"b":"c"}
+     * ]
+     * 
+ * and this JSONPointer string: + *
+     * "/0/b"
+     * 
+ * Then this method will return the String "c" + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(String jsonPointer) { + return query(new JSONPointer(jsonPointer)); + } + + /** + * Uses a user initialized JSONPointer and tries to + * match it to an item within this JSONArray. For example, given a + * JSONArray initialized with this document: + *
+     * [
+     *     {"b":"c"}
+     * ]
+     * 
+ * and this JSONPointer: + *
+     * "/0/b"
+     * 
+ * Then this method will return the String "c" + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer the string representation of the JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(String jsonPointer) { + return optQuery(new JSONPointer(jsonPointer)); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer The JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } catch (JSONPointerException e) { + return null; + } + } + + /** + * Remove an index and close the hole. + * + * @param index + * The index of the element to be removed. + * @return The value that was associated with the index, or null if there + * was no value. + */ + public Object remove(int index) { + return index >= 0 && index < this.length() + ? this.myArrayList.remove(index) + : null; + } + + /** + * Determine if two JSONArrays are similar. + * They must contain similar sequences. + * + * @param other The other JSONArray + * @return true if they are equal + */ + public boolean similar(Object other) { + if (!(other instanceof JSONArray)) { + return false; + } + int len = this.length(); + if (len != ((JSONArray)other).length()) { + return false; + } + for (int i = 0; i < len; i += 1) { + Object valueThis = this.myArrayList.get(i); + Object valueOther = ((JSONArray)other).myArrayList.get(i); + if(valueThis == valueOther) { + continue; + } + if(valueThis == null) { + return false; + } + if (valueThis instanceof JSONObject) { + if (!((JSONObject)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof Number && valueOther instanceof Number) { + if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) { + return false; + } + } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { + if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } + + /** + * Produce a JSONObject by combining a JSONArray of names with the values of + * this JSONArray. + * + * @param names + * A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException + * If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.isEmpty() || this.isEmpty()) { + return null; + } + JSONObject jo = new JSONObject(names.length()); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary + * whitespace is added. If it is not possible to produce a syntactically + * correct JSON text then null will be returned instead. This could occur if + * the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @return a printable, displayable, transmittable representation of the + * array. + */ + @Override + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a pretty-printed JSON text of this JSONArray. + * + *

If

 {@code indentFactor > 0}
and the {@link JSONArray} has only + * one element, then the array will be output on a single line: + *
{@code [1]}
+ * + *

If an array has 2 or more elements, then it will be output across + * multiple lines:

{@code
+     * [
+     * 1,
+     * "value 2",
+     * 3
+     * ]
+     * }
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, transmittable representation of the + * object, beginning with [ (left + * bracket) and ending with ] + *  (right bracket). + * @throws JSONException if a called function fails + */ + @SuppressWarnings("resource") + public String toString(int indentFactor) throws JSONException { + StringWriter sw = new StringWriter(); + return this.write(sw, indentFactor, 0).toString(); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param writer the writer object + * @return The writer. + * @throws JSONException if a called function fails + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. + * + *

If

{@code indentFactor > 0}
and the {@link JSONArray} has only + * one element, then the array will be output on a single line: + *
{@code [1]}
+ * + *

If an array has 2 or more elements, then it will be output across + * multiple lines:

{@code
+     * [
+     * 1,
+     * "value 2",
+     * 3
+     * ]
+     * }
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param writer + * Writes the serialized JSON + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return The writer. + * @throws JSONException if a called function fails or unable to write + */ + @SuppressWarnings("resource") + public Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean needsComma = false; + int length = this.length(); + writer.write('['); + + if (length == 1) { + try { + JSONObject.writeValue(writer, this.myArrayList.get(0), + indentFactor, indent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONArray value at index: 0", e); + } + } else if (length != 0) { + final int newIndent = indent + indentFactor; + + for (int i = 0; i < length; i += 1) { + if (needsComma) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, newIndent); + try { + JSONObject.writeValue(writer, this.myArrayList.get(i), + indentFactor, newIndent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONArray value at index: " + i, e); + } + needsComma = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, indent); + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } + + /** + * Returns a java.util.List containing all of the elements in this array. + * If an element in the array is a JSONArray or JSONObject it will also + * be converted to a List and a Map respectively. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a java.util.List containing the elements of this array + */ + public List toList() { + List results = new ArrayList(this.myArrayList.size()); + for (Object element : this.myArrayList) { + if (element == null || JSONObject.NULL.equals(element)) { + results.add(null); + } else if (element instanceof JSONArray) { + results.add(((JSONArray) element).toList()); + } else if (element instanceof JSONObject) { + results.add(((JSONObject) element).toMap()); + } else { + results.add(element); + } + } + return results; + } + + /** + * Check if JSONArray is empty. + * + * @return true if JSONArray is empty, otherwise false. + */ + public boolean isEmpty() { + return this.myArrayList.isEmpty(); + } + + /** + * Add a collection's elements to the JSONArray. + * + * @param collection + * A Collection. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + * @param recursionDepth + * Variable for tracking the count of nested object creations. + */ + private void addAll(Collection collection, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size()); + if (wrap) { + for (Object o: collection){ + this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration)); + } + } else { + for (Object o: collection){ + this.put(o); + } + } + } + + /** + * Add an Iterable's elements to the JSONArray. + * + * @param iter + * An Iterable. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + */ + private void addAll(Iterable iter, boolean wrap) { + if (wrap) { + for (Object o: iter){ + this.put(JSONObject.wrap(o)); + } + } else { + for (Object o: iter){ + this.put(o); + } + } + } + + /** + * Add an array's elements to the JSONArray. + * + * @param array + * Array. If the parameter passed is null, or not an array, + * JSONArray, Collection, or Iterable, an exception will be + * thrown. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + * @throws JSONException + * If not an array or if an array value is non-finite number. + */ + private void addAll(Object array, boolean wrap) throws JSONException { + this.addAll(array, wrap, 0); + } + + /** + * Add an array's elements to the JSONArray. + * + * @param array + * Array. If the parameter passed is null, or not an array, + * JSONArray, Collection, or Iterable, an exception will be + * thrown. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + * @param recursionDepth + * Variable for tracking the count of nested object creations. + */ + private void addAll(Object array, boolean wrap, int recursionDepth) { + addAll(array, wrap, recursionDepth, new JSONParserConfiguration()); + } + /** + * Add an array's elements to the JSONArray. + *` + * @param array + * Array. If the parameter passed is null, or not an array, + * JSONArray, Collection, or Iterable, an exception will be + * thrown. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + * @param recursionDepth + * Variable for tracking the count of nested object creations. + * @param jsonParserConfiguration + * Variable to pass parser custom configuration for json parsing. + * @throws JSONException + * If not an array or if an array value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. + */ + private void addAll(Object array, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + if (array.getClass().isArray()) { + int length = Array.getLength(array); + this.myArrayList.ensureCapacity(this.myArrayList.size() + length); + if (wrap) { + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1, jsonParserConfiguration)); + } + } else { + for (int i = 0; i < length; i += 1) { + this.put(Array.get(array, i)); + } + } + } else if (array instanceof JSONArray) { + // use the built in array list `addAll` as all object + // wrapping should have been completed in the original + // JSONArray + this.myArrayList.addAll(((JSONArray)array).myArrayList); + } else if (array instanceof Collection) { + this.addAll((Collection)array, wrap, recursionDepth); + } else if (array instanceof Iterable) { + this.addAll((Iterable)array, wrap); + } else { + throw new JSONException( + "JSONArray initial value should be a string or collection or array."); + } + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * @param idx index of the item + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + int idx, + String valueType, + Object value, + Throwable cause) { + if(value == null) { + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + " (null)." + , cause); + } + // don't try to toString collections or known object types that could be large. + if(value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + ")." + , cause); + } + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ")." + , cause); + } + +} diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index b133a7f..8aae654 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -1,2718 +1,2983 @@ -package org.json; - -/* -Public Domain. -*/ - -import java.io.Closeable; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * A JSONObject is an unordered collection of name/value pairs. Its external - * form is a string wrapped in curly braces with colons between the names and - * values, and commas between the values and names. The internal form is an - * object having get and opt methods for accessing - * the values by name, and put methods for adding or replacing - * values by name. The values can be any of these types: Boolean, - * JSONArray, JSONObject, Number, - * String, or the JSONObject.NULL object. A - * JSONObject constructor can be used to convert an external form JSON text - * into an internal form whose values can be retrieved with the - * get and opt methods, or to convert values into a - * JSON text using the put and toString methods. A - * get method returns a value if one can be found, and throws an - * exception if one cannot be found. An opt method returns a - * default value instead of throwing an exception, and so is useful for - * obtaining optional values. - *

- * The generic get() and opt() methods return an - * object, which you can cast or query for type. There are also typed - * get and opt methods that do type checking and type - * coercion for you. The opt methods differ from the get methods in that they - * do not throw. Instead, they return a specified value, such as null. - *

- * The put methods add or replace values in an object. For - * example, - * - *

- * myString = new JSONObject()
- *         .put("JSON", "Hello, World!").toString();
- * 
- * - * produces the string {"JSON": "Hello, World"}. - *

- * The texts produced by the toString methods strictly conform to - * the JSON syntax rules. The constructors are more forgiving in the texts they - * will accept: - *

    - *
  • An extra , (comma) may appear just - * before the closing brace.
  • - *
  • Strings may be quoted with ' (single - * quote).
  • - *
  • Strings do not need to be quoted at all if they do not begin with a - * quote or single quote, and if they do not contain leading or trailing - * spaces, and if they do not contain any of these characters: - * { } [ ] / \ : , # and if they do not look like numbers and - * if they are not the reserved words true, false, - * or null.
  • - *
- * - * @author JSON.org - * @version 2016-08-15 - */ -public class JSONObject { - /** - * JSONObject.NULL is equivalent to the value that JavaScript calls null, - * whilst Java's null is equivalent to the value that JavaScript calls - * undefined. - */ - private static final class Null { - - /** - * There is only intended to be a single instance of the NULL object, - * so the clone method returns itself. - * - * @return NULL. - */ - @Override - protected final Object clone() { - return this; - } - - /** - * A Null object is equal to the null value and to itself. - * - * @param object - * An object to test for nullness. - * @return true if the object parameter is the JSONObject.NULL object or - * null. - */ - @Override - @SuppressWarnings("lgtm[java/unchecked-cast-in-equals]") - public boolean equals(Object object) { - return object == null || object == this; - } - /** - * A Null object is equal to the null value and to itself. - * - * @return always returns 0. - */ - @Override - public int hashCode() { - return 0; - } - - /** - * Get the "null" string value. - * - * @return The string "null". - */ - @Override - public String toString() { - return "null"; - } - } - - /** - * Regular Expression Pattern that matches JSON Numbers. This is primarily used for - * output to guarantee that we are always writing valid JSON. - */ - static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); - - /** - * The map where the JSONObject's properties are kept. - */ - private final Map map; - - public Class getMapType() { - return map.getClass(); - } - - /** - * It is sometimes more convenient and less ambiguous to have a - * NULL object than to use Java's null value. - * JSONObject.NULL.equals(null) returns true. - * JSONObject.NULL.toString() returns "null". - */ - public static final Object NULL = new Null(); - - /** - * Construct an empty JSONObject. - */ - public JSONObject() { - // HashMap is used on purpose to ensure that elements are unordered by - // the specification. - // JSON tends to be a portable transfer format to allows the container - // implementations to rearrange their items for a faster element - // retrieval based on associative access. - // Therefore, an implementation mustn't rely on the order of the item. - this.map = new HashMap(); - } - - /** - * Construct a JSONObject from a subset of another JSONObject. An array of - * strings is used to identify the keys that should be copied. Missing keys - * are ignored. - * - * @param jo - * A JSONObject. - * @param names - * An array of strings. - */ - public JSONObject(JSONObject jo, String ... names) { - this(names.length); - for (int i = 0; i < names.length; i += 1) { - try { - this.putOnce(names[i], jo.opt(names[i])); - } catch (Exception ignore) { - } - } - } - - /** - * Construct a JSONObject from a JSONTokener. - * - * @param x - * A JSONTokener object containing the source string. - * @throws JSONException - * If there is a syntax error in the source string or a - * duplicated key. - */ - public JSONObject(JSONTokener x) throws JSONException { - this(); - char c; - String key; - - if (x.nextClean() != '{') { - throw x.syntaxError("A JSONObject text must begin with '{'"); - } - for (;;) { - char prev = x.getPrevious(); - c = x.nextClean(); - switch (c) { - case 0: - throw x.syntaxError("A JSONObject text must end with '}'"); - case '}': - return; - case '{': - case '[': - if(prev=='{') { - throw x.syntaxError("A JSON Object can not directly nest another JSON Object or JSON Array."); - } - // fall through - default: - x.back(); - key = x.nextValue().toString(); - } - - // The key is followed by ':'. - - c = x.nextClean(); - if (c != ':') { - throw x.syntaxError("Expected a ':' after a key"); - } - - // Use syntaxError(..) to include error location - - if (key != null) { - // Check if key exists - /* - if (this.opt(key) != null) { - // key already exists - throw x.syntaxError("Duplicate key \"" + key + "\""); - } - */ - // Only add value if non-null - Object value = x.nextValue(); - if (value!=null) { - this.put(key, value); - } - } - - // Pairs are separated by ','. - - switch (x.nextClean()) { - case ';': - case ',': - if (x.nextClean() == '}') { - return; - } - x.back(); - break; - case '}': - return; - default: - throw x.syntaxError("Expected a ',' or '}'"); - } - } - } - - /** - * Construct a JSONObject from a Map. - * - * @param m - * A map object that can be used to initialize the contents of - * the JSONObject. - * @throws JSONException - * If a value in the map is non-finite number. - * @throws NullPointerException - * If a key in the map is null - */ - public JSONObject(Map m) { - if (m == null) { - this.map = new HashMap(); - } else { - this.map = new HashMap(m.size()); - for (final Entry e : m.entrySet()) { - if(e.getKey() == null) { - throw new NullPointerException("Null key."); - } - final Object value = e.getValue(); - if (value != null) { - this.map.put(String.valueOf(e.getKey()), wrap(value)); - } - } - } - } - - /** - * Construct a JSONObject from an Object using bean getters. It reflects on - * all of the public methods of the object. For each of the methods with no - * parameters and a name starting with "get" or - * "is" followed by an uppercase letter, the method is invoked, - * and a key and the value returned from the getter method are put into the - * new JSONObject. - *

- * The key is formed by removing the "get" or "is" - * prefix. If the second remaining character is not upper case, then the - * first character is converted to lower case. - *

- * Methods that are static, return void, - * have parameters, or are "bridge" methods, are ignored. - *

- * For example, if an object has a method named "getName", and - * if the result of calling object.getName() is - * "Larry Fine", then the JSONObject will contain - * "name": "Larry Fine". - *

- * The {@link JSONPropertyName} annotation can be used on a bean getter to - * override key name used in the JSONObject. For example, using the object - * above with the getName method, if we annotated it with: - *

-     * @JSONPropertyName("FullName")
-     * public String getName() { return this.name; }
-     * 
- * The resulting JSON object would contain "FullName": "Larry Fine" - *

- * Similarly, the {@link JSONPropertyName} annotation can be used on non- - * get and is methods. We can also override key - * name used in the JSONObject as seen below even though the field would normally - * be ignored: - *

-     * @JSONPropertyName("FullName")
-     * public String fullName() { return this.name; }
-     * 
- * The resulting JSON object would contain "FullName": "Larry Fine" - *

- * The {@link JSONPropertyIgnore} annotation can be used to force the bean property - * to not be serialized into JSON. If both {@link JSONPropertyIgnore} and - * {@link JSONPropertyName} are defined on the same method, a depth comparison is - * performed and the one closest to the concrete class being serialized is used. - * If both annotations are at the same level, then the {@link JSONPropertyIgnore} - * annotation takes precedent and the field is not serialized. - * For example, the following declaration would prevent the getName - * method from being serialized: - *

-     * @JSONPropertyName("FullName")
-     * @JSONPropertyIgnore
-     * public String getName() { return this.name; }
-     * 
- *

- * - * @param bean - * An object that has getter methods that should be used to make - * a JSONObject. - */ - public JSONObject(Object bean) { - this(); - this.populateMap(bean); - } - - private JSONObject(Object bean, Set objectsRecord) { - this(); - this.populateMap(bean, objectsRecord); - } - - /** - * Construct a JSONObject from an Object, using reflection to find the - * public members. The resulting JSONObject's keys will be the strings from - * the names array, and the values will be the field values associated with - * those keys in the object. If a key is not found or not visible, then it - * will not be copied into the new JSONObject. - * - * @param object - * An object that has fields that should be used to make a - * JSONObject. - * @param names - * An array of strings, the names of the fields to be obtained - * from the object. - */ - public JSONObject(Object object, String ... names) { - this(names.length); - Class c = object.getClass(); - for (int i = 0; i < names.length; i += 1) { - String name = names[i]; - try { - this.putOpt(name, c.getField(name).get(object)); - } catch (Exception ignore) { - } - } - } - - /** - * Construct a JSONObject from a source JSON text string. This is the most - * commonly used JSONObject constructor. - * - * @param source - * A string beginning with { (left - * brace) and ending with } - *  (right brace). - * @exception JSONException - * If there is a syntax error in the source string or a - * duplicated key. - */ - public JSONObject(String source) throws JSONException { - this(new JSONTokener(source)); - } - - /** - * Construct a JSONObject from a ResourceBundle. - * - * @param baseName - * The ResourceBundle base name. - * @param locale - * The Locale to load the ResourceBundle for. - * @throws JSONException - * If any JSONExceptions are detected. - */ - public JSONObject(String baseName, Locale locale) throws JSONException { - this(); - ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, - Thread.currentThread().getContextClassLoader()); - -// Iterate through the keys in the bundle. - - Enumeration keys = bundle.getKeys(); - while (keys.hasMoreElements()) { - Object key = keys.nextElement(); - if (key != null) { - -// Go through the path, ensuring that there is a nested JSONObject for each -// segment except the last. Add the value using the last segment's name into -// the deepest nested JSONObject. - - String[] path = ((String) key).split("\\."); - int last = path.length - 1; - JSONObject target = this; - for (int i = 0; i < last; i += 1) { - String segment = path[i]; - JSONObject nextTarget = target.optJSONObject(segment); - if (nextTarget == null) { - nextTarget = new JSONObject(); - target.put(segment, nextTarget); - } - target = nextTarget; - } - target.put(path[last], bundle.getString((String) key)); - } - } - } - - /** - * Constructor to specify an initial capacity of the internal map. Useful for library - * internal calls where we know, or at least can best guess, how big this JSONObject - * will be. - * - * @param initialCapacity initial capacity of the internal map. - */ - protected JSONObject(int initialCapacity){ - this.map = new HashMap(initialCapacity); - } - - /** - * Accumulate values under a key. It is similar to the put method except - * that if there is already an object stored under the key then a JSONArray - * is stored under the key to hold all of the accumulated values. If there - * is already a JSONArray, then the new value is appended to it. In - * contrast, the put method replaces the previous value. - * - * If only one value is accumulated that is not a JSONArray, then the result - * will be the same as using put. But if multiple values are accumulated, - * then the result will be like append. - * - * @param key - * A key string. - * @param value - * An object to be accumulated under the key. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject accumulate(String key, Object value) throws JSONException { - testValidity(value); - Object object = this.opt(key); - if (object == null) { - this.put(key, - value instanceof JSONArray ? new JSONArray().put(value) - : value); - } else if (object instanceof JSONArray) { - ((JSONArray) object).put(value); - } else { - this.put(key, new JSONArray().put(object).put(value)); - } - return this; - } - - /** - * Append values to the array under a key. If the key does not exist in the - * JSONObject, then the key is put in the JSONObject with its value being a - * JSONArray containing the value parameter. If the key was already - * associated with a JSONArray, then the value parameter is appended to it. - * - * @param key - * A key string. - * @param value - * An object to be accumulated under the key. - * @return this. - * @throws JSONException - * If the value is non-finite number or if the current value associated with - * the key is not a JSONArray. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject append(String key, Object value) throws JSONException { - testValidity(value); - Object object = this.opt(key); - if (object == null) { - this.put(key, new JSONArray().put(value)); - } else if (object instanceof JSONArray) { - this.put(key, ((JSONArray) object).put(value)); - } else { - throw wrongValueFormatException(key, "JSONArray", null, null); - } - return this; - } - - /** - * Produce a string from a double. The string "null" will be returned if the - * number is not finite. - * - * @param d - * A double. - * @return A String. - */ - public static String doubleToString(double d) { - if (Double.isInfinite(d) || Double.isNaN(d)) { - return "null"; - } - -// Shave off trailing zeros and decimal point, if possible. - - String string = Double.toString(d); - if (string.indexOf('.') > 0 && string.indexOf('e') < 0 - && string.indexOf('E') < 0) { - while (string.endsWith("0")) { - string = string.substring(0, string.length() - 1); - } - if (string.endsWith(".")) { - string = string.substring(0, string.length() - 1); - } - } - return string; - } - - /** - * Get the value object associated with a key. - * - * @param key - * A key string. - * @return The object associated with the key. - * @throws JSONException - * if the key is not found. - */ - public Object get(String key) throws JSONException { - if (key == null) { - throw new JSONException("Null key."); - } - Object object = this.opt(key); - if (object == null) { - throw new JSONException("JSONObject[" + quote(key) + "] not found."); - } - return object; - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param key - * A key string. - * @return The enum value associated with the key - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an enum. - */ - public > E getEnum(Class clazz, String key) throws JSONException { - E val = optEnum(clazz, key); - if(val==null) { - // JSONException should really take a throwable argument. - // If it did, I would re-implement this with the Enum.valueOf - // method and place any thrown exception in the JSONException - throw wrongValueFormatException(key, "enum of type " + quote(clazz.getSimpleName()), opt(key), null); - } - return val; - } - - /** - * Get the boolean value associated with a key. - * - * @param key - * A key string. - * @return The truth. - * @throws JSONException - * if the value is not a Boolean or the String "true" or - * "false". - */ - public boolean getBoolean(String key) throws JSONException { - Object object = this.get(key); - if (object.equals(Boolean.FALSE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("false"))) { - return false; - } else if (object.equals(Boolean.TRUE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("true"))) { - return true; - } - throw wrongValueFormatException(key, "Boolean", object, null); - } - - /** - * Get the BigInteger value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value cannot - * be converted to BigInteger. - */ - public BigInteger getBigInteger(String key) throws JSONException { - Object object = this.get(key); - BigInteger ret = objectToBigInteger(object, null); - if (ret != null) { - return ret; - } - throw wrongValueFormatException(key, "BigInteger", object, null); - } - - /** - * Get the BigDecimal value associated with a key. If the value is float or - * double, the {@link BigDecimal#BigDecimal(double)} constructor will - * be used. See notes on the constructor for conversion issues that may - * arise. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value - * cannot be converted to BigDecimal. - */ - public BigDecimal getBigDecimal(String key) throws JSONException { - Object object = this.get(key); - BigDecimal ret = objectToBigDecimal(object, null); - if (ret != null) { - return ret; - } - throw wrongValueFormatException(key, "BigDecimal", object, null); - } - - /** - * Get the double value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public double getDouble(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).doubleValue(); - } - try { - return Double.parseDouble(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "double", object, e); - } - } - - /** - * Get the float value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public float getFloat(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).floatValue(); - } - try { - return Float.parseFloat(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "float", object, e); - } - } - - /** - * Get the Number value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public Number getNumber(String key) throws JSONException { - Object object = this.get(key); - try { - if (object instanceof Number) { - return (Number)object; - } - return stringToNumber(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "number", object, e); - } - } - - /** - * Get the int value associated with a key. - * - * @param key - * A key string. - * @return The integer value. - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an integer. - */ - public int getInt(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).intValue(); - } - try { - return Integer.parseInt(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "int", object, e); - } - } - - /** - * Get the JSONArray value associated with a key. - * - * @param key - * A key string. - * @return A JSONArray which is the value. - * @throws JSONException - * if the key is not found or if the value is not a JSONArray. - */ - public JSONArray getJSONArray(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof JSONArray) { - return (JSONArray) object; - } - throw wrongValueFormatException(key, "JSONArray", object, null); - } - - /** - * Get the JSONObject value associated with a key. - * - * @param key - * A key string. - * @return A JSONObject which is the value. - * @throws JSONException - * if the key is not found or if the value is not a JSONObject. - */ - public JSONObject getJSONObject(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof JSONObject) { - return (JSONObject) object; - } - throw wrongValueFormatException(key, "JSONObject", object, null); - } - - /** - * Get the long value associated with a key. - * - * @param key - * A key string. - * @return The long value. - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to a long. - */ - public long getLong(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).longValue(); - } - try { - return Long.parseLong(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "long", object, e); - } - } - - /** - * Get an array of field names from a JSONObject. - * - * @param jo - * JSON object - * @return An array of field names, or null if there are no names. - */ - public static String[] getNames(JSONObject jo) { - if (jo.isEmpty()) { - return null; - } - return jo.keySet().toArray(new String[jo.length()]); - } - - /** - * Get an array of public field names from an Object. - * - * @param object - * object to read - * @return An array of field names, or null if there are no names. - */ - public static String[] getNames(Object object) { - if (object == null) { - return null; - } - Class klass = object.getClass(); - Field[] fields = klass.getFields(); - int length = fields.length; - if (length == 0) { - return null; - } - String[] names = new String[length]; - for (int i = 0; i < length; i += 1) { - names[i] = fields[i].getName(); - } - return names; - } - - /** - * Get the string associated with a key. - * - * @param key - * A key string. - * @return A string which is the value. - * @throws JSONException - * if there is no string value for the key. - */ - public String getString(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof String) { - return (String) object; - } - throw wrongValueFormatException(key, "string", object, null); - } - - /** - * Determine if the JSONObject contains a specific key. - * - * @param key - * A key string. - * @return true if the key exists in the JSONObject. - */ - public boolean has(String key) { - return this.map.containsKey(key); - } - - /** - * Increment a property of a JSONObject. If there is no such property, - * create one with a value of 1 (Integer). If there is such a property, and if it is - * an Integer, Long, Double, Float, BigInteger, or BigDecimal then add one to it. - * No overflow bounds checking is performed, so callers should initialize the key - * prior to this call with an appropriate type that can handle the maximum expected - * value. - * - * @param key - * A key string. - * @return this. - * @throws JSONException - * If there is already a property with this name that is not an - * Integer, Long, Double, or Float. - */ - public JSONObject increment(String key) throws JSONException { - Object value = this.opt(key); - if (value == null) { - this.put(key, 1); - } else if (value instanceof Integer) { - this.put(key, ((Integer) value).intValue() + 1); - } else if (value instanceof Long) { - this.put(key, ((Long) value).longValue() + 1L); - } else if (value instanceof BigInteger) { - this.put(key, ((BigInteger)value).add(BigInteger.ONE)); - } else if (value instanceof Float) { - this.put(key, ((Float) value).floatValue() + 1.0f); - } else if (value instanceof Double) { - this.put(key, ((Double) value).doubleValue() + 1.0d); - } else if (value instanceof BigDecimal) { - this.put(key, ((BigDecimal)value).add(BigDecimal.ONE)); - } else { - throw new JSONException("Unable to increment [" + quote(key) + "]."); - } - return this; - } - - /** - * Determine if the value associated with the key is null or if there is no - * value. - * - * @param key - * A key string. - * @return true if there is no value associated with the key or if the value - * is the JSONObject.NULL object. - */ - public boolean isNull(String key) { - return JSONObject.NULL.equals(this.opt(key)); - } - - /** - * Get an enumeration of the keys of the JSONObject. Modifying this key Set will also - * modify the JSONObject. Use with caution. - * - * @see Set#iterator() - * - * @return An iterator of the keys. - */ - public Iterator keys() { - return this.keySet().iterator(); - } - - /** - * Get a set of keys of the JSONObject. Modifying this key Set will also modify the - * JSONObject. Use with caution. - * - * @see Map#keySet() - * - * @return A keySet. - */ - public Set keySet() { - return this.map.keySet(); - } - - /** - * Get a set of entries of the JSONObject. These are raw values and may not - * match what is returned by the JSONObject get* and opt* functions. Modifying - * the returned EntrySet or the Entry objects contained therein will modify the - * backing JSONObject. This does not return a clone or a read-only view. - * - * Use with caution. - * - * @see Map#entrySet() - * - * @return An Entry Set - */ - protected Set> entrySet() { - return this.map.entrySet(); - } - - /** - * Get the number of keys stored in the JSONObject. - * - * @return The number of keys in the JSONObject. - */ - public int length() { - return this.map.size(); - } - - /** - * Removes all of the elements from this JSONObject. - * The JSONObject will be empty after this call returns. - */ - public void clear() { - this.map.clear(); - } - - /** - * Check if JSONObject is empty. - * - * @return true if JSONObject is empty, otherwise false. - */ - public boolean isEmpty() { - return this.map.isEmpty(); - } - - /** - * Produce a JSONArray containing the names of the elements of this - * JSONObject. - * - * @return A JSONArray containing the key strings, or null if the JSONObject - * is empty. - */ - public JSONArray names() { - if(this.map.isEmpty()) { - return null; - } - return new JSONArray(this.map.keySet()); - } - - /** - * Produce a string from a Number. - * - * @param number - * A Number - * @return A String. - * @throws JSONException - * If n is a non-finite number. - */ - public static String numberToString(Number number) throws JSONException { - if (number == null) { - throw new JSONException("Null pointer"); - } - testValidity(number); - - // Shave off trailing zeros and decimal point, if possible. - - String string = number.toString(); - if (string.indexOf('.') > 0 && string.indexOf('e') < 0 - && string.indexOf('E') < 0) { - while (string.endsWith("0")) { - string = string.substring(0, string.length() - 1); - } - if (string.endsWith(".")) { - string = string.substring(0, string.length() - 1); - } - } - return string; - } - - /** - * Get an optional value associated with a key. - * - * @param key - * A key string. - * @return An object which is the value, or null if there is no value. - */ - public Object opt(String key) { - return key == null ? null : this.map.get(key); - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param key - * A key string. - * @return The enum value associated with the key or null if not found - */ - public > E optEnum(Class clazz, String key) { - return this.optEnum(clazz, key, null); - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param key - * A key string. - * @param defaultValue - * The default in case the value is not found - * @return The enum value associated with the key or defaultValue - * if the value is not found or cannot be assigned to clazz - */ - public > E optEnum(Class clazz, String key, E defaultValue) { - try { - Object val = this.opt(key); - if (NULL.equals(val)) { - return defaultValue; - } - if (clazz.isAssignableFrom(val.getClass())) { - // we just checked it! - @SuppressWarnings("unchecked") - E myE = (E) val; - return myE; - } - return Enum.valueOf(clazz, val.toString()); - } catch (IllegalArgumentException e) { - return defaultValue; - } catch (NullPointerException e) { - return defaultValue; - } - } - - /** - * Get an optional boolean associated with a key. It returns false if there - * is no such key, or if the value is not Boolean.TRUE or the String "true". - * - * @param key - * A key string. - * @return The truth. - */ - public boolean optBoolean(String key) { - return this.optBoolean(key, false); - } - - /** - * Get an optional boolean associated with a key. It returns the - * defaultValue if there is no such key, or if it is not a Boolean or the - * String "true" or "false" (case insensitive). - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return The truth. - */ - public boolean optBoolean(String key, boolean defaultValue) { - Object val = this.opt(key); - if (NULL.equals(val)) { - return defaultValue; - } - if (val instanceof Boolean){ - return ((Boolean) val).booleanValue(); - } - try { - // we'll use the get anyway because it does string conversion. - return this.getBoolean(key); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get an optional BigDecimal associated with a key, or the defaultValue if - * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. If the value - * is float or double, then the {@link BigDecimal#BigDecimal(double)} - * constructor will be used. See notes on the constructor for conversion - * issues that may arise. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { - Object val = this.opt(key); - return objectToBigDecimal(val, defaultValue); - } - - /** - * @param val value to convert - * @param defaultValue default value to return is the conversion doesn't work or is null. - * @return BigDecimal conversion of the original value, or the defaultValue if unable - * to convert. - */ - static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { - return objectToBigDecimal(val, defaultValue, true); - } - - /** - * @param val value to convert - * @param defaultValue default value to return is the conversion doesn't work or is null. - * @param exact When true, then {@link Double} and {@link Float} values will be converted exactly. - * When false, they will be converted to {@link String} values before converting to {@link BigDecimal}. - * @return BigDecimal conversion of the original value, or the defaultValue if unable - * to convert. - */ - static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue, boolean exact) { - if (NULL.equals(val)) { - return defaultValue; - } - if (val instanceof BigDecimal){ - return (BigDecimal) val; - } - if (val instanceof BigInteger){ - return new BigDecimal((BigInteger) val); - } - if (val instanceof Double || val instanceof Float){ - if (!numberIsFinite((Number)val)) { - return defaultValue; - } - if (exact) { - return new BigDecimal(((Number)val).doubleValue()); - } - // use the string constructor so that we maintain "nice" values for doubles and floats - // the double constructor will translate doubles to "exact" values instead of the likely - // intended representation - return new BigDecimal(val.toString()); - } - if (val instanceof Long || val instanceof Integer - || val instanceof Short || val instanceof Byte){ - return new BigDecimal(((Number) val).longValue()); - } - // don't check if it's a string in case of unchecked Number subclasses - try { - return new BigDecimal(val.toString()); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get an optional BigInteger associated with a key, or the defaultValue if - * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public BigInteger optBigInteger(String key, BigInteger defaultValue) { - Object val = this.opt(key); - return objectToBigInteger(val, defaultValue); - } - - /** - * @param val value to convert - * @param defaultValue default value to return is the conversion doesn't work or is null. - * @return BigInteger conversion of the original value, or the defaultValue if unable - * to convert. - */ - static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { - if (NULL.equals(val)) { - return defaultValue; - } - if (val instanceof BigInteger){ - return (BigInteger) val; - } - if (val instanceof BigDecimal){ - return ((BigDecimal) val).toBigInteger(); - } - if (val instanceof Double || val instanceof Float){ - if (!numberIsFinite((Number)val)) { - return defaultValue; - } - return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); - } - if (val instanceof Long || val instanceof Integer - || val instanceof Short || val instanceof Byte){ - return BigInteger.valueOf(((Number) val).longValue()); - } - // don't check if it's a string in case of unchecked Number subclasses - try { - // the other opt functions handle implicit conversions, i.e. - // jo.put("double",1.1d); - // jo.optInt("double"); -- will return 1, not an error - // this conversion to BigDecimal then to BigInteger is to maintain - // that type cast support that may truncate the decimal. - final String valStr = val.toString(); - if(isDecimalNotation(valStr)) { - return new BigDecimal(valStr).toBigInteger(); - } - return new BigInteger(valStr); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get an optional double associated with a key, or NaN if there is no such - * key or if its value is not a number. If the value is a string, an attempt - * will be made to evaluate it as a number. - * - * @param key - * A string which is the key. - * @return An object which is the value. - */ - public double optDouble(String key) { - return this.optDouble(key, Double.NaN); - } - - /** - * Get an optional double associated with a key, or the defaultValue if - * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public double optDouble(String key, double defaultValue) { - Number val = this.optNumber(key); - if (val == null) { - return defaultValue; - } - final double doubleValue = val.doubleValue(); - // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { - // return defaultValue; - // } - return doubleValue; - } - - /** - * Get the optional double value associated with an index. NaN is returned - * if there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param key - * A key string. - * @return The value. - */ - public float optFloat(String key) { - return this.optFloat(key, Float.NaN); - } - - /** - * Get the optional double value associated with an index. The defaultValue - * is returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param key - * A key string. - * @param defaultValue - * The default value. - * @return The value. - */ - public float optFloat(String key, float defaultValue) { - Number val = this.optNumber(key); - if (val == null) { - return defaultValue; - } - final float floatValue = val.floatValue(); - // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { - // return defaultValue; - // } - return floatValue; - } - - /** - * Get an optional int value associated with a key, or zero if there is no - * such key or if the value is not a number. If the value is a string, an - * attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @return An object which is the value. - */ - public int optInt(String key) { - return this.optInt(key, 0); - } - - /** - * Get an optional int value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public int optInt(String key, int defaultValue) { - final Number val = this.optNumber(key, null); - if (val == null) { - return defaultValue; - } - return val.intValue(); - } - - /** - * Get an optional JSONArray associated with a key. It returns null if there - * is no such key, or if its value is not a JSONArray. - * - * @param key - * A key string. - * @return A JSONArray which is the value. - */ - public JSONArray optJSONArray(String key) { - Object o = this.opt(key); - return o instanceof JSONArray ? (JSONArray) o : null; - } - - /** - * Get an optional JSONObject associated with a key. It returns null if - * there is no such key, or if its value is not a JSONObject. - * - * @param key - * A key string. - * @return A JSONObject which is the value. - */ - public JSONObject optJSONObject(String key) { return this.optJSONObject(key, null); } - - /** - * Get an optional JSONObject associated with a key, or the default if there - * is no such key or if the value is not a JSONObject. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An JSONObject which is the value. - */ - public JSONObject optJSONObject(String key, JSONObject defaultValue) { - Object object = this.opt(key); - return object instanceof JSONObject ? (JSONObject) object : defaultValue; - } - - /** - * Get an optional long value associated with a key, or zero if there is no - * such key or if the value is not a number. If the value is a string, an - * attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @return An object which is the value. - */ - public long optLong(String key) { - return this.optLong(key, 0); - } - - /** - * Get an optional long value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public long optLong(String key, long defaultValue) { - final Number val = this.optNumber(key, null); - if (val == null) { - return defaultValue; - } - - return val.longValue(); - } - - /** - * Get an optional {@link Number} value associated with a key, or null - * if there is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method - * would be used in cases where type coercion of the number value is unwanted. - * - * @param key - * A key string. - * @return An object which is the value. - */ - public Number optNumber(String key) { - return this.optNumber(key, null); - } - - /** - * Get an optional {@link Number} value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number. This method - * would be used in cases where type coercion of the number value is unwanted. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public Number optNumber(String key, Number defaultValue) { - Object val = this.opt(key); - if (NULL.equals(val)) { - return defaultValue; - } - if (val instanceof Number){ - return (Number) val; - } - - try { - return stringToNumber(val.toString()); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get an optional string associated with a key. It returns an empty string - * if there is no such key. If the value is not a string and is not null, - * then it is converted to a string. - * - * @param key - * A key string. - * @return A string which is the value. - */ - public String optString(String key) { - return this.optString(key, ""); - } - - /** - * Get an optional string associated with a key. It returns the defaultValue - * if there is no such key. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return A string which is the value. - */ - public String optString(String key, String defaultValue) { - Object object = this.opt(key); - return NULL.equals(object) ? defaultValue : object.toString(); - } - - /** - * Populates the internal map of the JSONObject with the bean properties. The - * bean can not be recursive. - * - * @see JSONObject#JSONObject(Object) - * - * @param bean - * the bean - */ - private void populateMap(Object bean) { - populateMap(bean, Collections.newSetFromMap(new IdentityHashMap())); - } - - private void populateMap(Object bean, Set objectsRecord) { - Class klass = bean.getClass(); - - // If klass is a System class then set includeSuperClass to false. - - boolean includeSuperClass = klass.getClassLoader() != null; - - Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); - for (final Method method : methods) { - final int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers) - && !Modifier.isStatic(modifiers) - && method.getParameterTypes().length == 0 - && !method.isBridge() - && method.getReturnType() != Void.TYPE - && isValidMethodName(method.getName())) { - final String key = getKeyNameFromMethod(method); - if (key != null && !key.isEmpty()) { - try { - final Object result = method.invoke(bean); - if (result != null) { - // check cyclic dependency and throw error if needed - // the wrap and populateMap combination method is - // itself DFS recursive - if (objectsRecord.contains(result)) { - throw recursivelyDefinedObjectException(key); - } - - objectsRecord.add(result); - - this.map.put(key, wrap(result, objectsRecord)); - - objectsRecord.remove(result); - - // we don't use the result anywhere outside of wrap - // if it's a resource we should be sure to close it - // after calling toString - if (result instanceof Closeable) { - try { - ((Closeable) result).close(); - } catch (IOException ignore) { - } - } - } - } catch (IllegalAccessException ignore) { - } catch (IllegalArgumentException ignore) { - } catch (InvocationTargetException ignore) { - } - } - } - } - } - - private static boolean isValidMethodName(String name) { - return !"getClass".equals(name) && !"getDeclaringClass".equals(name); - } - - private static String getKeyNameFromMethod(Method method) { - final int ignoreDepth = getAnnotationDepth(method, JSONPropertyIgnore.class); - if (ignoreDepth > 0) { - final int forcedNameDepth = getAnnotationDepth(method, JSONPropertyName.class); - if (forcedNameDepth < 0 || ignoreDepth <= forcedNameDepth) { - // the hierarchy asked to ignore, and the nearest name override - // was higher or non-existent - return null; - } - } - JSONPropertyName annotation = getAnnotation(method, JSONPropertyName.class); - if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) { - return annotation.value(); - } - String key; - final String name = method.getName(); - if (name.startsWith("get") && name.length() > 3) { - key = name.substring(3); - } else if (name.startsWith("is") && name.length() > 2) { - key = name.substring(2); - } else { - return null; - } - // if the first letter in the key is not uppercase, then skip. - // This is to maintain backwards compatibility before PR406 - // (https://github.com/stleary/JSON-java/pull/406/) - if (key.length() == 0 || Character.isLowerCase(key.charAt(0))) { - return null; - } - if (key.length() == 1) { - key = key.toLowerCase(Locale.ROOT); - } else if (!Character.isUpperCase(key.charAt(1))) { - key = key.substring(0, 1).toLowerCase(Locale.ROOT) + key.substring(1); - } - return key; - } - - /** - * Searches the class hierarchy to see if the method or it's super - * implementations and interfaces has the annotation. - * - * @param - * type of the annotation - * - * @param m - * method to check - * @param annotationClass - * annotation to look for - * @return the {@link Annotation} if the annotation exists on the current method - * or one of its super class definitions - */ - private static A getAnnotation(final Method m, final Class annotationClass) { - // if we have invalid data the result is null - if (m == null || annotationClass == null) { - return null; - } - - if (m.isAnnotationPresent(annotationClass)) { - return m.getAnnotation(annotationClass); - } - - // if we've already reached the Object class, return null; - Class c = m.getDeclaringClass(); - if (c.getSuperclass() == null) { - return null; - } - - // check directly implemented interfaces for the method being checked - for (Class i : c.getInterfaces()) { - try { - Method im = i.getMethod(m.getName(), m.getParameterTypes()); - return getAnnotation(im, annotationClass); - } catch (final SecurityException ex) { - continue; - } catch (final NoSuchMethodException ex) { - continue; - } - } - - try { - return getAnnotation( - c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), - annotationClass); - } catch (final SecurityException ex) { - return null; - } catch (final NoSuchMethodException ex) { - return null; - } - } - - /** - * Searches the class hierarchy to see if the method or it's super - * implementations and interfaces has the annotation. Returns the depth of the - * annotation in the hierarchy. - * - * @param m - * method to check - * @param annotationClass - * annotation to look for - * @return Depth of the annotation or -1 if the annotation is not on the method. - */ - private static int getAnnotationDepth(final Method m, final Class annotationClass) { - // if we have invalid data the result is -1 - if (m == null || annotationClass == null) { - return -1; - } - - if (m.isAnnotationPresent(annotationClass)) { - return 1; - } - - // if we've already reached the Object class, return -1; - Class c = m.getDeclaringClass(); - if (c.getSuperclass() == null) { - return -1; - } - - // check directly implemented interfaces for the method being checked - for (Class i : c.getInterfaces()) { - try { - Method im = i.getMethod(m.getName(), m.getParameterTypes()); - int d = getAnnotationDepth(im, annotationClass); - if (d > 0) { - // since the annotation was on the interface, add 1 - return d + 1; - } - } catch (final SecurityException ex) { - continue; - } catch (final NoSuchMethodException ex) { - continue; - } - } - - try { - int d = getAnnotationDepth( - c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), - annotationClass); - if (d > 0) { - // since the annotation was on the superclass, add 1 - return d + 1; - } - return -1; - } catch (final SecurityException ex) { - return -1; - } catch (final NoSuchMethodException ex) { - return -1; - } - } - - /** - * Put a key/boolean pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A boolean which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, boolean value) throws JSONException { - return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); - } - - /** - * Put a key/value pair in the JSONObject, where the value will be a - * JSONArray which is produced from a Collection. - * - * @param key - * A key string. - * @param value - * A Collection value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, Collection value) throws JSONException { - return this.put(key, new JSONArray(value)); - } - - /** - * Put a key/double pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A double which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, double value) throws JSONException { - return this.put(key, Double.valueOf(value)); - } - - /** - * Put a key/float pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A float which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, float value) throws JSONException { - return this.put(key, Float.valueOf(value)); - } - - /** - * Put a key/int pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * An int which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, int value) throws JSONException { - return this.put(key, Integer.valueOf(value)); - } - - /** - * Put a key/long pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A long which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, long value) throws JSONException { - return this.put(key, Long.valueOf(value)); - } - - /** - * Put a key/value pair in the JSONObject, where the value will be a - * JSONObject which is produced from a Map. - * - * @param key - * A key string. - * @param value - * A Map value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, Map value) throws JSONException { - return this.put(key, new JSONObject(value)); - } - - /** - * Put a key/value pair in the JSONObject. If the value is null, then the - * key will be removed from the JSONObject if it is present. - * - * @param key - * A key string. - * @param value - * An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, Object value) throws JSONException { - if (key == null) { - throw new NullPointerException("Null key."); - } - if (value != null) { - testValidity(value); - this.map.put(key, value); - } else { - this.remove(key); - } - return this; - } - - /** - * Put a key/value pair in the JSONObject, but only if the key and the value - * are both non-null, and only if there is not already a member with that - * name. - * - * @param key - * key to insert into - * @param value - * value to insert - * @return this. - * @throws JSONException - * if the key is a duplicate - */ - public JSONObject putOnce(String key, Object value) throws JSONException { - if (key != null && value != null) { - if (this.opt(key) != null) { - throw new JSONException("Duplicate key \"" + key + "\""); - } - return this.put(key, value); - } - return this; - } - - /** - * Put a key/value pair in the JSONObject, but only if the key and the value - * are both non-null. - * - * @param key - * A key string. - * @param value - * An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the value is a non-finite number. - */ - public JSONObject putOpt(String key, Object value) throws JSONException { - if (key != null && value != null) { - return this.put(key, value); - } - return this; - } - - /** - * Creates a JSONPointer using an initialization string and tries to - * match it to an item within this JSONObject. For example, given a - * JSONObject initialized with this document: - *
-     * {
-     *     "a":{"b":"c"}
-     * }
-     * 
- * and this JSONPointer string: - *
-     * "/a/b"
-     * 
- * Then this method will return the String "c". - * A JSONPointerException may be thrown from code called by this method. - * - * @param jsonPointer string that can be used to create a JSONPointer - * @return the item matched by the JSONPointer, otherwise null - */ - public Object query(String jsonPointer) { - return query(new JSONPointer(jsonPointer)); - } - /** - * Uses a user initialized JSONPointer and tries to - * match it to an item within this JSONObject. For example, given a - * JSONObject initialized with this document: - *
-     * {
-     *     "a":{"b":"c"}
-     * }
-     * 
- * and this JSONPointer: - *
-     * "/a/b"
-     * 
- * Then this method will return the String "c". - * A JSONPointerException may be thrown from code called by this method. - * - * @param jsonPointer string that can be used to create a JSONPointer - * @return the item matched by the JSONPointer, otherwise null - */ - public Object query(JSONPointer jsonPointer) { - return jsonPointer.queryFrom(this); - } - - /** - * Queries and returns a value from this object using {@code jsonPointer}, or - * returns null if the query fails due to a missing key. - * - * @param jsonPointer the string representation of the JSON pointer - * @return the queried value or {@code null} - * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax - */ - public Object optQuery(String jsonPointer) { - return optQuery(new JSONPointer(jsonPointer)); - } - - /** - * Queries and returns a value from this object using {@code jsonPointer}, or - * returns null if the query fails due to a missing key. - * - * @param jsonPointer The JSON pointer - * @return the queried value or {@code null} - * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax - */ - public Object optQuery(JSONPointer jsonPointer) { - try { - return jsonPointer.queryFrom(this); - } catch (JSONPointerException e) { - return null; - } - } - - /** - * Produce a string in double quotes with backslash sequences in all the - * right places. A backslash will be inserted within </, producing - * <\/, allowing JSON text to be delivered in HTML. In JSON text, a - * string cannot contain a control character or an unescaped quote or - * backslash. - * - * @param string - * A String - * @return A String correctly formatted for insertion in a JSON text. - */ - @SuppressWarnings("resource") - public static String quote(String string) { - StringWriter sw = new StringWriter(); - synchronized (sw.getBuffer()) { - try { - return quote(string, sw).toString(); - } catch (IOException ignored) { - // will never happen - we are writing to a string writer - return ""; - } - } - } - - public static Writer quote(String string, Writer w) throws IOException { - if (string == null || string.isEmpty()) { - w.write("\"\""); - return w; - } - - char b; - char c = 0; - String hhhh; - int i; - int len = string.length(); - - w.write('"'); - for (i = 0; i < len; i += 1) { - b = c; - c = string.charAt(i); - switch (c) { - case '\\': - case '"': - w.write('\\'); - w.write(c); - break; - case '/': - if (b == '<') { - w.write('\\'); - } - w.write(c); - break; - case '\b': - w.write("\\b"); - break; - case '\t': - w.write("\\t"); - break; - case '\n': - w.write("\\n"); - break; - case '\f': - w.write("\\f"); - break; - case '\r': - w.write("\\r"); - break; - default: - if (c < ' ' || (c >= '\u0080' && c < '\u00a0') - || (c >= '\u2000' && c < '\u2100')) { - w.write("\\u"); - hhhh = Integer.toHexString(c); - w.write("0000", 0, 4 - hhhh.length()); - w.write(hhhh); - } else { - w.write(c); - } - } - } - w.write('"'); - return w; - } - - /** - * Remove a name and its value, if present. - * - * @param key - * The name to be removed. - * @return The value that was associated with the name, or null if there was - * no value. - */ - public Object remove(String key) { - return this.map.remove(key); - } - - /** - * Determine if two JSONObjects are similar. - * They must contain the same set of names which must be associated with - * similar values. - * - * @param other The other JSONObject - * @return true if they are equal - */ - public boolean similar(Object other) { - try { - if (!(other instanceof JSONObject)) { - return false; - } - if (!this.keySet().equals(((JSONObject)other).keySet())) { - return false; - } - for (final Entry entry : this.entrySet()) { - String name = entry.getKey(); - Object valueThis = entry.getValue(); - Object valueOther = ((JSONObject)other).get(name); - if(valueThis == valueOther) { - continue; - } - if(valueThis == null) { - return false; - } - if (valueThis instanceof JSONObject) { - if (!((JSONObject)valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof JSONArray) { - if (!((JSONArray)valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof Number && valueOther instanceof Number) { - if (!isNumberSimilar((Number)valueThis, (Number)valueOther)) { - return false; - } - } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { - if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { - return false; - } - } else if (!valueThis.equals(valueOther)) { - return false; - } - } - return true; - } catch (Throwable exception) { - return false; - } - } - - /** - * Compares two numbers to see if they are similar. - * - * If either of the numbers are Double or Float instances, then they are checked to have - * a finite value. If either value is not finite (NaN or ±infinity), then this - * function will always return false. If both numbers are finite, they are first checked - * to be the same type and implement {@link Comparable}. If they do, then the actual - * {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't - * implement Comparable, then they are converted to {@link BigDecimal}s. Finally the - * BigDecimal values are compared using {@link BigDecimal#compareTo(BigDecimal)}. - * - * @param l the Left value to compare. Can not be null. - * @param r the right value to compare. Can not be null. - * @return true if the numbers are similar, false otherwise. - */ - static boolean isNumberSimilar(Number l, Number r) { - if (!numberIsFinite(l) || !numberIsFinite(r)) { - // non-finite numbers are never similar - return false; - } - - // if the classes are the same and implement Comparable - // then use the built in compare first. - if(l.getClass().equals(r.getClass()) && l instanceof Comparable) { - @SuppressWarnings({ "rawtypes", "unchecked" }) - int compareTo = ((Comparable)l).compareTo(r); - return compareTo==0; - } - - // BigDecimal should be able to handle all of our number types that we support through - // documentation. Convert to BigDecimal first, then use the Compare method to - // decide equality. - final BigDecimal lBigDecimal = objectToBigDecimal(l, null, false); - final BigDecimal rBigDecimal = objectToBigDecimal(r, null, false); - if (lBigDecimal == null || rBigDecimal == null) { - return false; - } - return lBigDecimal.compareTo(rBigDecimal) == 0; - } - - private static boolean numberIsFinite(Number n) { - if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) { - return false; - } else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) { - return false; - } - return true; - } - - /** - * Tests if the value should be tried as a decimal. It makes no test if there are actual digits. - * - * @param val value to test - * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise. - */ - protected static boolean isDecimalNotation(final String val) { - return val.indexOf('.') > -1 || val.indexOf('e') > -1 - || val.indexOf('E') > -1 || "-0".equals(val); - } - - /** - * Converts a string to a number using the narrowest possible type. Possible - * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer. - * When a Double is returned, it should always be a valid Double and not NaN or +-infinity. - * - * @param val value to convert - * @return Number representation of the value. - * @throws NumberFormatException thrown if the value is not a valid number. A public - * caller should catch this and wrap it in a {@link JSONException} if applicable. - */ - protected static Number stringToNumber(final String val) throws NumberFormatException { - char initial = val.charAt(0); - if ((initial >= '0' && initial <= '9') || initial == '-') { - // decimal representation - if (isDecimalNotation(val)) { - // Use a BigDecimal all the time so we keep the original - // representation. BigDecimal doesn't support -0.0, ensure we - // keep that by forcing a decimal. - try { - BigDecimal bd = new BigDecimal(val); - if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) { - return Double.valueOf(-0.0); - } - return bd; - } catch (NumberFormatException retryAsDouble) { - // this is to support "Hex Floats" like this: 0x1.0P-1074 - try { - Double d = Double.valueOf(val); - if(d.isNaN() || d.isInfinite()) { - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - return d; - } catch (NumberFormatException ignore) { - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - } - } - // block items like 00 01 etc. Java number parsers treat these as Octal. - if(initial == '0' && val.length() > 1) { - char at1 = val.charAt(1); - if(at1 >= '0' && at1 <= '9') { - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - } else if (initial == '-' && val.length() > 2) { - char at1 = val.charAt(1); - char at2 = val.charAt(2); - if(at1 == '0' && at2 >= '0' && at2 <= '9') { - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - } - // integer representation. - // This will narrow any values to the smallest reasonable Object representation - // (Integer, Long, or BigInteger) - - // BigInteger down conversion: We use a similar bitLength compare as - // BigInteger#intValueExact uses. Increases GC, but objects hold - // only what they need. i.e. Less runtime overhead if the value is - // long lived. - BigInteger bi = new BigInteger(val); - if(bi.bitLength() <= 31){ - return Integer.valueOf(bi.intValue()); - } - if(bi.bitLength() <= 63){ - return Long.valueOf(bi.longValue()); - } - return bi; - } - throw new NumberFormatException("val ["+val+"] is not a valid number."); - } - - /** - * Try to convert a string into a number, boolean, or null. If the string - * can't be converted, return the string. - * - * @param string - * A String. can not be null. - * @return A simple JSON value. - * @throws NullPointerException - * Thrown if the string is null. - */ - // Changes to this method must be copied to the corresponding method in - // the XML class to keep full support for Android - public static Object stringToValue(String string) { - if ("".equals(string)) { - return string; - } - - // check JSON key words true/false/null - if ("true".equalsIgnoreCase(string)) { - return Boolean.TRUE; - } - if ("false".equalsIgnoreCase(string)) { - return Boolean.FALSE; - } - if ("null".equalsIgnoreCase(string)) { - return JSONObject.NULL; - } - - /* - * If it might be a number, try converting it. If a number cannot be - * produced, then the value will just be a string. - */ - - char initial = string.charAt(0); - if ((initial >= '0' && initial <= '9') || initial == '-') { - try { - return stringToNumber(string); - } catch (Exception ignore) { - } - } - return string; - } - - /** - * Throw an exception if the object is a NaN or infinite number. - * - * @param o - * The object to test. - * @throws JSONException - * If o is a non-finite number. - */ - public static void testValidity(Object o) throws JSONException { - if (o instanceof Number && !numberIsFinite((Number) o)) { - throw new JSONException("JSON does not allow non-finite numbers."); - } - } - - /** - * Produce a JSONArray containing the values of the members of this - * JSONObject. - * - * @param names - * A JSONArray containing a list of key strings. This determines - * the sequence of the values in the result. - * @return A JSONArray of values. - * @throws JSONException - * If any of the values are non-finite numbers. - */ - public JSONArray toJSONArray(JSONArray names) throws JSONException { - if (names == null || names.isEmpty()) { - return null; - } - JSONArray ja = new JSONArray(); - for (int i = 0; i < names.length(); i += 1) { - ja.put(this.opt(names.getString(i))); - } - return ja; - } - - /** - * Make a JSON text of this JSONObject. For compactness, no whitespace is - * added. If this would not result in a syntactically correct JSON text, - * then null will be returned instead. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @return a printable, displayable, portable, transmittable representation - * of the object, beginning with { (left - * brace) and ending with } (right - * brace). - */ - @Override - public String toString() { - try { - return this.toString(0); - } catch (Exception e) { - return null; - } - } - - /** - * Make a pretty-printed JSON text of this JSONObject. - * - *

If

{@code indentFactor > 0}
and the {@link JSONObject} - * has only one key, then the object will be output on a single line: - *
{@code {"key": 1}}
- * - *

If an object has 2 or more keys, then it will be output across - * multiple lines:

{@code {
-     *  "key1": 1,
-     *  "key2": "value 2",
-     *  "key3": 3
-     * }}
- *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @return a printable, displayable, portable, transmittable representation - * of the object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the object contains an invalid number. - */ - @SuppressWarnings("resource") - public String toString(int indentFactor) throws JSONException { - StringWriter w = new StringWriter(); - synchronized (w.getBuffer()) { - return this.write(w, indentFactor, 0).toString(); - } - } - - /** - * Make a JSON text of an Object value. If the object has an - * value.toJSONString() method, then that method will be used to produce the - * JSON text. The method is required to produce a strictly conforming text. - * If the object does not contain a toJSONString method (which is the most - * common case), then a text will be produced by other means. If the value - * is an array or Collection, then a JSONArray will be made from it and its - * toJSONString method will be called. If the value is a MAP, then a - * JSONObject will be made from it and its toJSONString method will be - * called. Otherwise, the value's toString method will be called, and the - * result will be quoted. - * - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @param value - * The value to be serialized. - * @return a printable, displayable, transmittable representation of the - * object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the value is or contains an invalid number. - */ - public static String valueToString(Object value) throws JSONException { - // moves the implementation to JSONWriter as: - // 1. It makes more sense to be part of the writer class - // 2. For Android support this method is not available. By implementing it in the Writer - // Android users can use the writer with the built in Android JSONObject implementation. - return JSONWriter.valueToString(value); - } - - /** - * Wrap an object, if necessary. If the object is null, return the NULL - * object. If it is an array or collection, wrap it in a JSONArray. If it is - * a map, wrap it in a JSONObject. If it is a standard property (Double, - * String, et al) then it is already wrapped. Otherwise, if it comes from - * one of the java packages, turn it into a string. And if it doesn't, try - * to wrap it in a JSONObject. If the wrapping fails, then null is returned. - * - * @param object - * The object to wrap - * @return The wrapped value - */ - public static Object wrap(Object object) { - return wrap(object, null); - } - - private static Object wrap(Object object, Set objectsRecord) { - try { - if (NULL.equals(object)) { - return NULL; - } - if (object instanceof JSONObject || object instanceof JSONArray - || NULL.equals(object) || object instanceof JSONString - || object instanceof Byte || object instanceof Character - || object instanceof Short || object instanceof Integer - || object instanceof Long || object instanceof Boolean - || object instanceof Float || object instanceof Double - || object instanceof String || object instanceof BigInteger - || object instanceof BigDecimal || object instanceof Enum) { - return object; - } - - if (object instanceof Collection) { - Collection coll = (Collection) object; - return new JSONArray(coll); - } - if (object.getClass().isArray()) { - return new JSONArray(object); - } - if (object instanceof Map) { - Map map = (Map) object; - return new JSONObject(map); - } - Package objectPackage = object.getClass().getPackage(); - String objectPackageName = objectPackage != null ? objectPackage - .getName() : ""; - if (objectPackageName.startsWith("java.") - || objectPackageName.startsWith("javax.") - || object.getClass().getClassLoader() == null) { - return object.toString(); - } - if (objectsRecord != null) { - return new JSONObject(object, objectsRecord); - } - return new JSONObject(object); - } - catch (JSONException exception) { - throw exception; - } catch (Exception exception) { - return null; - } - } - - /** - * Write the contents of the JSONObject as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @param writer the writer object - * @return The writer. - * @throws JSONException if a called function has an error - */ - public Writer write(Writer writer) throws JSONException { - return this.write(writer, 0, 0); - } - - @SuppressWarnings("resource") - static final Writer writeValue(Writer writer, Object value, - int indentFactor, int indent) throws JSONException, IOException { - if (value == null || value.equals(null)) { - writer.write("null"); - } else if (value instanceof JSONString) { - Object o; - try { - o = ((JSONString) value).toJSONString(); - } catch (Exception e) { - throw new JSONException(e); - } - writer.write(o != null ? o.toString() : quote(value.toString())); - } else if (value instanceof Number) { - // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary - final String numberAsString = numberToString((Number) value); - if(NUMBER_PATTERN.matcher(numberAsString).matches()) { - writer.write(numberAsString); - } else { - // The Number value is not a valid JSON number. - // Instead we will quote it as a string - quote(numberAsString, writer); - } - } else if (value instanceof Boolean) { - writer.write(value.toString()); - } else if (value instanceof Enum) { - writer.write(quote(((Enum)value).name())); - } else if (value instanceof JSONObject) { - ((JSONObject) value).write(writer, indentFactor, indent); - } else if (value instanceof JSONArray) { - ((JSONArray) value).write(writer, indentFactor, indent); - } else if (value instanceof Map) { - Map map = (Map) value; - new JSONObject(map).write(writer, indentFactor, indent); - } else if (value instanceof Collection) { - Collection coll = (Collection) value; - new JSONArray(coll).write(writer, indentFactor, indent); - } else if (value.getClass().isArray()) { - new JSONArray(value).write(writer, indentFactor, indent); - } else { - quote(value.toString(), writer); - } - return writer; - } - - static final void indent(Writer writer, int indent) throws IOException { - for (int i = 0; i < indent; i += 1) { - writer.write(' '); - } - } - - /** - * Write the contents of the JSONObject as JSON text to a writer. - * - *

If

{@code indentFactor > 0}
and the {@link JSONObject} - * has only one key, then the object will be output on a single line: - *
{@code {"key": 1}}
- * - *

If an object has 2 or more keys, then it will be output across - * multiple lines:

{@code {
-     *  "key1": 1,
-     *  "key2": "value 2",
-     *  "key3": 3
-     * }}
- *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @param writer - * Writes the serialized JSON - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @param indent - * The indentation of the top level. - * @return The writer. - * @throws JSONException if a called function has an error or a write error - * occurs - */ - @SuppressWarnings("resource") - public Writer write(Writer writer, int indentFactor, int indent) - throws JSONException { - try { - boolean needsComma = false; - final int length = this.length(); - writer.write('{'); - - if (length == 1) { - final Entry entry = this.entrySet().iterator().next(); - final String key = entry.getKey(); - writer.write(quote(key)); - writer.write(':'); - if (indentFactor > 0) { - writer.write(' '); - } - try{ - writeValue(writer, entry.getValue(), indentFactor, indent); - } catch (Exception e) { - throw new JSONException("Unable to write JSONObject value for key: " + key, e); - } - } else if (length != 0) { - final int newIndent = indent + indentFactor; - for (final Entry entry : this.entrySet()) { - if (needsComma) { - writer.write(','); - } - if (indentFactor > 0) { - writer.write('\n'); - } - indent(writer, newIndent); - final String key = entry.getKey(); - writer.write(quote(key)); - writer.write(':'); - if (indentFactor > 0) { - writer.write(' '); - } - try { - writeValue(writer, entry.getValue(), indentFactor, newIndent); - } catch (Exception e) { - throw new JSONException("Unable to write JSONObject value for key: " + key, e); - } - needsComma = true; - } - if (indentFactor > 0) { - writer.write('\n'); - } - indent(writer, indent); - } - writer.write('}'); - return writer; - } catch (IOException exception) { - throw new JSONException(exception); - } - } - - /** - * Returns a java.util.Map containing all of the entries in this object. - * If an entry in the object is a JSONArray or JSONObject it will also - * be converted. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @return a java.util.Map containing the entries of this object - */ - public Map toMap() { - Map results = new HashMap(); - for (Entry entry : this.entrySet()) { - Object value; - if (entry.getValue() == null || NULL.equals(entry.getValue())) { - value = null; - } else if (entry.getValue() instanceof JSONObject) { - value = ((JSONObject) entry.getValue()).toMap(); - } else if (entry.getValue() instanceof JSONArray) { - value = ((JSONArray) entry.getValue()).toList(); - } else { - value = entry.getValue(); - } - results.put(entry.getKey(), value); - } - return results; - } - - /** - * Create a new JSONException in a common format for incorrect conversions. - * @param key name of the key - * @param valueType the type of value being coerced to - * @param cause optional cause of the coercion failure - * @return JSONException that can be thrown. - */ - private static JSONException wrongValueFormatException( - String key, - String valueType, - Object value, - Throwable cause) { - if(value == null) { - - return new JSONException( - "JSONObject[" + quote(key) + "] is not a " + valueType + " (null)." - , cause); - } - // don't try to toString collections or known object types that could be large. - if(value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { - return new JSONException( - "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value.getClass() + ")." - , cause); - } - return new JSONException( - "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ")." - , cause); - } - - /** - * Create a new JSONException in a common format for recursive object definition. - * @param key name of the key - * @return JSONException that can be thrown. - */ - private static JSONException recursivelyDefinedObjectException(String key) { - return new JSONException( - "JavaBean object contains recursively defined member variable of key " + quote(key) - ); - } -} +package org.json; + +/* +Public Domain. +*/ + +import java.io.Closeable; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * A JSONObject is an unordered collection of name/value pairs. Its external + * form is a string wrapped in curly braces with colons between the names and + * values, and commas between the values and names. The internal form is an + * object having get and opt methods for accessing + * the values by name, and put methods for adding or replacing + * values by name. The values can be any of these types: Boolean, + * JSONArray, JSONObject, Number, + * String, or the JSONObject.NULL object. A + * JSONObject constructor can be used to convert an external form JSON text + * into an internal form whose values can be retrieved with the + * get and opt methods, or to convert values into a + * JSON text using the put and toString methods. A + * get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object, which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. The opt methods differ from the get methods in that they + * do not throw. Instead, they return a specified value, such as null. + *

+ * The put methods add or replace values in an object. For + * example, + * + *

+ * myString = new JSONObject()
+ *         .put("JSON", "Hello, World!").toString();
+ * 
+ * + * produces the string {"JSON": "Hello, World"}. + *

+ * The texts produced by the toString methods strictly conform to + * the JSON syntax rules. The constructors are more forgiving in the texts they + * will accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing brace.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a + * quote or single quote, and if they do not contain leading or trailing + * spaces, and if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and + * if they are not the reserved words true, false, + * or null.
  • + *
+ * + * @author JSON.org + * @version 2016-08-15 + */ +public class JSONObject { + /** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. + */ + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * + * @return NULL. + */ + @Override + protected final Object clone() { + return this; + } + + /** + * A Null object is equal to the null value and to itself. + * + * @param object + * An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object or + * null. + */ + @Override + @SuppressWarnings("lgtm[java/unchecked-cast-in-equals]") + public boolean equals(Object object) { + return object == null || object == this; + } + /** + * A Null object is equal to the null value and to itself. + * + * @return always returns 0. + */ + @Override + public int hashCode() { + return 0; + } + + /** + * Get the "null" string value. + * + * @return The string "null". + */ + @Override + public String toString() { + return "null"; + } + } + + /** + * Regular Expression Pattern that matches JSON Numbers. This is primarily used for + * output to guarantee that we are always writing valid JSON. + */ + static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); + + /** + * The map where the JSONObject's properties are kept. + */ + private final Map map; + + /** + * Retrieves the type of the underlying Map in this class. + * + * @return The class object representing the type of the underlying Map. + */ + public Class getMapType() { + return map.getClass(); + } + + /** + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". + */ + public static final Object NULL = new Null(); + + /** + * Construct an empty JSONObject. + */ + public JSONObject() { + // HashMap is used on purpose to ensure that elements are unordered by + // the specification. + // JSON tends to be a portable transfer format to allows the container + // implementations to rearrange their items for a faster element + // retrieval based on associative access. + // Therefore, an implementation mustn't rely on the order of the item. + this.map = new HashMap(); + } + + /** + * Construct a JSONObject from a subset of another JSONObject. An array of + * strings is used to identify the keys that should be copied. Missing keys + * are ignored. + * + * @param jo + * A JSONObject. + * @param names + * An array of strings. + */ + public JSONObject(JSONObject jo, String ... names) { + this(names.length); + for (int i = 0; i < names.length; i += 1) { + try { + this.putOnce(names[i], jo.opt(names[i])); + } catch (Exception ignore) { + } + } + } + + /** + * Construct a JSONObject from a JSONTokener. + * + * @param x + * A JSONTokener object containing the source string. + * @throws JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ + public JSONObject(JSONTokener x) throws JSONException { + this(); + char c; + String key; + + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + c = x.nextClean(); + switch (c) { + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + key = x.nextSimpleValue(c).toString(); + } + + // The key is followed by ':'. + + c = x.nextClean(); + if (c != ':') { + throw x.syntaxError("Expected a ':' after a key"); + } + + // Use syntaxError(..) to include error location + + if (key != null) { + // Check if key exists + if (this.opt(key) != null) { + // key already exists + throw x.syntaxError("Duplicate key \"" + key + "\""); + } + // Only add value if non-null + Object value = x.nextValue(); + if (value!=null) { + this.put(key, value); + } + } + + // Pairs are separated by ','. + + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + if (x.end()) { + throw x.syntaxError("A JSONObject text must end with '}'"); + } + x.back(); + break; + case '}': + return; + default: + throw x.syntaxError("Expected a ',' or '}'"); + } + } + } + + /** + * Construct a JSONObject from a Map. + * + * @param m + * A map object that can be used to initialize the contents of + * the JSONObject. + * @throws JSONException + * If a value in the map is non-finite number. + * @throws NullPointerException + * If a key in the map is null + */ + public JSONObject(Map m) { + this(m, 0, new JSONParserConfiguration()); + } + + /** + * Construct a JSONObject from a Map with custom json parse configurations. + * + * @param m + * A map object that can be used to initialize the contents of + * the JSONObject. + * @param jsonParserConfiguration + * Variable to pass parser custom configuration for json parsing. + */ + public JSONObject(Map m, JSONParserConfiguration jsonParserConfiguration) { + this(m, 0, jsonParserConfiguration); + } + + /** + * Construct a JSONObject from a map with recursion depth. + * + */ + private JSONObject(Map m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) { + throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth()); + } + if (m == null) { + this.map = new HashMap(); + } else { + this.map = new HashMap(m.size()); + for (final Entry e : m.entrySet()) { + if(e.getKey() == null) { + throw new NullPointerException("Null key."); + } + final Object value = e.getValue(); + if (value != null) { + testValidity(value); + this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration)); + } + } + } + } + + /** + * Construct a JSONObject from an Object using bean getters. It reflects on + * all of the public methods of the object. For each of the methods with no + * parameters and a name starting with "get" or + * "is" followed by an uppercase letter, the method is invoked, + * and a key and the value returned from the getter method are put into the + * new JSONObject. + *

+ * The key is formed by removing the "get" or "is" + * prefix. If the second remaining character is not upper case, then the + * first character is converted to lower case. + *

+ * Methods that are static, return void, + * have parameters, or are "bridge" methods, are ignored. + *

+ * For example, if an object has a method named "getName", and + * if the result of calling object.getName() is + * "Larry Fine", then the JSONObject will contain + * "name": "Larry Fine". + *

+ * The {@link JSONPropertyName} annotation can be used on a bean getter to + * override key name used in the JSONObject. For example, using the object + * above with the getName method, if we annotated it with: + *

+     * @JSONPropertyName("FullName")
+     * public String getName() { return this.name; }
+     * 
+ * The resulting JSON object would contain "FullName": "Larry Fine" + *

+ * Similarly, the {@link JSONPropertyName} annotation can be used on non- + * get and is methods. We can also override key + * name used in the JSONObject as seen below even though the field would normally + * be ignored: + *

+     * @JSONPropertyName("FullName")
+     * public String fullName() { return this.name; }
+     * 
+ * The resulting JSON object would contain "FullName": "Larry Fine" + *

+ * The {@link JSONPropertyIgnore} annotation can be used to force the bean property + * to not be serialized into JSON. If both {@link JSONPropertyIgnore} and + * {@link JSONPropertyName} are defined on the same method, a depth comparison is + * performed and the one closest to the concrete class being serialized is used. + * If both annotations are at the same level, then the {@link JSONPropertyIgnore} + * annotation takes precedent and the field is not serialized. + * For example, the following declaration would prevent the getName + * method from being serialized: + *

+     * @JSONPropertyName("FullName")
+     * @JSONPropertyIgnore
+     * public String getName() { return this.name; }
+     * 
+ * + * @param bean + * An object that has getter methods that should be used to make + * a JSONObject. + * @throws JSONException + * If a getter returned a non-finite number. + */ + public JSONObject(Object bean) { + this(); + this.populateMap(bean); + } + + private JSONObject(Object bean, Set objectsRecord) { + this(); + this.populateMap(bean, objectsRecord); + } + + /** + * Construct a JSONObject from an Object, using reflection to find the + * public members. The resulting JSONObject's keys will be the strings from + * the names array, and the values will be the field values associated with + * those keys in the object. If a key is not found or not visible, then it + * will not be copied into the new JSONObject. + * + * @param object + * An object that has fields that should be used to make a + * JSONObject. + * @param names + * An array of strings, the names of the fields to be obtained + * from the object. + */ + public JSONObject(Object object, String ... names) { + this(names.length); + Class c = object.getClass(); + for (int i = 0; i < names.length; i += 1) { + String name = names[i]; + try { + this.putOpt(name, c.getField(name).get(object)); + } catch (Exception ignore) { + } + } + } + + /** + * Construct a JSONObject from a source JSON text string. This is the most + * commonly used JSONObject constructor. + * + * @param source + * A string beginning with { (left + * brace) and ending with } + *  (right brace). + * @exception JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ + public JSONObject(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONObject from a ResourceBundle. + * + * @param baseName + * The ResourceBundle base name. + * @param locale + * The Locale to load the ResourceBundle for. + * @throws JSONException + * If any JSONExceptions are detected. + */ + public JSONObject(String baseName, Locale locale) throws JSONException { + this(); + ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, + Thread.currentThread().getContextClassLoader()); + +// Iterate through the keys in the bundle. + + Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (key != null) { + +// Go through the path, ensuring that there is a nested JSONObject for each +// segment except the last. Add the value using the last segment's name into +// the deepest nested JSONObject. + + String[] path = ((String) key).split("\\."); + int last = path.length - 1; + JSONObject target = this; + for (int i = 0; i < last; i += 1) { + String segment = path[i]; + JSONObject nextTarget = target.optJSONObject(segment); + if (nextTarget == null) { + nextTarget = new JSONObject(); + target.put(segment, nextTarget); + } + target = nextTarget; + } + target.put(path[last], bundle.getString((String) key)); + } + } + } + + /** + * Constructor to specify an initial capacity of the internal map. Useful for library + * internal calls where we know, or at least can best guess, how big this JSONObject + * will be. + * + * @param initialCapacity initial capacity of the internal map. + */ + protected JSONObject(int initialCapacity){ + this.map = new HashMap(initialCapacity); + } + + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a JSONArray + * is stored under the key to hold all of the accumulated values. If there + * is already a JSONArray, then the new value is appended to it. In + * contrast, the put method replaces the previous value. + * + * If only one value is accumulated that is not a JSONArray, then the result + * will be the same as using put. But if multiple values are accumulated, + * then the result will be like append. + * + * @param key + * A key string. + * @param value + * An object to be accumulated under the key. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject accumulate(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, + value instanceof JSONArray ? new JSONArray().put(value) + : value); + } else if (object instanceof JSONArray) { + ((JSONArray) object).put(value); + } else { + this.put(key, new JSONArray().put(object).put(value)); + } + return this; + } + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * + * @param key + * A key string. + * @param value + * An object to be accumulated under the key. + * @return this. + * @throws JSONException + * If the value is non-finite number or if the current value associated with + * the key is not a JSONArray. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject append(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, new JSONArray().put(value)); + } else if (object instanceof JSONArray) { + this.put(key, ((JSONArray) object).put(value)); + } else { + throw wrongValueFormatException(key, "JSONArray", null, null); + } + return this; + } + + /** + * Produce a string from a double. The string "null" will be returned if the + * number is not finite. + * + * @param d + * A double. + * @return A String. + */ + public static String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + +// Shave off trailing zeros and decimal point, if possible. + + String string = Double.toString(d); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 + && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Get the value object associated with a key. + * + * @param key + * A key string. + * @return The object associated with the key. + * @throws JSONException + * if the key is not found. + */ + public Object get(String key) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = this.opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + quote(key) + "] not found."); + } + return object; + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param key + * A key string. + * @return The enum value associated with the key + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an enum. + */ + public > E getEnum(Class clazz, String key) throws JSONException { + E val = optEnum(clazz, key); + if(val==null) { + // JSONException should really take a throwable argument. + // If it did, I would re-implement this with the Enum.valueOf + // method and place any thrown exception in the JSONException + throw wrongValueFormatException(key, "enum of type " + quote(clazz.getSimpleName()), opt(key), null); + } + return val; + } + + /** + * Get the boolean value associated with a key. + * + * @param key + * A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or + * "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object object = this.get(key); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw wrongValueFormatException(key, "Boolean", object, null); + } + + /** + * Get the BigInteger value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value cannot + * be converted to BigInteger. + */ + public BigInteger getBigInteger(String key) throws JSONException { + Object object = this.get(key); + BigInteger ret = objectToBigInteger(object, null); + if (ret != null) { + return ret; + } + throw wrongValueFormatException(key, "BigInteger", object, null); + } + + /** + * Get the BigDecimal value associated with a key. If the value is float or + * double, the {@link BigDecimal#BigDecimal(double)} constructor will + * be used. See notes on the constructor for conversion issues that may + * arise. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value + * cannot be converted to BigDecimal. + */ + public BigDecimal getBigDecimal(String key) throws JSONException { + Object object = this.get(key); + BigDecimal ret = objectToBigDecimal(object, null); + if (ret != null) { + return ret; + } + throw wrongValueFormatException(key, "BigDecimal", object, null); + } + + /** + * Get the double value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "double", object, e); + } + } + + /** + * Get the float value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public float getFloat(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "float", object, e); + } + } + + /** + * Get the Number value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public Number getNumber(String key) throws JSONException { + Object object = this.get(key); + try { + if (object instanceof Number) { + return (Number)object; + } + return stringToNumber(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "number", object, e); + } + } + + /** + * Get the int value associated with a key. + * + * @param key + * A key string. + * @return The integer value. + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an integer. + */ + public int getInt(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "int", object, e); + } + } + + /** + * Get the JSONArray value associated with a key. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw wrongValueFormatException(key, "JSONArray", object, null); + } + + /** + * Get the JSONObject value associated with a key. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw wrongValueFormatException(key, "JSONObject", object, null); + } + + /** + * Get the long value associated with a key. + * + * @param key + * A key string. + * @return The long value. + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to a long. + */ + public long getLong(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "long", object, e); + } + } + + /** + * Get an array of field names from a JSONObject. + * + * @param jo + * JSON object + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(JSONObject jo) { + if (jo.isEmpty()) { + return null; + } + return jo.keySet().toArray(new String[jo.length()]); + } + + /** + * Get an array of public field names from an Object. + * + * @param object + * object to read + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(Object object) { + if (object == null) { + return null; + } + Class klass = object.getClass(); + Field[] fields = klass.getFields(); + int length = fields.length; + if (length == 0) { + return null; + } + String[] names = new String[length]; + for (int i = 0; i < length; i += 1) { + names[i] = fields[i].getName(); + } + return names; + } + + /** + * Get the string associated with a key. + * + * @param key + * A key string. + * @return A string which is the value. + * @throws JSONException + * if there is no string value for the key. + */ + public String getString(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof String) { + return (String) object; + } + throw wrongValueFormatException(key, "string", object, null); + } + + /** + * Determine if the JSONObject contains a specific key. + * + * @param key + * A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.map.containsKey(key); + } + + /** + * Increment a property of a JSONObject. If there is no such property, + * create one with a value of 1 (Integer). If there is such a property, and if it is + * an Integer, Long, Double, Float, BigInteger, or BigDecimal then add one to it. + * No overflow bounds checking is performed, so callers should initialize the key + * prior to this call with an appropriate type that can handle the maximum expected + * value. + * + * @param key + * A key string. + * @return this. + * @throws JSONException + * If there is already a property with this name that is not an + * Integer, Long, Double, or Float. + */ + public JSONObject increment(String key) throws JSONException { + Object value = this.opt(key); + if (value == null) { + this.put(key, 1); + } else if (value instanceof Integer) { + this.put(key, ((Integer) value).intValue() + 1); + } else if (value instanceof Long) { + this.put(key, ((Long) value).longValue() + 1L); + } else if (value instanceof BigInteger) { + this.put(key, ((BigInteger)value).add(BigInteger.ONE)); + } else if (value instanceof Float) { + this.put(key, ((Float) value).floatValue() + 1.0f); + } else if (value instanceof Double) { + this.put(key, ((Double) value).doubleValue() + 1.0d); + } else if (value instanceof BigDecimal) { + this.put(key, ((BigDecimal)value).add(BigDecimal.ONE)); + } else { + throw new JSONException("Unable to increment [" + quote(key) + "]."); + } + return this; + } + + /** + * Determine if the value associated with the key is null or if there is no + * value. + * + * @param key + * A key string. + * @return true if there is no value associated with the key or if the value + * is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(this.opt(key)); + } + + /** + * Get an enumeration of the keys of the JSONObject. Modifying this key Set will also + * modify the JSONObject. Use with caution. + * + * @see Set#iterator() + * + * @return An iterator of the keys. + */ + public Iterator keys() { + return this.keySet().iterator(); + } + + /** + * Get a set of keys of the JSONObject. Modifying this key Set will also modify the + * JSONObject. Use with caution. + * + * @see Map#keySet() + * + * @return A keySet. + */ + public Set keySet() { + return this.map.keySet(); + } + + /** + * Get a set of entries of the JSONObject. These are raw values and may not + * match what is returned by the JSONObject get* and opt* functions. Modifying + * the returned EntrySet or the Entry objects contained therein will modify the + * backing JSONObject. This does not return a clone or a read-only view. + * + * Use with caution. + * + * @see Map#entrySet() + * + * @return An Entry Set + */ + protected Set> entrySet() { + return this.map.entrySet(); + } + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.map.size(); + } + + /** + * Removes all of the elements from this JSONObject. + * The JSONObject will be empty after this call returns. + */ + public void clear() { + this.map.clear(); + } + + /** + * Check if JSONObject is empty. + * + * @return true if JSONObject is empty, otherwise false. + */ + public boolean isEmpty() { + return this.map.isEmpty(); + } + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + if(this.map.isEmpty()) { + return null; + } + return new JSONArray(this.map.keySet()); + } + + /** + * Produce a string from a Number. + * + * @param number + * A Number + * @return A String. + * @throws JSONException + * If n is a non-finite number. + */ + public static String numberToString(Number number) throws JSONException { + if (number == null) { + throw new JSONException("Null pointer"); + } + testValidity(number); + + // Shave off trailing zeros and decimal point, if possible. + + String string = number.toString(); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 + && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Get an optional value associated with a key. + * + * @param key + * A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param key + * A key string. + * @return The enum value associated with the key or null if not found + */ + public > E optEnum(Class clazz, String key) { + return this.optEnum(clazz, key, null); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param key + * A key string. + * @param defaultValue + * The default in case the value is not found + * @return The enum value associated with the key or defaultValue + * if the value is not found or cannot be assigned to clazz + */ + public > E optEnum(Class clazz, String key, E defaultValue) { + try { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + // we just checked it! + @SuppressWarnings("unchecked") + E myE = (E) val; + return myE; + } + return Enum.valueOf(clazz, val.toString()); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (NullPointerException e) { + return defaultValue; + } + } + + /** + * Get an optional boolean associated with a key. It returns false if there + * is no such key, or if the value is not Boolean.TRUE or the String "true". + * + * @param key + * A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return this.optBoolean(key, false); + } + + /** + * Get an optional boolean associated with a key. It returns the + * defaultValue if there is no such key, or if it is not a Boolean or the + * String "true" or "false" (case insensitive). + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Boolean){ + return ((Boolean) val).booleanValue(); + } + try { + // we'll use the get anyway because it does string conversion. + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional boolean object associated with a key. It returns false if there + * is no such key, or if the value is not Boolean.TRUE or the String "true". + * + * @param key + * A key string. + * @return The truth. + */ + public Boolean optBooleanObject(String key) { + return this.optBooleanObject(key, false); + } + + /** + * Get an optional boolean object associated with a key. It returns the + * defaultValue if there is no such key, or if it is not a Boolean or the + * String "true" or "false" (case insensitive). + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return The truth. + */ + public Boolean optBooleanObject(String key, Boolean defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Boolean){ + return ((Boolean) val).booleanValue(); + } + try { + // we'll use the get anyway because it does string conversion. + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional BigDecimal associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. If the value + * is float or double, then the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { + Object val = this.opt(key); + return objectToBigDecimal(val, defaultValue); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @return BigDecimal conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { + return objectToBigDecimal(val, defaultValue, true); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @param exact When true, then {@link Double} and {@link Float} values will be converted exactly. + * When false, they will be converted to {@link String} values before converting to {@link BigDecimal}. + * @return BigDecimal conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue, boolean exact) { + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof BigDecimal){ + return (BigDecimal) val; + } + if (val instanceof BigInteger){ + return new BigDecimal((BigInteger) val); + } + if (val instanceof Double || val instanceof Float){ + if (!numberIsFinite((Number)val)) { + return defaultValue; + } + if (exact) { + return new BigDecimal(((Number)val).doubleValue()); + } + // use the string constructor so that we maintain "nice" values for doubles and floats + // the double constructor will translate doubles to "exact" values instead of the likely + // intended representation + return new BigDecimal(val.toString()); + } + if (val instanceof Long || val instanceof Integer + || val instanceof Short || val instanceof Byte){ + return new BigDecimal(((Number) val).longValue()); + } + // don't check if it's a string in case of unchecked Number subclasses + try { + return new BigDecimal(val.toString()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional BigInteger associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public BigInteger optBigInteger(String key, BigInteger defaultValue) { + Object val = this.opt(key); + return objectToBigInteger(val, defaultValue); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @return BigInteger conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof BigInteger){ + return (BigInteger) val; + } + if (val instanceof BigDecimal){ + return ((BigDecimal) val).toBigInteger(); + } + if (val instanceof Double || val instanceof Float){ + if (!numberIsFinite((Number)val)) { + return defaultValue; + } + return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); + } + if (val instanceof Long || val instanceof Integer + || val instanceof Short || val instanceof Byte){ + return BigInteger.valueOf(((Number) val).longValue()); + } + // don't check if it's a string in case of unchecked Number subclasses + try { + // the other opt functions handle implicit conversions, i.e. + // jo.put("double",1.1d); + // jo.optInt("double"); -- will return 1, not an error + // this conversion to BigDecimal then to BigInteger is to maintain + // that type cast support that may truncate the decimal. + final String valStr = val.toString(); + if(isDecimalNotation(valStr)) { + return new BigDecimal(valStr).toBigInteger(); + } + return new BigInteger(valStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional double associated with a key, or NaN if there is no such + * key or if its value is not a number. If the value is a string, an attempt + * will be made to evaluate it as a number. + * + * @param key + * A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return this.optDouble(key, Double.NaN); + } + + /** + * Get an optional double associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + return val.doubleValue(); + } + + /** + * Get an optional Double object associated with a key, or NaN if there is no such + * key or if its value is not a number. If the value is a string, an attempt + * will be made to evaluate it as a number. + * + * @param key + * A string which is the key. + * @return An object which is the value. + */ + public Double optDoubleObject(String key) { + return this.optDoubleObject(key, Double.NaN); + } + + /** + * Get an optional Double object associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Double optDoubleObject(String key, Double defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + return val.doubleValue(); + } + + /** + * Get the optional float value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param key + * A key string. + * @return The value. + */ + public float optFloat(String key) { + return this.optFloat(key, Float.NaN); + } + + /** + * Get the optional float value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param key + * A key string. + * @param defaultValue + * The default value. + * @return The value. + */ + public float optFloat(String key, float defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + final float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return defaultValue; + // } + return floatValue; + } + + /** + * Get the optional Float object associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param key + * A key string. + * @return The object. + */ + public Float optFloatObject(String key) { + return this.optFloatObject(key, Float.NaN); + } + + /** + * Get the optional Float object associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param key + * A key string. + * @param defaultValue + * The default object. + * @return The object. + */ + public Float optFloatObject(String key, Float defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + final Float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return defaultValue; + // } + return floatValue; + } + + /** + * Get an optional int value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return this.optInt(key, 0); + } + + /** + * Get an optional int value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get an optional Integer object associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public Integer optIntegerObject(String key) { + return this.optIntegerObject(key, 0); + } + + /** + * Get an optional Integer object associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Integer optIntegerObject(String key, Integer defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get an optional JSONArray associated with a key. It returns null if there + * is no such key, or if its value is not a JSONArray. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + return this.optJSONArray(key, null); + } + + /** + * Get an optional JSONArray associated with a key, or the default if there + * is no such key, or if its value is not a JSONArray. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key, JSONArray defaultValue) { + Object object = this.opt(key); + return object instanceof JSONArray ? (JSONArray) object : defaultValue; + } + + /** + * Get an optional JSONObject associated with a key. It returns null if + * there is no such key, or if its value is not a JSONObject. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { return this.optJSONObject(key, null); } + + /** + * Get an optional JSONObject associated with a key, or the default if there + * is no such key or if the value is not a JSONObject. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An JSONObject which is the value. + */ + public JSONObject optJSONObject(String key, JSONObject defaultValue) { + Object object = this.opt(key); + return object instanceof JSONObject ? (JSONObject) object : defaultValue; + } + + /** + * Get an optional long value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return this.optLong(key, 0); + } + + /** + * Get an optional long value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + + return val.longValue(); + } + + /** + * Get an optional Long object associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public Long optLongObject(String key) { + return this.optLongObject(key, 0L); + } + + /** + * Get an optional Long object associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Long optLongObject(String key, Long defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + + return val.longValue(); + } + + /** + * Get an optional {@link Number} value associated with a key, or null + * if there is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public Number optNumber(String key) { + return this.optNumber(key, null); + } + + /** + * Get an optional {@link Number} value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Number optNumber(String key, Number defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number){ + return (Number) val; + } + + try { + return stringToNumber(val.toString()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional string associated with a key. It returns an empty string + * if there is no such key. If the value is not a string and is not null, + * then it is converted to a string. + * + * @param key + * A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return this.optString(key, ""); + } + + /** + * Get an optional string associated with a key. It returns the defaultValue + * if there is no such key. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + /** + * Populates the internal map of the JSONObject with the bean properties. The + * bean can not be recursive. + * + * @see JSONObject#JSONObject(Object) + * + * @param bean + * the bean + * @throws JSONException + * If a getter returned a non-finite number. + */ + private void populateMap(Object bean) { + populateMap(bean, Collections.newSetFromMap(new IdentityHashMap())); + } + + private void populateMap(Object bean, Set objectsRecord) { + Class klass = bean.getClass(); + + // If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); + for (final Method method : methods) { + final int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) + && !Modifier.isStatic(modifiers) + && method.getParameterTypes().length == 0 + && !method.isBridge() + && method.getReturnType() != Void.TYPE + && isValidMethodName(method.getName())) { + final String key = getKeyNameFromMethod(method); + if (key != null && !key.isEmpty()) { + try { + final Object result = method.invoke(bean); + if (result != null) { + // check cyclic dependency and throw error if needed + // the wrap and populateMap combination method is + // itself DFS recursive + if (objectsRecord.contains(result)) { + throw recursivelyDefinedObjectException(key); + } + + objectsRecord.add(result); + + testValidity(result); + this.map.put(key, wrap(result, objectsRecord)); + + objectsRecord.remove(result); + + // we don't use the result anywhere outside of wrap + // if it's a resource we should be sure to close it + // after calling toString + if (result instanceof Closeable) { + try { + ((Closeable) result).close(); + } catch (IOException ignore) { + } + } + } + } catch (IllegalAccessException ignore) { + } catch (IllegalArgumentException ignore) { + } catch (InvocationTargetException ignore) { + } + } + } + } + } + + private static boolean isValidMethodName(String name) { + return !"getClass".equals(name) && !"getDeclaringClass".equals(name); + } + + private static String getKeyNameFromMethod(Method method) { + final int ignoreDepth = getAnnotationDepth(method, JSONPropertyIgnore.class); + if (ignoreDepth > 0) { + final int forcedNameDepth = getAnnotationDepth(method, JSONPropertyName.class); + if (forcedNameDepth < 0 || ignoreDepth <= forcedNameDepth) { + // the hierarchy asked to ignore, and the nearest name override + // was higher or non-existent + return null; + } + } + JSONPropertyName annotation = getAnnotation(method, JSONPropertyName.class); + if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) { + return annotation.value(); + } + String key; + final String name = method.getName(); + if (name.startsWith("get") && name.length() > 3) { + key = name.substring(3); + } else if (name.startsWith("is") && name.length() > 2) { + key = name.substring(2); + } else { + return null; + } + // if the first letter in the key is not uppercase, then skip. + // This is to maintain backwards compatibility before PR406 + // (https://github.com/stleary/JSON-java/pull/406/) + if (key.length() == 0 || Character.isLowerCase(key.charAt(0))) { + return null; + } + if (key.length() == 1) { + key = key.toLowerCase(Locale.ROOT); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase(Locale.ROOT) + key.substring(1); + } + return key; + } + + /** + * Searches the class hierarchy to see if the method or it's super + * implementations and interfaces has the annotation. + * + * @param + * type of the annotation + * + * @param m + * method to check + * @param annotationClass + * annotation to look for + * @return the {@link Annotation} if the annotation exists on the current method + * or one of its super class definitions + */ + private static A getAnnotation(final Method m, final Class annotationClass) { + // if we have invalid data the result is null + if (m == null || annotationClass == null) { + return null; + } + + if (m.isAnnotationPresent(annotationClass)) { + return m.getAnnotation(annotationClass); + } + + // if we've already reached the Object class, return null; + Class c = m.getDeclaringClass(); + if (c.getSuperclass() == null) { + return null; + } + + // check directly implemented interfaces for the method being checked + for (Class i : c.getInterfaces()) { + try { + Method im = i.getMethod(m.getName(), m.getParameterTypes()); + return getAnnotation(im, annotationClass); + } catch (final SecurityException ex) { + continue; + } catch (final NoSuchMethodException ex) { + continue; + } + } + + //If the superclass is Object, no annotations will be found any more + if (c.getSuperclass().equals(Object.class)) + return null; + + try { + return getAnnotation( + c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), + annotationClass); + } catch (final SecurityException ex) { + return null; + } catch (final NoSuchMethodException ex) { + return null; + } + } + + /** + * Searches the class hierarchy to see if the method or it's super + * implementations and interfaces has the annotation. Returns the depth of the + * annotation in the hierarchy. + * + * @param m + * method to check + * @param annotationClass + * annotation to look for + * @return Depth of the annotation or -1 if the annotation is not on the method. + */ + private static int getAnnotationDepth(final Method m, final Class annotationClass) { + // if we have invalid data the result is -1 + if (m == null || annotationClass == null) { + return -1; + } + + if (m.isAnnotationPresent(annotationClass)) { + return 1; + } + + // if we've already reached the Object class, return -1; + Class c = m.getDeclaringClass(); + if (c.getSuperclass() == null) { + return -1; + } + + // check directly implemented interfaces for the method being checked + for (Class i : c.getInterfaces()) { + try { + Method im = i.getMethod(m.getName(), m.getParameterTypes()); + int d = getAnnotationDepth(im, annotationClass); + if (d > 0) { + // since the annotation was on the interface, add 1 + return d + 1; + } + } catch (final SecurityException ex) { + continue; + } catch (final NoSuchMethodException ex) { + continue; + } + } + + //If the superclass is Object, no annotations will be found any more + if (c.getSuperclass().equals(Object.class)) + return -1; + + try { + int d = getAnnotationDepth( + c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), + annotationClass); + if (d > 0) { + // since the annotation was on the superclass, add 1 + return d + 1; + } + return -1; + } catch (final SecurityException ex) { + return -1; + } catch (final NoSuchMethodException ex) { + return -1; + } + } + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A boolean which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * + * @param key + * A key string. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, Collection value) throws JSONException { + return this.put(key, new JSONArray(value)); + } + + /** + * Put a key/double pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A double which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, double value) throws JSONException { + return this.put(key, Double.valueOf(value)); + } + + /** + * Put a key/float pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A float which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, float value) throws JSONException { + return this.put(key, Float.valueOf(value)); + } + + /** + * Put a key/int pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * An int which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + return this.put(key, Integer.valueOf(value)); + } + + /** + * Put a key/long pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A long which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + return this.put(key, Long.valueOf(value)); + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * + * @param key + * A key string. + * @param value + * A Map value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, Map value) throws JSONException { + return this.put(key, new JSONObject(value)); + } + + /** + * Put a key/value pair in the JSONObject. If the value is null, then the + * key will be removed from the JSONObject if it is present. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new NullPointerException("Null key."); + } + if (value != null) { + testValidity(value); + this.map.put(key, value); + } else { + this.remove(key); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null, and only if there is not already a member with that + * name. + * + * @param key + * key to insert into + * @param value + * value to insert + * @return this. + * @throws JSONException + * if the key is a duplicate + */ + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (this.opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + return this.put(key, value); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + return this.put(key, value); + } + return this; + } + + /** + * Creates a JSONPointer using an initialization string and tries to + * match it to an item within this JSONObject. For example, given a + * JSONObject initialized with this document: + *
+     * {
+     *     "a":{"b":"c"}
+     * }
+     * 
+ * and this JSONPointer string: + *
+     * "/a/b"
+     * 
+ * Then this method will return the String "c". + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(String jsonPointer) { + return query(new JSONPointer(jsonPointer)); + } + /** + * Uses a user initialized JSONPointer and tries to + * match it to an item within this JSONObject. For example, given a + * JSONObject initialized with this document: + *
+     * {
+     *     "a":{"b":"c"}
+     * }
+     * 
+ * and this JSONPointer: + *
+     * "/a/b"
+     * 
+ * Then this method will return the String "c". + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer the string representation of the JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(String jsonPointer) { + return optQuery(new JSONPointer(jsonPointer)); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer The JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } catch (JSONPointerException e) { + return null; + } + } + + /** + * Produce a string in double quotes with backslash sequences in all the + * right places. A backslash will be inserted within </, producing + * <\/, allowing JSON text to be delivered in HTML. In JSON text, a + * string cannot contain a control character or an unescaped quote or + * backslash. + * + * @param string + * A String + * @return A String correctly formatted for insertion in a JSON text. + */ + @SuppressWarnings("resource") + public static String quote(String string) { + StringWriter sw = new StringWriter(); + try { + return quote(string, sw).toString(); + } catch (IOException ignored) { + // will never happen - we are writing to a string writer + return ""; + } + } + + /** + * Quotes a string and appends the result to a given Writer. + * + * @param string The input string to be quoted. + * @param w The Writer to which the quoted string will be appended. + * @return The same Writer instance after appending the quoted string. + * @throws IOException If an I/O error occurs while writing to the Writer. + */ + public static Writer quote(String string, Writer w) throws IOException { + if (string == null || string.isEmpty()) { + w.write("\"\""); + return w; + } + + char b; + char c = 0; + String hhhh; + int i; + int len = string.length(); + + w.write('"'); + for (i = 0; i < len; i += 1) { + b = c; + c = string.charAt(i); + switch (c) { + case '\\': + case '"': + w.write('\\'); + w.write(c); + break; + case '/': + if (b == '<') { + w.write('\\'); + } + w.write(c); + break; + case '\b': + w.write("\\b"); + break; + case '\t': + w.write("\\t"); + break; + case '\n': + w.write("\\n"); + break; + case '\f': + w.write("\\f"); + break; + case '\r': + w.write("\\r"); + break; + default: + if (c < ' ' || (c >= '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + w.write("\\u"); + hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { + w.write(c); + } + } + } + w.write('"'); + return w; + } + + /** + * Remove a name and its value, if present. + * + * @param key + * The name to be removed. + * @return The value that was associated with the name, or null if there was + * no value. + */ + public Object remove(String key) { + return this.map.remove(key); + } + + /** + * Determine if two JSONObjects are similar. + * They must contain the same set of names which must be associated with + * similar values. + * + * @param other The other JSONObject + * @return true if they are equal + */ + public boolean similar(Object other) { + try { + if (!(other instanceof JSONObject)) { + return false; + } + if (!this.keySet().equals(((JSONObject)other).keySet())) { + return false; + } + for (final Entry entry : this.entrySet()) { + String name = entry.getKey(); + Object valueThis = entry.getValue(); + Object valueOther = ((JSONObject)other).get(name); + if(valueThis == valueOther) { + continue; + } + if(valueThis == null) { + return false; + } + if (valueThis instanceof JSONObject) { + if (!((JSONObject)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof Number && valueOther instanceof Number) { + if (!isNumberSimilar((Number)valueThis, (Number)valueOther)) { + return false; + } + } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { + if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } catch (Throwable exception) { + return false; + } + } + + /** + * Compares two numbers to see if they are similar. + * + * If either of the numbers are Double or Float instances, then they are checked to have + * a finite value. If either value is not finite (NaN or ±infinity), then this + * function will always return false. If both numbers are finite, they are first checked + * to be the same type and implement {@link Comparable}. If they do, then the actual + * {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't + * implement Comparable, then they are converted to {@link BigDecimal}s. Finally the + * BigDecimal values are compared using {@link BigDecimal#compareTo(BigDecimal)}. + * + * @param l the Left value to compare. Can not be null. + * @param r the right value to compare. Can not be null. + * @return true if the numbers are similar, false otherwise. + */ + static boolean isNumberSimilar(Number l, Number r) { + if (!numberIsFinite(l) || !numberIsFinite(r)) { + // non-finite numbers are never similar + return false; + } + + // if the classes are the same and implement Comparable + // then use the built in compare first. + if(l.getClass().equals(r.getClass()) && l instanceof Comparable) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + int compareTo = ((Comparable)l).compareTo(r); + return compareTo==0; + } + + // BigDecimal should be able to handle all of our number types that we support through + // documentation. Convert to BigDecimal first, then use the Compare method to + // decide equality. + final BigDecimal lBigDecimal = objectToBigDecimal(l, null, false); + final BigDecimal rBigDecimal = objectToBigDecimal(r, null, false); + if (lBigDecimal == null || rBigDecimal == null) { + return false; + } + return lBigDecimal.compareTo(rBigDecimal) == 0; + } + + private static boolean numberIsFinite(Number n) { + if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) { + return false; + } else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) { + return false; + } + return true; + } + + /** + * Tests if the value should be tried as a decimal. It makes no test if there are actual digits. + * + * @param val value to test + * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise. + */ + protected static boolean isDecimalNotation(final String val) { + return val.indexOf('.') > -1 || val.indexOf('e') > -1 + || val.indexOf('E') > -1 || "-0".equals(val); + } + + /** + * Try to convert a string into a number, boolean, or null. If the string + * can't be converted, return the string. + * + * @param string + * A String. can not be null. + * @return A simple JSON value. + * @throws NullPointerException + * Thrown if the string is null. + */ + // Changes to this method must be copied to the corresponding method in + // the XML class to keep full support for Android + public static Object stringToValue(String string) { + if ("".equals(string)) { + return string; + } + + // check JSON key words true/false/null + if ("true".equalsIgnoreCase(string)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(string)) { + return Boolean.FALSE; + } + if ("null".equalsIgnoreCase(string)) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. If a number cannot be + * produced, then the value will just be a string. + */ + + char initial = string.charAt(0); + if ((initial >= '0' && initial <= '9') || initial == '-') { + try { + return stringToNumber(string); + } catch (Exception ignore) { + } + } + return string; + } + + /** + * Converts a string to a number using the narrowest possible type. Possible + * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer. + * When a Double is returned, it should always be a valid Double and not NaN or +-infinity. + * + * @param val value to convert + * @return Number representation of the value. + * @throws NumberFormatException thrown if the value is not a valid number. A public + * caller should catch this and wrap it in a {@link JSONException} if applicable. + */ + protected static Number stringToNumber(final String val) throws NumberFormatException { + char initial = val.charAt(0); + if ((initial >= '0' && initial <= '9') || initial == '-') { + // decimal representation + if (isDecimalNotation(val)) { + // Use a BigDecimal all the time so we keep the original + // representation. BigDecimal doesn't support -0.0, ensure we + // keep that by forcing a decimal. + try { + BigDecimal bd = new BigDecimal(val); + if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) { + return Double.valueOf(-0.0); + } + return bd; + } catch (NumberFormatException retryAsDouble) { + // this is to support "Hex Floats" like this: 0x1.0P-1074 + try { + Double d = Double.valueOf(val); + if(d.isNaN() || d.isInfinite()) { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + return d; + } catch (NumberFormatException ignore) { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + } + } + // block items like 00 01 etc. Java number parsers treat these as Octal. + if(initial == '0' && val.length() > 1) { + char at1 = val.charAt(1); + if(at1 >= '0' && at1 <= '9') { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + } else if (initial == '-' && val.length() > 2) { + char at1 = val.charAt(1); + char at2 = val.charAt(2); + if(at1 == '0' && at2 >= '0' && at2 <= '9') { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + } + // integer representation. + // This will narrow any values to the smallest reasonable Object representation + // (Integer, Long, or BigInteger) + + // BigInteger down conversion: We use a similar bitLength compare as + // BigInteger#intValueExact uses. Increases GC, but objects hold + // only what they need. i.e. Less runtime overhead if the value is + // long lived. + BigInteger bi = new BigInteger(val); + if(bi.bitLength() <= 31){ + return Integer.valueOf(bi.intValue()); + } + if(bi.bitLength() <= 63){ + return Long.valueOf(bi.longValue()); + } + return bi; + } + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + + /** + * Throw an exception if the object is a NaN or infinite number. + * + * @param o + * The object to test. + * @throws JSONException + * If o is a non-finite number. + */ + public static void testValidity(Object o) throws JSONException { + if (o instanceof Number && !numberIsFinite((Number) o)) { + throw new JSONException("JSON does not allow non-finite numbers."); + } + } + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * + * @param names + * A JSONArray containing a list of key strings. This determines + * the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException + * If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.isEmpty()) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace is + * added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + */ + @Override + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a pretty-printed JSON text of this JSONObject. + * + *

If

{@code indentFactor > 0}
and the {@link JSONObject} + * has only one key, then the object will be output on a single line: + *
{@code {"key": 1}}
+ * + *

If an object has 2 or more keys, then it will be output across + * multiple lines:

{@code {
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }}
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the object contains an invalid number. + */ + @SuppressWarnings("resource") + public String toString(int indentFactor) throws JSONException { + StringWriter w = new StringWriter(); + return this.write(w, indentFactor, 0).toString(); + } + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce the + * JSON text. The method is required to produce a strictly conforming text. + * If the object does not contain a toJSONString method (which is the most + * common case), then a text will be produced by other means. If the value + * is an array or Collection, then a JSONArray will be made from it and its + * toJSONString method will be called. If the value is a MAP, then a + * JSONObject will be made from it and its toJSONString method will be + * called. Otherwise, the value's toString method will be called, and the + * result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the value is or contains an invalid number. + */ + public static String valueToString(Object value) throws JSONException { + // moves the implementation to JSONWriter as: + // 1. It makes more sense to be part of the writer class + // 2. For Android support this method is not available. By implementing it in the Writer + // Android users can use the writer with the built in Android JSONObject implementation. + return JSONWriter.valueToString(value); + } + + /** + * Wrap an object, if necessary. If the object is null, return the NULL + * object. If it is an array or collection, wrap it in a JSONArray. If it is + * a map, wrap it in a JSONObject. If it is a standard property (Double, + * String, et al) then it is already wrapped. Otherwise, if it comes from + * one of the java packages, turn it into a string. And if it doesn't, try + * to wrap it in a JSONObject. If the wrapping fails, then null is returned. + * + * @param object + * The object to wrap + * @return The wrapped value + */ + public static Object wrap(Object object) { + return wrap(object, null); + } + + /** + * Wrap an object, if necessary. If the object is null, return the NULL + * object. If it is an array or collection, wrap it in a JSONArray. If it is + * a map, wrap it in a JSONObject. If it is a standard property (Double, + * String, et al) then it is already wrapped. Otherwise, if it comes from + * one of the java packages, turn it into a string. And if it doesn't, try + * to wrap it in a JSONObject. If the wrapping fails, then null is returned. + * + * @param object + * The object to wrap + * @param recursionDepth + * Variable for tracking the count of nested object creations. + * @param jsonParserConfiguration + * Variable to pass parser custom configuration for json parsing. + * @return The wrapped value + */ + static Object wrap(Object object, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + return wrap(object, null, recursionDepth, jsonParserConfiguration); + } + + private static Object wrap(Object object, Set objectsRecord) { + return wrap(object, objectsRecord, 0, new JSONParserConfiguration()); + } + + private static Object wrap(Object object, Set objectsRecord, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + try { + if (NULL.equals(object)) { + return NULL; + } + if (object instanceof JSONObject || object instanceof JSONArray + || NULL.equals(object) || object instanceof JSONString + || object instanceof Byte || object instanceof Character + || object instanceof Short || object instanceof Integer + || object instanceof Long || object instanceof Boolean + || object instanceof Float || object instanceof Double + || object instanceof String || object instanceof BigInteger + || object instanceof BigDecimal || object instanceof Enum) { + return object; + } + + if (object instanceof Collection) { + Collection coll = (Collection) object; + return new JSONArray(coll, recursionDepth, jsonParserConfiguration); + } + if (object.getClass().isArray()) { + return new JSONArray(object); + } + if (object instanceof Map) { + Map map = (Map) object; + return new JSONObject(map, recursionDepth, jsonParserConfiguration); + } + Package objectPackage = object.getClass().getPackage(); + String objectPackageName = objectPackage != null ? objectPackage + .getName() : ""; + if (objectPackageName.startsWith("java.") + || objectPackageName.startsWith("javax.") + || object.getClass().getClassLoader() == null) { + return object.toString(); + } + if (objectsRecord != null) { + return new JSONObject(object, objectsRecord); + } + return new JSONObject(object); + } + catch (JSONException exception) { + throw exception; + } catch (Exception exception) { + return null; + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param writer the writer object + * @return The writer. + * @throws JSONException if a called function has an error + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + @SuppressWarnings("resource") + static final Writer writeValue(Writer writer, Object value, + int indentFactor, int indent) throws JSONException, IOException { + if (value == null || value.equals(null)) { + writer.write("null"); + } else if (value instanceof JSONString) { + Object o; + try { + o = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + writer.write(o != null ? o.toString() : quote(value.toString())); + } else if (value instanceof Number) { + // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary + final String numberAsString = numberToString((Number) value); + if(NUMBER_PATTERN.matcher(numberAsString).matches()) { + writer.write(numberAsString); + } else { + // The Number value is not a valid JSON number. + // Instead we will quote it as a string + quote(numberAsString, writer); + } + } else if (value instanceof Boolean) { + writer.write(value.toString()); + } else if (value instanceof Enum) { + writer.write(quote(((Enum)value).name())); + } else if (value instanceof JSONObject) { + ((JSONObject) value).write(writer, indentFactor, indent); + } else if (value instanceof JSONArray) { + ((JSONArray) value).write(writer, indentFactor, indent); + } else if (value instanceof Map) { + Map map = (Map) value; + new JSONObject(map).write(writer, indentFactor, indent); + } else if (value instanceof Collection) { + Collection coll = (Collection) value; + new JSONArray(coll).write(writer, indentFactor, indent); + } else if (value.getClass().isArray()) { + new JSONArray(value).write(writer, indentFactor, indent); + } else { + quote(value.toString(), writer); + } + return writer; + } + + static final void indent(Writer writer, int indent) throws IOException { + for (int i = 0; i < indent; i += 1) { + writer.write(' '); + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. + * + *

If

{@code indentFactor > 0}
and the {@link JSONObject} + * has only one key, then the object will be output on a single line: + *
{@code {"key": 1}}
+ * + *

If an object has 2 or more keys, then it will be output across + * multiple lines:

{@code {
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }}
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param writer + * Writes the serialized JSON + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return The writer. + * @throws JSONException if a called function has an error or a write error + * occurs + */ + @SuppressWarnings("resource") + public Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean needsComma = false; + final int length = this.length(); + writer.write('{'); + + if (length == 1) { + final Entry entry = this.entrySet().iterator().next(); + final String key = entry.getKey(); + writer.write(quote(key)); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + try{ + writeValue(writer, entry.getValue(), indentFactor, indent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONObject value for key: " + key, e); + } + } else if (length != 0) { + final int newIndent = indent + indentFactor; + for (final Entry entry : this.entrySet()) { + if (needsComma) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, newIndent); + final String key = entry.getKey(); + writer.write(quote(key)); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + try { + writeValue(writer, entry.getValue(), indentFactor, newIndent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONObject value for key: " + key, e); + } + needsComma = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, indent); + } + writer.write('}'); + return writer; + } catch (IOException exception) { + throw new JSONException(exception); + } + } + + /** + * Returns a java.util.Map containing all of the entries in this object. + * If an entry in the object is a JSONArray or JSONObject it will also + * be converted. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a java.util.Map containing the entries of this object + */ + public Map toMap() { + Map results = new HashMap(); + for (Entry entry : this.entrySet()) { + Object value; + if (entry.getValue() == null || NULL.equals(entry.getValue())) { + value = null; + } else if (entry.getValue() instanceof JSONObject) { + value = ((JSONObject) entry.getValue()).toMap(); + } else if (entry.getValue() instanceof JSONArray) { + value = ((JSONArray) entry.getValue()).toList(); + } else { + value = entry.getValue(); + } + results.put(entry.getKey(), value); + } + return results; + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * @param key name of the key + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + String key, + String valueType, + Object value, + Throwable cause) { + if(value == null) { + + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + " (null)." + , cause); + } + // don't try to toString collections or known object types that could be large. + if(value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value.getClass() + ")." + , cause); + } + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ")." + , cause); + } + + /** + * Create a new JSONException in a common format for recursive object definition. + * @param key name of the key + * @return JSONException that can be thrown. + */ + private static JSONException recursivelyDefinedObjectException(String key) { + return new JSONException( + "JavaBean object contains recursively defined member variable of key " + quote(key) + ); + } + + /** + * For a prospective number, remove the leading zeros + * @param value prospective number + * @return number without leading zeros + */ + private static String removeLeadingZerosOfNumber(String value){ + if (value.equals("-")){return value;} + boolean negativeFirstChar = (value.charAt(0) == '-'); + int counter = negativeFirstChar ? 1:0; + while (counter < value.length()){ + if (value.charAt(counter) != '0'){ + if (negativeFirstChar) {return "-".concat(value.substring(counter));} + return value.substring(counter); + } + ++counter; + } + if (negativeFirstChar) {return "-0";} + return "0"; + } +} diff --git a/src/main/java/org/json/JSONParserConfiguration.java b/src/main/java/org/json/JSONParserConfiguration.java new file mode 100755 index 0000000..a5a5db0 --- /dev/null +++ b/src/main/java/org/json/JSONParserConfiguration.java @@ -0,0 +1,26 @@ +package org.json; + +/** + * Configuration object for the JSON parser. The configuration is immutable. + */ +public class JSONParserConfiguration extends ParserConfiguration { + + /** + * Configuration with the default values. + */ + public JSONParserConfiguration() { + super(); + } + + @Override + protected JSONParserConfiguration clone() { + return new JSONParserConfiguration(); + } + + @SuppressWarnings("unchecked") + @Override + public JSONParserConfiguration withMaxNestingDepth(final int maxNestingDepth) { + return super.withMaxNestingDepth(maxNestingDepth); + } + +} diff --git a/src/main/java/org/json/JSONPointer.java b/src/main/java/org/json/JSONPointer.java index b3b39c8..b8cf044 100644 --- a/src/main/java/org/json/JSONPointer.java +++ b/src/main/java/org/json/JSONPointer.java @@ -1,275 +1,287 @@ -package org.json; - -import static net.lax1dude.eaglercraft.v1_8.HString.format; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/* -Public Domain. -*/ - -/** - * A JSON Pointer is a simple query language defined for JSON documents by - * RFC 6901. - * - * In a nutshell, JSONPointer allows the user to navigate into a JSON document - * using strings, and retrieve targeted objects, like a simple form of XPATH. - * Path segments are separated by the '/' char, which signifies the root of - * the document when it appears as the first char of the string. Array - * elements are navigated using ordinals, counting from 0. JSONPointer strings - * may be extended to any arbitrary number of segments. If the navigation - * is successful, the matched item is returned. A matched item may be a - * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building - * fails, an appropriate exception is thrown. If the navigation fails to find - * a match, a JSONPointerException is thrown. - * - * @author JSON.org - * @version 2016-05-14 - */ -public class JSONPointer { - - // used for URL encoding and decoding - private static final String ENCODING = "utf-8"; - - /** - * This class allows the user to build a JSONPointer in steps, using - * exactly one segment in each step. - */ - public static class Builder { - - // Segments for the eventual JSONPointer string - private final List refTokens = new ArrayList(); - - /** - * Creates a {@code JSONPointer} instance using the tokens previously set using the - * {@link #append(String)} method calls. - * @return a JSONPointer object - */ - public JSONPointer build() { - return new JSONPointer(this.refTokens); - } - - /** - * Adds an arbitrary token to the list of reference tokens. It can be any non-null value. - * - * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the - * argument of this method MUST NOT be escaped. If you want to query the property called - * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no - * need to escape it as {@code "a~0b"}. - * - * @param token the new token to be appended to the list - * @return {@code this} - * @throws NullPointerException if {@code token} is null - */ - public Builder append(String token) { - if (token == null) { - throw new NullPointerException("token cannot be null"); - } - this.refTokens.add(token); - return this; - } - - /** - * Adds an integer to the reference token list. Although not necessarily, mostly this token will - * denote an array index. - * - * @param arrayIndex the array index to be added to the token list - * @return {@code this} - */ - public Builder append(int arrayIndex) { - this.refTokens.add(String.valueOf(arrayIndex)); - return this; - } - } - - /** - * Static factory method for {@link Builder}. Example usage: - * - *


-     * JSONPointer pointer = JSONPointer.builder()
-     *       .append("obj")
-     *       .append("other~key").append("another/key")
-     *       .append("\"")
-     *       .append(0)
-     *       .build();
-     * 
- * - * @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained - * {@link Builder#append(String)} calls. - */ - public static Builder builder() { - return new Builder(); - } - - // Segments for the JSONPointer string - private final List refTokens; - - /** - * Pre-parses and initializes a new {@code JSONPointer} instance. If you want to - * evaluate the same JSON Pointer on different JSON documents then it is recommended - * to keep the {@code JSONPointer} instances due to performance considerations. - * - * @param pointer the JSON String or URI Fragment representation of the JSON pointer. - * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer - */ - public JSONPointer(final String pointer) { - if (pointer == null) { - throw new NullPointerException("pointer cannot be null"); - } - if (pointer.isEmpty() || pointer.equals("#")) { - this.refTokens = Collections.emptyList(); - return; - } - String refs; - if (pointer.startsWith("#/")) { - refs = pointer.substring(2); - try { - refs = URLDecoder.decode(refs, ENCODING); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } else if (pointer.startsWith("/")) { - refs = pointer.substring(1); - } else { - throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); - } - this.refTokens = new ArrayList(); - int slashIdx = -1; - int prevSlashIdx = 0; - do { - prevSlashIdx = slashIdx + 1; - slashIdx = refs.indexOf('/', prevSlashIdx); - if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) { - // found 2 slashes in a row ( obj//next ) - // or single slash at the end of a string ( obj/test/ ) - this.refTokens.add(""); - } else if (slashIdx >= 0) { - final String token = refs.substring(prevSlashIdx, slashIdx); - this.refTokens.add(unescape(token)); - } else { - // last item after separator, or no separator at all. - final String token = refs.substring(prevSlashIdx); - this.refTokens.add(unescape(token)); - } - } while (slashIdx >= 0); - // using split does not take into account consecutive separators or "ending nulls" - //for (String token : refs.split("/")) { - // this.refTokens.add(unescape(token)); - //} - } - - public JSONPointer(List refTokens) { - this.refTokens = new ArrayList(refTokens); - } - - /** - * @see rfc6901 section 3 - */ - private static String unescape(String token) { - return token.replace("~1", "/").replace("~0", "~"); - } - - /** - * Evaluates this JSON Pointer on the given {@code document}. The {@code document} - * is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty - * JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the - * returned value will be {@code document} itself. - * - * @param document the JSON document which should be the subject of querying. - * @return the result of the evaluation - * @throws JSONPointerException if an error occurs during evaluation - */ - public Object queryFrom(Object document) throws JSONPointerException { - if (this.refTokens.isEmpty()) { - return document; - } - Object current = document; - for (String token : this.refTokens) { - if (current instanceof JSONObject) { - current = ((JSONObject) current).opt(unescape(token)); - } else if (current instanceof JSONArray) { - current = readByIndexToken(current, token); - } else { - throw new JSONPointerException(format( - "value [%s] is not an array or object therefore its key %s cannot be resolved", current, - token)); - } - } - return current; - } - - /** - * Matches a JSONArray element by ordinal position - * @param current the JSONArray to be evaluated - * @param indexToken the array index in string form - * @return the matched object. If no matching item is found a - * @throws JSONPointerException is thrown if the index is out of bounds - */ - private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { - try { - int index = Integer.parseInt(indexToken); - JSONArray currentArr = (JSONArray) current; - if (index >= currentArr.length()) { - throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, - Integer.valueOf(currentArr.length()))); - } - try { - return currentArr.get(index); - } catch (JSONException e) { - throw new JSONPointerException("Error reading value at index position " + index, e); - } - } catch (NumberFormatException e) { - throw new JSONPointerException(format("%s is not an array index", indexToken), e); - } - } - - /** - * Returns a string representing the JSONPointer path value using string - * representation - */ - @Override - public String toString() { - StringBuilder rval = new StringBuilder(""); - for (String token: this.refTokens) { - rval.append('/').append(escape(token)); - } - return rval.toString(); - } - - /** - * Escapes path segment values to an unambiguous form. - * The escape char to be inserted is '~'. The chars to be escaped - * are ~, which maps to ~0, and /, which maps to ~1. - * @param token the JSONPointer segment value to be escaped - * @return the escaped value for the token - * - * @see rfc6901 section 3 - */ - private static String escape(String token) { - return token.replace("~", "~0") - .replace("/", "~1"); - } - - /** - * Returns a string representing the JSONPointer path value using URI - * fragment identifier representation - * @return a uri fragment string - */ - public String toURIFragment() { - try { - StringBuilder rval = new StringBuilder("#"); - for (String token : this.refTokens) { - rval.append('/').append(URLEncoder.encode(token, ENCODING)); - } - return rval.toString(); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - -} +package org.json; + +import static net.lax1dude.eaglercraft.v1_8.HString.format; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/* +Public Domain. +*/ + +/** + * A JSON Pointer is a simple query language defined for JSON documents by + * RFC 6901. + * + * In a nutshell, JSONPointer allows the user to navigate into a JSON document + * using strings, and retrieve targeted objects, like a simple form of XPATH. + * Path segments are separated by the '/' char, which signifies the root of + * the document when it appears as the first char of the string. Array + * elements are navigated using ordinals, counting from 0. JSONPointer strings + * may be extended to any arbitrary number of segments. If the navigation + * is successful, the matched item is returned. A matched item may be a + * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building + * fails, an appropriate exception is thrown. If the navigation fails to find + * a match, a JSONPointerException is thrown. + * + * @author JSON.org + * @version 2016-05-14 + */ +public class JSONPointer { + + // used for URL encoding and decoding + private static final String ENCODING = "utf-8"; + + /** + * This class allows the user to build a JSONPointer in steps, using + * exactly one segment in each step. + */ + public static class Builder { + + /** + * Constructs a new Builder object. + */ + public Builder() { + } + + // Segments for the eventual JSONPointer string + private final List refTokens = new ArrayList(); + + /** + * Creates a {@code JSONPointer} instance using the tokens previously set using the + * {@link #append(String)} method calls. + * @return a JSONPointer object + */ + public JSONPointer build() { + return new JSONPointer(this.refTokens); + } + + /** + * Adds an arbitrary token to the list of reference tokens. It can be any non-null value. + * + * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the + * argument of this method MUST NOT be escaped. If you want to query the property called + * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no + * need to escape it as {@code "a~0b"}. + * + * @param token the new token to be appended to the list + * @return {@code this} + * @throws NullPointerException if {@code token} is null + */ + public Builder append(String token) { + if (token == null) { + throw new NullPointerException("token cannot be null"); + } + this.refTokens.add(token); + return this; + } + + /** + * Adds an integer to the reference token list. Although not necessarily, mostly this token will + * denote an array index. + * + * @param arrayIndex the array index to be added to the token list + * @return {@code this} + */ + public Builder append(int arrayIndex) { + this.refTokens.add(String.valueOf(arrayIndex)); + return this; + } + } + + /** + * Static factory method for {@link Builder}. Example usage: + * + *

+     * JSONPointer pointer = JSONPointer.builder()
+     *       .append("obj")
+     *       .append("other~key").append("another/key")
+     *       .append("\"")
+     *       .append(0)
+     *       .build();
+     * 
+ * + * @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained + * {@link Builder#append(String)} calls. + */ + public static Builder builder() { + return new Builder(); + } + + // Segments for the JSONPointer string + private final List refTokens; + + /** + * Pre-parses and initializes a new {@code JSONPointer} instance. If you want to + * evaluate the same JSON Pointer on different JSON documents then it is recommended + * to keep the {@code JSONPointer} instances due to performance considerations. + * + * @param pointer the JSON String or URI Fragment representation of the JSON pointer. + * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer + */ + public JSONPointer(final String pointer) { + if (pointer == null) { + throw new NullPointerException("pointer cannot be null"); + } + if (pointer.isEmpty() || pointer.equals("#")) { + this.refTokens = Collections.emptyList(); + return; + } + String refs; + if (pointer.startsWith("#/")) { + refs = pointer.substring(2); + try { + refs = URLDecoder.decode(refs, ENCODING); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } else if (pointer.startsWith("/")) { + refs = pointer.substring(1); + } else { + throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); + } + this.refTokens = new ArrayList(); + int slashIdx = -1; + int prevSlashIdx = 0; + do { + prevSlashIdx = slashIdx + 1; + slashIdx = refs.indexOf('/', prevSlashIdx); + if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) { + // found 2 slashes in a row ( obj//next ) + // or single slash at the end of a string ( obj/test/ ) + this.refTokens.add(""); + } else if (slashIdx >= 0) { + final String token = refs.substring(prevSlashIdx, slashIdx); + this.refTokens.add(unescape(token)); + } else { + // last item after separator, or no separator at all. + final String token = refs.substring(prevSlashIdx); + this.refTokens.add(unescape(token)); + } + } while (slashIdx >= 0); + // using split does not take into account consecutive separators or "ending nulls" + //for (String token : refs.split("/")) { + // this.refTokens.add(unescape(token)); + //} + } + + /** + * Constructs a new JSONPointer instance with the provided list of reference tokens. + * + * @param refTokens A list of strings representing the reference tokens for the JSON Pointer. + * Each token identifies a step in the path to the targeted value. + */ + public JSONPointer(List refTokens) { + this.refTokens = new ArrayList(refTokens); + } + + /** + * @see rfc6901 section 3 + */ + private static String unescape(String token) { + return token.replace("~1", "/").replace("~0", "~"); + } + + /** + * Evaluates this JSON Pointer on the given {@code document}. The {@code document} + * is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty + * JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the + * returned value will be {@code document} itself. + * + * @param document the JSON document which should be the subject of querying. + * @return the result of the evaluation + * @throws JSONPointerException if an error occurs during evaluation + */ + public Object queryFrom(Object document) throws JSONPointerException { + if (this.refTokens.isEmpty()) { + return document; + } + Object current = document; + for (String token : this.refTokens) { + if (current instanceof JSONObject) { + current = ((JSONObject) current).opt(unescape(token)); + } else if (current instanceof JSONArray) { + current = readByIndexToken(current, token); + } else { + throw new JSONPointerException(format( + "value [%s] is not an array or object therefore its key %s cannot be resolved", current, + token)); + } + } + return current; + } + + /** + * Matches a JSONArray element by ordinal position + * @param current the JSONArray to be evaluated + * @param indexToken the array index in string form + * @return the matched object. If no matching item is found a + * @throws JSONPointerException is thrown if the index is out of bounds + */ + private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { + try { + int index = Integer.parseInt(indexToken); + JSONArray currentArr = (JSONArray) current; + if (index >= currentArr.length()) { + throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, + Integer.valueOf(currentArr.length()))); + } + try { + return currentArr.get(index); + } catch (JSONException e) { + throw new JSONPointerException("Error reading value at index position " + index, e); + } + } catch (NumberFormatException e) { + throw new JSONPointerException(format("%s is not an array index", indexToken), e); + } + } + + /** + * Returns a string representing the JSONPointer path value using string + * representation + */ + @Override + public String toString() { + StringBuilder rval = new StringBuilder(""); + for (String token: this.refTokens) { + rval.append('/').append(escape(token)); + } + return rval.toString(); + } + + /** + * Escapes path segment values to an unambiguous form. + * The escape char to be inserted is '~'. The chars to be escaped + * are ~, which maps to ~0, and /, which maps to ~1. + * @param token the JSONPointer segment value to be escaped + * @return the escaped value for the token + * + * @see rfc6901 section 3 + */ + private static String escape(String token) { + return token.replace("~", "~0") + .replace("/", "~1"); + } + + /** + * Returns a string representing the JSONPointer path value using URI + * fragment identifier representation + * @return a uri fragment string + */ + public String toURIFragment() { + try { + StringBuilder rval = new StringBuilder("#"); + for (String token : this.refTokens) { + rval.append('/').append(URLEncoder.encode(token, ENCODING)); + } + return rval.toString(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/org/json/JSONPointerException.java b/src/main/java/org/json/JSONPointerException.java index a0e128c..3b23c7d 100644 --- a/src/main/java/org/json/JSONPointerException.java +++ b/src/main/java/org/json/JSONPointerException.java @@ -1,25 +1,36 @@ -package org.json; - -/* -Public Domain. -*/ - -/** - * The JSONPointerException is thrown by {@link JSONPointer} if an error occurs - * during evaluating a pointer. - * - * @author JSON.org - * @version 2016-05-13 - */ -public class JSONPointerException extends JSONException { - private static final long serialVersionUID = 8872944667561856751L; - - public JSONPointerException(String message) { - super(message); - } - - public JSONPointerException(String message, Throwable cause) { - super(message, cause); - } - -} +package org.json; + +/* +Public Domain. +*/ + +/** + * The JSONPointerException is thrown by {@link JSONPointer} if an error occurs + * during evaluating a pointer. + * + * @author JSON.org + * @version 2016-05-13 + */ +public class JSONPointerException extends JSONException { + private static final long serialVersionUID = 8872944667561856751L; + + /** + * Constructs a new JSONPointerException with the specified error message. + * + * @param message The detail message describing the reason for the exception. + */ + public JSONPointerException(String message) { + super(message); + } + + /** + * Constructs a new JSONPointerException with the specified error message and cause. + * + * @param message The detail message describing the reason for the exception. + * @param cause The cause of the exception. + */ + public JSONPointerException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/org/json/JSONPropertyIgnore.java b/src/main/java/org/json/JSONPropertyIgnore.java index 7c5fa53..d3a5bc5 100644 --- a/src/main/java/org/json/JSONPropertyIgnore.java +++ b/src/main/java/org/json/JSONPropertyIgnore.java @@ -11,13 +11,13 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Documented -@Retention(RUNTIME) -@Target({METHOD}) /** * Use this annotation on a getter method to override the Bean name * parser for Bean -> JSONObject mapping. If this annotation is * present at any level in the class hierarchy, then the method will * not be serialized from the bean into the JSONObject. */ +@Documented +@Retention(RUNTIME) +@Target({METHOD}) public @interface JSONPropertyIgnore { } diff --git a/src/main/java/org/json/JSONPropertyName.java b/src/main/java/org/json/JSONPropertyName.java index a66f4ad..0e4123f 100644 --- a/src/main/java/org/json/JSONPropertyName.java +++ b/src/main/java/org/json/JSONPropertyName.java @@ -11,16 +11,17 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Documented -@Retention(RUNTIME) -@Target({METHOD}) /** * Use this annotation on a getter method to override the Bean name * parser for Bean -> JSONObject mapping. A value set to empty string "" * will have the Bean parser fall back to the default field name processing. */ +@Documented +@Retention(RUNTIME) +@Target({METHOD}) public @interface JSONPropertyName { /** + * The value of the JSON property. * @return The name of the property as to be used in the JSON Object. */ String value(); diff --git a/src/main/java/org/json/JSONString.java b/src/main/java/org/json/JSONString.java index cd8d184..ee82720 100644 --- a/src/main/java/org/json/JSONString.java +++ b/src/main/java/org/json/JSONString.java @@ -21,3 +21,4 @@ public interface JSONString { */ public String toJSONString(); } + diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java index e9ffff6..225e9ee 100644 --- a/src/main/java/org/json/JSONTokener.java +++ b/src/main/java/org/json/JSONTokener.java @@ -1,525 +1,538 @@ -package org.json; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; - -/* -Public Domain. - */ - -/** - * A JSONTokener takes a source string and extracts characters and tokens from - * it. It is used by the JSONObject and JSONArray constructors to parse - * JSON source strings. - * @author JSON.org - * @version 2014-05-03 - */ -public class JSONTokener { - /** current read character position on the current line. */ - private long character; - /** flag to indicate if the end of the input has been found. */ - private boolean eof; - /** current read index of the input. */ - private long index; - /** current line of the input. */ - private long line; - /** previous character read from the input. */ - private char previous; - /** Reader for the input. */ - private final Reader reader; - /** flag to indicate that a previous character was requested. */ - private boolean usePrevious; - /** the number of characters read in the previous line. */ - private long characterPreviousLine; - - - /** - * Construct a JSONTokener from a Reader. The caller must close the Reader. - * - * @param reader A reader. - */ - public JSONTokener(Reader reader) { - this.reader = reader.markSupported() - ? reader - : new BufferedReader(reader); - this.eof = false; - this.usePrevious = false; - this.previous = 0; - this.index = 0; - this.character = 1; - this.characterPreviousLine = 0; - this.line = 1; - } - - - /** - * Construct a JSONTokener from an InputStream. The caller must close the input stream. - * @param inputStream The source. - */ - public JSONTokener(InputStream inputStream) { - this(new InputStreamReader(inputStream)); - } - - - /** - * Construct a JSONTokener from a string. - * - * @param s A source string. - */ - public JSONTokener(String s) { - this(new StringReader(s)); - } - - - /** - * Back up one character. This provides a sort of lookahead capability, - * so that you can test for a digit or letter before attempting to parse - * the next number or identifier. - * @throws JSONException Thrown if trying to step back more than 1 step - * or if already at the start of the string - */ - public void back() throws JSONException { - if (this.usePrevious || this.index <= 0) { - throw new JSONException("Stepping back two steps is not supported"); - } - this.decrementIndexes(); - this.usePrevious = true; - this.eof = false; - } - - /** - * Decrements the indexes for the {@link #back()} method based on the previous character read. - */ - private void decrementIndexes() { - this.index--; - if(this.previous=='\r' || this.previous == '\n') { - this.line--; - this.character=this.characterPreviousLine ; - } else if(this.character > 0){ - this.character--; - } - } - - /** - * Get the hex value of a character (base16). - * @param c A character between '0' and '9' or between 'A' and 'F' or - * between 'a' and 'f'. - * @return An int between 0 and 15, or -1 if c was not a hex digit. - */ - public static int dehexchar(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'A' && c <= 'F') { - return c - ('A' - 10); - } - if (c >= 'a' && c <= 'f') { - return c - ('a' - 10); - } - return -1; - } - - /** - * Checks if the end of the input has been reached. - * - * @return true if at the end of the file and we didn't step back - */ - public boolean end() { - return this.eof && !this.usePrevious; - } - - - /** - * Determine if the source string still contains characters that next() - * can consume. - * @return true if not yet at the end of the source. - * @throws JSONException thrown if there is an error stepping forward - * or backward while checking for more data. - */ - public boolean more() throws JSONException { - if(this.usePrevious) { - return true; - } - try { - this.reader.mark(1); - } catch (IOException e) { - throw new JSONException("Unable to preserve stream position", e); - } - try { - // -1 is EOF, but next() can not consume the null character '\0' - if(this.reader.read() <= 0) { - this.eof = true; - return false; - } - this.reader.reset(); - } catch (IOException e) { - throw new JSONException("Unable to read the next character from the stream", e); - } - return true; - } - - - /** - * Get the next character in the source string. - * - * @return The next character, or 0 if past the end of the source string. - * @throws JSONException Thrown if there is an error reading the source string. - */ - public char next() throws JSONException { - int c; - if (this.usePrevious) { - this.usePrevious = false; - c = this.previous; - } else { - try { - c = this.reader.read(); - } catch (IOException exception) { - throw new JSONException(exception); - } - } - if (c <= 0) { // End of stream - this.eof = true; - return 0; - } - this.incrementIndexes(c); - this.previous = (char) c; - return this.previous; - } - - /** - * Get the last character read from the input or '\0' if nothing has been read yet. - * @return the last character read from the input. - */ - protected char getPrevious() { return this.previous;} - - /** - * Increments the internal indexes according to the previous character - * read and the character passed as the current character. - * @param c the current character read. - */ - private void incrementIndexes(int c) { - if(c > 0) { - this.index++; - if(c=='\r') { - this.line++; - this.characterPreviousLine = this.character; - this.character=0; - }else if (c=='\n') { - if(this.previous != '\r') { - this.line++; - this.characterPreviousLine = this.character; - } - this.character=0; - } else { - this.character++; - } - } - } - - /** - * Consume the next character, and check that it matches a specified - * character. - * @param c The character to match. - * @return The character. - * @throws JSONException if the character does not match. - */ - public char next(char c) throws JSONException { - char n = this.next(); - if (n != c) { - if(n > 0) { - throw this.syntaxError("Expected '" + c + "' and instead saw '" + - n + "'"); - } - throw this.syntaxError("Expected '" + c + "' and instead saw ''"); - } - return n; - } - - - /** - * Get the next n characters. - * - * @param n The number of characters to take. - * @return A string of n characters. - * @throws JSONException - * Substring bounds error if there are not - * n characters remaining in the source string. - */ - public String next(int n) throws JSONException { - if (n == 0) { - return ""; - } - - char[] chars = new char[n]; - int pos = 0; - - while (pos < n) { - chars[pos] = this.next(); - if (this.end()) { - throw this.syntaxError("Substring bounds error"); - } - pos += 1; - } - return new String(chars); - } - - - /** - * Get the next char in the string, skipping whitespace. - * @throws JSONException Thrown if there is an error reading the source string. - * @return A character, or 0 if there are no more characters. - */ - public char nextClean() throws JSONException { - for (;;) { - char c = this.next(); - if (c == 0 || c > ' ') { - return c; - } - } - } - - - /** - * Return the characters up to the next close quote character. - * Backslash processing is done. The formal JSON format does not - * allow strings in single quotes, but an implementation is allowed to - * accept them. - * @param quote The quoting character, either - * " (double quote) or - * ' (single quote). - * @return A String. - * @throws JSONException Unterminated string. - */ - public String nextString(char quote) throws JSONException { - char c; - StringBuilder sb = new StringBuilder(); - for (;;) { - c = this.next(); - switch (c) { - case 0: - throw this.syntaxError("Unterminated string"); - case '\r': - break; - case '\\': - c = this.next(); - switch (c) { - case 'b': - sb.append('\b'); - break; - case 't': - sb.append('\t'); - break; - case 'n': - sb.append('\n'); - break; - case 'f': - sb.append('\f'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - try { - sb.append((char)Integer.parseInt(this.next(4), 16)); - } catch (NumberFormatException e) { - throw this.syntaxError("Illegal escape.", e); - } - break; - case '"': - case '\'': - case '\\': - case '/': - sb.append(c); - break; - default: - throw this.syntaxError("Illegal escape."); - } - break; - default: - if (c == quote) { - return sb.toString(); - } - sb.append(c); - } - } - } - - - /** - * Get the text up but not including the specified character or the - * end of line, whichever comes first. - * @param delimiter A delimiter character. - * @return A string. - * @throws JSONException Thrown if there is an error while searching - * for the delimiter - */ - public String nextTo(char delimiter) throws JSONException { - StringBuilder sb = new StringBuilder(); - for (;;) { - char c = this.next(); - if (c == delimiter || c == 0 || c == '\n' || c == '\r') { - if (c != 0) { - this.back(); - } - return sb.toString().trim(); - } - sb.append(c); - } - } - - - /** - * Get the text up but not including one of the specified delimiter - * characters or the end of line, whichever comes first. - * @param delimiters A set of delimiter characters. - * @return A string, trimmed. - * @throws JSONException Thrown if there is an error while searching - * for the delimiter - */ - public String nextTo(String delimiters) throws JSONException { - char c; - StringBuilder sb = new StringBuilder(); - for (;;) { - c = this.next(); - if (delimiters.indexOf(c) >= 0 || c == 0 || - c == '\n' || c == '\r') { - if (c != 0) { - this.back(); - } - return sb.toString().trim(); - } - sb.append(c); - } - } - - - /** - * Get the next value. The value can be a Boolean, Double, Integer, - * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. - * @throws JSONException If syntax error. - * - * @return An object. - */ - public Object nextValue() throws JSONException { - char c = this.nextClean(); - String string; - - switch (c) { - case '"': - case '\'': - return this.nextString(c); - case '{': - this.back(); - try { - return new JSONObject(this); - } catch (StackOverflowError e) { - throw new JSONException("JSON Array or Object depth too large to process.", e); - } - case '[': - this.back(); - try { - return new JSONArray(this); - } catch (StackOverflowError e) { - throw new JSONException("JSON Array or Object depth too large to process.", e); - } - } - - /* - * Handle unquoted text. This could be the values true, false, or - * null, or it can be a number. An implementation (such as this one) - * is allowed to also accept non-standard forms. - * - * Accumulate characters until we reach the end of the text or a - * formatting character. - */ - - StringBuilder sb = new StringBuilder(); - while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { - sb.append(c); - c = this.next(); - } - if (!this.eof) { - this.back(); - } - - string = sb.toString().trim(); - if ("".equals(string)) { - throw this.syntaxError("Missing value"); - } - return JSONObject.stringToValue(string); - } - - - /** - * Skip characters until the next character is the requested character. - * If the requested character is not found, no characters are skipped. - * @param to A character to skip to. - * @return The requested character, or zero if the requested character - * is not found. - * @throws JSONException Thrown if there is an error while searching - * for the to character - */ - public char skipTo(char to) throws JSONException { - char c; - try { - long startIndex = this.index; - long startCharacter = this.character; - long startLine = this.line; - this.reader.mark(1000000); - do { - c = this.next(); - if (c == 0) { - // in some readers, reset() may throw an exception if - // the remaining portion of the input is greater than - // the mark size (1,000,000 above). - this.reader.reset(); - this.index = startIndex; - this.character = startCharacter; - this.line = startLine; - return 0; - } - } while (c != to); - this.reader.mark(1); - } catch (IOException exception) { - throw new JSONException(exception); - } - this.back(); - return c; - } - - /** - * Make a JSONException to signal a syntax error. - * - * @param message The error message. - * @return A JSONException object, suitable for throwing - */ - public JSONException syntaxError(String message) { - return new JSONException(message + this.toString()); - } - - /** - * Make a JSONException to signal a syntax error. - * - * @param message The error message. - * @param causedBy The throwable that caused the error. - * @return A JSONException object, suitable for throwing - */ - public JSONException syntaxError(String message, Throwable causedBy) { - return new JSONException(message + this.toString(), causedBy); - } - - /** - * Make a printable string of this JSONTokener. - * - * @return " at {index} [character {character} line {line}]" - */ - @Override - public String toString() { - return " at " + this.index + " [character " + this.character + " line " + - this.line + "]"; - } -} +package org.json; + +import java.io.*; +import java.nio.charset.Charset; + +/* +Public Domain. + */ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from + * it. It is used by the JSONObject and JSONArray constructors to parse + * JSON source strings. + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONTokener { + /** current read character position on the current line. */ + private long character; + /** flag to indicate if the end of the input has been found. */ + private boolean eof; + /** current read index of the input. */ + private long index; + /** current line of the input. */ + private long line; + /** previous character read from the input. */ + private char previous; + /** Reader for the input. */ + private final Reader reader; + /** flag to indicate that a previous character was requested. */ + private boolean usePrevious; + /** the number of characters read in the previous line. */ + private long characterPreviousLine; + + + /** + * Construct a JSONTokener from a Reader. The caller must close the Reader. + * + * @param reader A reader. + */ + public JSONTokener(Reader reader) { + this.reader = reader.markSupported() + ? reader + : new BufferedReader(reader); + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.characterPreviousLine = 0; + this.line = 1; + } + + + /** + * Construct a JSONTokener from an InputStream. The caller must close the input stream. + * @param inputStream The source. + */ + public JSONTokener(InputStream inputStream) { + this(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); + } + + + /** + * Construct a JSONTokener from a string. + * + * @param s A source string. + */ + public JSONTokener(String s) { + this(new StringReader(s)); + } + + + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + * @throws JSONException Thrown if trying to step back more than 1 step + * or if already at the start of the string + */ + public void back() throws JSONException { + if (this.usePrevious || this.index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.decrementIndexes(); + this.usePrevious = true; + this.eof = false; + } + + /** + * Decrements the indexes for the {@link #back()} method based on the previous character read. + */ + private void decrementIndexes() { + this.index--; + if(this.previous=='\r' || this.previous == '\n') { + this.line--; + this.character=this.characterPreviousLine ; + } else if(this.character > 0){ + this.character--; + } + } + + /** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + /** + * Checks if the end of the input has been reached. + * + * @return true if at the end of the file and we didn't step back + */ + public boolean end() { + return this.eof && !this.usePrevious; + } + + + /** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. + * @throws JSONException thrown if there is an error stepping forward + * or backward while checking for more data. + */ + public boolean more() throws JSONException { + if(this.usePrevious) { + return true; + } + try { + this.reader.mark(1); + } catch (IOException e) { + throw new JSONException("Unable to preserve stream position", e); + } + try { + // -1 is EOF, but next() can not consume the null character '\0' + if(this.reader.read() <= 0) { + this.eof = true; + return false; + } + this.reader.reset(); + } catch (IOException e) { + throw new JSONException("Unable to read the next character from the stream", e); + } + return true; + } + + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + * @throws JSONException Thrown if there is an error reading the source string. + */ + public char next() throws JSONException { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } catch (IOException exception) { + throw new JSONException(exception); + } + } + if (c <= 0) { // End of stream + this.eof = true; + return 0; + } + this.incrementIndexes(c); + this.previous = (char) c; + return this.previous; + } + + /** + * Get the last character read from the input or '\0' if nothing has been read yet. + * @return the last character read from the input. + */ + protected char getPrevious() { return this.previous;} + + /** + * Increments the internal indexes according to the previous character + * read and the character passed as the current character. + * @param c the current character read. + */ + private void incrementIndexes(int c) { + if(c > 0) { + this.index++; + if(c=='\r') { + this.line++; + this.characterPreviousLine = this.character; + this.character=0; + }else if (c=='\n') { + if(this.previous != '\r') { + this.line++; + this.characterPreviousLine = this.character; + } + this.character=0; + } else { + this.character++; + } + } + } + + /** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws JSONException if the character does not match. + */ + public char next(char c) throws JSONException { + char n = this.next(); + if (n != c) { + if(n > 0) { + throw this.syntaxError("Expected '" + c + "' and instead saw '" + + n + "'"); + } + throw this.syntaxError("Expected '" + c + "' and instead saw ''"); + } + return n; + } + + + /** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not + * n characters remaining in the source string. + */ + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } + + char[] chars = new char[n]; + int pos = 0; + + while (pos < n) { + chars[pos] = this.next(); + if (this.end()) { + throw this.syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(chars); + } + + + /** + * Get the next char in the string, skipping whitespace. + * @throws JSONException Thrown if there is an error reading the source string. + * @return A character, or 0 if there are no more characters. + */ + public char nextClean() throws JSONException { + for (;;) { + char c = this.next(); + if (c == 0 || c > ' ') { + return c; + } + } + } + + + /** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException Unterminated string. + */ + public String nextString(char quote) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + for (;;) { + c = this.next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string"); + case '\\': + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + try { + sb.append((char)Integer.parseInt(this.next(4), 16)); + } catch (NumberFormatException e) { + throw this.syntaxError("Illegal escape.", e); + } + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + } + + + /** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param delimiter A delimiter character. + * @return A string. + * @throws JSONException Thrown if there is an error while searching + * for the delimiter + */ + public String nextTo(char delimiter) throws JSONException { + StringBuilder sb = new StringBuilder(); + for (;;) { + char c = this.next(); + if (c == delimiter || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the text up but not including one of the specified delimiter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. + * @throws JSONException Thrown if there is an error while searching + * for the delimiter + */ + public String nextTo(String delimiters) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + for (;;) { + c = this.next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || + c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @throws JSONException If syntax error. + * + * @return An object. + */ + public Object nextValue() throws JSONException { + char c = this.nextClean(); + switch (c) { + case '{': + this.back(); + try { + return new JSONObject(this); + } catch (StackOverflowError e) { + throw new JSONException("JSON Array or Object depth too large to process.", e); + } + case '[': + this.back(); + try { + return new JSONArray(this); + } catch (StackOverflowError e) { + throw new JSONException("JSON Array or Object depth too large to process.", e); + } + } + return nextSimpleValue(c); + } + + Object nextSimpleValue(char c) { + String string; + + switch (c) { + case '"': + case '\'': + return this.nextString(c); + } + + /* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + */ + + StringBuilder sb = new StringBuilder(); + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = this.next(); + } + if (!this.eof) { + this.back(); + } + + string = sb.toString().trim(); + if ("".equals(string)) { + throw this.syntaxError("Missing value"); + } + return JSONObject.stringToValue(string); + } + + + /** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + * @throws JSONException Thrown if there is an error while searching + * for the to character + */ + public char skipTo(char to) throws JSONException { + char c; + try { + long startIndex = this.index; + long startCharacter = this.character; + long startLine = this.line; + this.reader.mark(1000000); + do { + c = this.next(); + if (c == 0) { + // in some readers, reset() may throw an exception if + // the remaining portion of the input is greater than + // the mark size (1,000,000 above). + this.reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return 0; + } + } while (c != to); + this.reader.mark(1); + } catch (IOException exception) { + throw new JSONException(exception); + } + this.back(); + return c; + } + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message) { + return new JSONException(message + this.toString()); + } + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @param causedBy The throwable that caused the error. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message, Throwable causedBy) { + return new JSONException(message + this.toString(), causedBy); + } + + /** + * Make a printable string of this JSONTokener. + * + * @return " at {index} [character {character} line {line}]" + */ + @Override + public String toString() { + return " at " + this.index + " [character " + this.character + " line " + + this.line + "]"; + } + + /** + * Closes the underlying reader, releasing any resources associated with it. + * + * @throws IOException If an I/O error occurs while closing the reader. + */ + public void close() throws IOException { + if(reader!=null){ + reader.close(); + } + } +} diff --git a/src/main/java/org/json/ParserConfiguration.java b/src/main/java/org/json/ParserConfiguration.java new file mode 100755 index 0000000..17d565a --- /dev/null +++ b/src/main/java/org/json/ParserConfiguration.java @@ -0,0 +1,126 @@ +package org.json; +/* +Public Domain. +*/ + +/** + * Configuration base object for parsers. The configuration is immutable. + */ +@SuppressWarnings({""}) +public class ParserConfiguration { + /** + * Used to indicate there's no defined limit to the maximum nesting depth when parsing a document. + */ + public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1; + + /** + * The default maximum nesting depth when parsing a document. + */ + public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; + + /** + * Specifies if values should be kept as strings (true), or if + * they should try to be guessed into JSON values (numeric, boolean, string) + */ + protected boolean keepStrings; + + /** + * The maximum nesting depth when parsing a document. + */ + protected int maxNestingDepth; + + /** + * Constructs a new ParserConfiguration with default settings. + */ + public ParserConfiguration() { + this.keepStrings = false; + this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH; + } + + /** + * Constructs a new ParserConfiguration with the specified settings. + * + * @param keepStrings A boolean indicating whether to preserve strings during parsing. + * @param maxNestingDepth An integer representing the maximum allowed nesting depth. + */ + protected ParserConfiguration(final boolean keepStrings, final int maxNestingDepth) { + this.keepStrings = keepStrings; + this.maxNestingDepth = maxNestingDepth; + } + + /** + * Provides a new instance of the same configuration. + */ + @Override + protected ParserConfiguration clone() { + // future modifications to this method should always ensure a "deep" + // clone in the case of collections. i.e. if a Map is added as a configuration + // item, a new map instance should be created and if possible each value in the + // map should be cloned as well. If the values of the map are known to also + // be immutable, then a shallow clone of the map is acceptable. + return new ParserConfiguration( + this.keepStrings, + this.maxNestingDepth + ); + } + + /** + * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if + * they should try to be guessed into JSON values (numeric, boolean, string) + * + * @return The keepStrings configuration value. + */ + public boolean isKeepStrings() { + return this.keepStrings; + } + + /** + * When parsing the XML into JSONML, specifies if values should be kept as strings (true), or if + * they should try to be guessed into JSON values (numeric, boolean, string) + * + * @param newVal + * new value to use for the keepStrings configuration option. + * @param the type of the configuration object + * + * @return The existing configuration will not be modified. A new configuration is returned. + */ + @SuppressWarnings("unchecked") + public T withKeepStrings(final boolean newVal) { + T newConfig = (T)this.clone(); + newConfig.keepStrings = newVal; + return newConfig; + } + + /** + * The maximum nesting depth that the parser will descend before throwing an exception + * when parsing the XML into JSONML. + * @return the maximum nesting depth set for this configuration + */ + public int getMaxNestingDepth() { + return maxNestingDepth; + } + + /** + * Defines the maximum nesting depth that the parser will descend before throwing an exception + * when parsing the XML into JSONML. The default max nesting depth is 512, which means the parser + * will throw a JsonException if the maximum depth is reached. + * Using any negative value as a parameter is equivalent to setting no limit to the nesting depth, + * which means the parses will go as deep as the maximum call stack size allows. + * @param maxNestingDepth the maximum nesting depth allowed to the XML parser + * @param the type of the configuration object + * + * @return The existing configuration will not be modified. A new configuration is returned. + */ + @SuppressWarnings("unchecked") + public T withMaxNestingDepth(int maxNestingDepth) { + T newConfig = (T)this.clone(); + + if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) { + newConfig.maxNestingDepth = maxNestingDepth; + } else { + newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH; + } + + return newConfig; + } +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePacketInputBuffer.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePacketInputBuffer.java new file mode 100755 index 0000000..bc95f90 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePacketInputBuffer.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol; + +import java.io.DataInput; +import java.io.IOException; +import java.io.InputStream; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface GamePacketInputBuffer extends DataInput { + + void skipAllBytes(int n) throws IOException; + + int readVarInt() throws IOException; + + long readVarLong() throws IOException; + + String readStringMC(int maxLen) throws IOException; + + String readStringEaglerASCII8() throws IOException; + + String readStringEaglerASCII16() throws IOException; + + byte[] readByteArrayMC() throws IOException; + + int available() throws IOException; + + InputStream stream(); + + byte[] toByteArray() throws IOException; + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePacketOutputBuffer.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePacketOutputBuffer.java new file mode 100755 index 0000000..0f31767 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePacketOutputBuffer.java @@ -0,0 +1,61 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface GamePacketOutputBuffer extends DataOutput { + + void writeVarInt(int i) throws IOException; + + void writeVarLong(long i) throws IOException; + + void writeStringMC(String str) throws IOException; + + void writeStringEaglerASCII8(String str) throws IOException; + + void writeStringEaglerASCII16(String str) throws IOException; + + void writeByteArrayMC(byte[] bytes) throws IOException; + + OutputStream stream(); + + public static int getVarIntSize(int input) { + for (int i = 1; i < 5; ++i) { + if ((input & -1 << i * 7) == 0) { + return i; + } + } + + return 5; + } + + public static int getVarLongSize(long input) { + for (int i = 1; i < 9; ++i) { + if ((input & -1 << i * 7) == 0) { + return i; + } + } + + return 9; + } + + public static int getArrayMCSize(int len) { + return getVarIntSize(len) + len; + } +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePluginMessageConstants.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePluginMessageConstants.java new file mode 100755 index 0000000..9bbf3a9 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePluginMessageConstants.java @@ -0,0 +1,52 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GamePluginMessageConstants { + + public static final String V3_SKIN_CHANNEL = "EAG|Skins-1.8"; + public static final String V3_CAPE_CHANNEL = "EAG|Capes-1.8"; + public static final String V3_VOICE_CHANNEL = "EAG|Voice-1.8"; + public static final String V3_UPDATE_CHANNEL = "EAG|UpdateCert-1.8"; + public static final String V3_FNAW_EN_CHANNEL = "EAG|FNAWSEn-1.8"; + + public static final String V4_CHANNEL = "EAG|1.8"; + + public static final int CLIENT_TO_SERVER = 0; + public static final int SERVER_TO_CLIENT = 1; + + public static String getDirectionString(int dir) { + switch(dir) { + case CLIENT_TO_SERVER: + return "CLIENT_TO_SERVER"; + case SERVER_TO_CLIENT: + return "SERVER_TO_CLIENT"; + default: + return "UNKNOWN"; + } + } + + public static int oppositeDirection(int dir) { + switch(dir) { + case CLIENT_TO_SERVER: + return SERVER_TO_CLIENT; + case SERVER_TO_CLIENT: + return CLIENT_TO_SERVER; + default: + throw new IllegalArgumentException("Invalid direction: " + dir); + } + } +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePluginMessageProtocol.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePluginMessageProtocol.java new file mode 100755 index 0000000..95c89fb --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/GamePluginMessageProtocol.java @@ -0,0 +1,230 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; + +import static net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum GamePluginMessageProtocol { + V3(3, + define(V3_SKIN_CHANNEL, 0x03, CLIENT_TO_SERVER, CPacketGetOtherSkinEAG.class), + define(V3_SKIN_CHANNEL, 0x04, SERVER_TO_CLIENT, SPacketOtherSkinPresetEAG.class), + define(V3_SKIN_CHANNEL, 0x05, SERVER_TO_CLIENT, SPacketOtherSkinCustomV3EAG.class), + define(V3_SKIN_CHANNEL, 0x06, CLIENT_TO_SERVER, CPacketGetSkinByURLEAG.class), + define(V3_SKIN_CHANNEL, 0x07, CLIENT_TO_SERVER, CPacketInstallSkinSPEAG.class), + define(V3_CAPE_CHANNEL, 0x03, CLIENT_TO_SERVER, CPacketGetOtherCapeEAG.class), + define(V3_CAPE_CHANNEL, 0x04, SERVER_TO_CLIENT, SPacketOtherCapePresetEAG.class), + define(V3_CAPE_CHANNEL, 0x05, SERVER_TO_CLIENT, SPacketOtherCapeCustomEAG.class), + define(V3_VOICE_CHANNEL, 0x00, SERVER_TO_CLIENT, SPacketVoiceSignalAllowedEAG.class), + define(V3_VOICE_CHANNEL, 0x00, CLIENT_TO_SERVER, CPacketVoiceSignalRequestEAG.class), + define(V3_VOICE_CHANNEL, 0x01, CLIENT_TO_SERVER, CPacketVoiceSignalConnectEAG.class), + define(V3_VOICE_CHANNEL, 0x01, SERVER_TO_CLIENT, SPacketVoiceSignalConnectV3EAG.class), + define(V3_VOICE_CHANNEL, 0x02, CLIENT_TO_SERVER, CPacketVoiceSignalDisconnectV3EAG.class), + define(V3_VOICE_CHANNEL, 0x02, SERVER_TO_CLIENT, SPacketVoiceSignalDisconnectPeerEAG.class), + define(V3_VOICE_CHANNEL, 0x03, CLIENT_TO_SERVER, CPacketVoiceSignalICEEAG.class), + define(V3_VOICE_CHANNEL, 0x03, SERVER_TO_CLIENT, SPacketVoiceSignalICEEAG.class), + define(V3_VOICE_CHANNEL, 0x04, CLIENT_TO_SERVER, CPacketVoiceSignalDescEAG.class), + define(V3_VOICE_CHANNEL, 0x04, SERVER_TO_CLIENT, SPacketVoiceSignalDescEAG.class), + define(V3_VOICE_CHANNEL, 0x05, SERVER_TO_CLIENT, SPacketVoiceSignalGlobalEAG.class), + define(V3_UPDATE_CHANNEL, -1, SERVER_TO_CLIENT, SPacketUpdateCertEAG.class), + define(V3_FNAW_EN_CHANNEL, -1, SERVER_TO_CLIENT, SPacketEnableFNAWSkinsEAG.class) + ), V4(4, + define(V4_CHANNEL, 0x01, CLIENT_TO_SERVER, CPacketGetOtherSkinEAG.class), + define(V4_CHANNEL, 0x02, SERVER_TO_CLIENT, SPacketOtherSkinPresetEAG.class), + define(V4_CHANNEL, 0x03, SERVER_TO_CLIENT, SPacketOtherSkinCustomV4EAG.class), + define(V4_CHANNEL, 0x04, CLIENT_TO_SERVER, CPacketGetSkinByURLEAG.class), + define(V4_CHANNEL, 0x05, CLIENT_TO_SERVER, CPacketInstallSkinSPEAG.class), + define(V4_CHANNEL, 0x06, CLIENT_TO_SERVER, CPacketGetOtherCapeEAG.class), + define(V4_CHANNEL, 0x07, SERVER_TO_CLIENT, SPacketOtherCapePresetEAG.class), + define(V4_CHANNEL, 0x08, SERVER_TO_CLIENT, SPacketOtherCapeCustomEAG.class), + define(V4_CHANNEL, 0x09, SERVER_TO_CLIENT, SPacketVoiceSignalAllowedEAG.class), + define(V4_CHANNEL, 0x0A, CLIENT_TO_SERVER, CPacketVoiceSignalRequestEAG.class), + define(V4_CHANNEL, 0x0B, CLIENT_TO_SERVER, CPacketVoiceSignalConnectEAG.class), + define(V4_CHANNEL, 0x0C, SERVER_TO_CLIENT, SPacketVoiceSignalConnectV4EAG.class), + define(V4_CHANNEL, 0x0D, SERVER_TO_CLIENT, SPacketVoiceSignalConnectAnnounceV4EAG.class), + define(V4_CHANNEL, 0x0E, CLIENT_TO_SERVER, CPacketVoiceSignalDisconnectV4EAG.class), + define(V4_CHANNEL, 0x0F, CLIENT_TO_SERVER, CPacketVoiceSignalDisconnectPeerV4EAG.class), + define(V4_CHANNEL, 0x10, SERVER_TO_CLIENT, SPacketVoiceSignalDisconnectPeerEAG.class), + define(V4_CHANNEL, 0x11, CLIENT_TO_SERVER, CPacketVoiceSignalICEEAG.class), + define(V4_CHANNEL, 0x12, SERVER_TO_CLIENT, SPacketVoiceSignalICEEAG.class), + define(V4_CHANNEL, 0x13, CLIENT_TO_SERVER, CPacketVoiceSignalDescEAG.class), + define(V4_CHANNEL, 0x14, SERVER_TO_CLIENT, SPacketVoiceSignalDescEAG.class), + define(V4_CHANNEL, 0x15, SERVER_TO_CLIENT, SPacketVoiceSignalGlobalEAG.class), + define(V4_CHANNEL, 0x16, SERVER_TO_CLIENT, SPacketUpdateCertEAG.class), + define(V4_CHANNEL, 0x17, SERVER_TO_CLIENT, SPacketEnableFNAWSkinsEAG.class), + define(V4_CHANNEL, 0x18, SERVER_TO_CLIENT, SPacketForceClientSkinPresetV4EAG.class), + define(V4_CHANNEL, 0x19, SERVER_TO_CLIENT, SPacketForceClientSkinCustomV4EAG.class), + define(V4_CHANNEL, 0x1A, SERVER_TO_CLIENT, SPacketSetServerCookieV4EAG.class), + define(V4_CHANNEL, 0x1B, SERVER_TO_CLIENT, SPacketRedirectClientV4EAG.class), + define(V4_CHANNEL, 0x1C, CLIENT_TO_SERVER, CPacketGetOtherClientUUIDV4EAG.class), + define(V4_CHANNEL, 0x1D, SERVER_TO_CLIENT, SPacketOtherPlayerClientUUIDV4EAG.class), + define(V4_CHANNEL, 0x1E, SERVER_TO_CLIENT, SPacketForceClientCapePresetV4EAG.class), + define(V4_CHANNEL, 0x1F, SERVER_TO_CLIENT, SPacketForceClientCapeCustomV4EAG.class), + define(V4_CHANNEL, 0x20, SERVER_TO_CLIENT, SPacketInvalidatePlayerCacheV4EAG.class), + define(V4_CHANNEL, 0x21, SERVER_TO_CLIENT, SPacketUnforceClientV4EAG.class), + define(V4_CHANNEL, 0x22, SERVER_TO_CLIENT, SPacketCustomizePauseMenuV4EAG.class), + define(V4_CHANNEL, 0x23, CLIENT_TO_SERVER, CPacketRequestServerInfoV4EAG.class), + define(V4_CHANNEL, 0x24, SERVER_TO_CLIENT, SPacketServerInfoDataChunkV4EAG.class), + define(V4_CHANNEL, 0x25, CLIENT_TO_SERVER, CPacketWebViewMessageEnV4EAG.class), + define(V4_CHANNEL, 0x26, CLIENT_TO_SERVER, CPacketWebViewMessageV4EAG.class), + define(V4_CHANNEL, 0x27, SERVER_TO_CLIENT, SPacketWebViewMessageV4EAG.class), + define(V4_CHANNEL, 0x28, SERVER_TO_CLIENT, SPacketNotifIconsRegisterV4EAG.class), + define(V4_CHANNEL, 0x29, SERVER_TO_CLIENT, SPacketNotifIconsReleaseV4EAG.class), + define(V4_CHANNEL, 0x2A, SERVER_TO_CLIENT, SPacketNotifBadgeShowV4EAG.class), + define(V4_CHANNEL, 0x2B, SERVER_TO_CLIENT, SPacketNotifBadgeHideV4EAG.class) + ); + + public final int ver; + + private final Map[] channelMap; + private final Map, PacketDef>[] classMap; + private final Set notChannelMap = new HashSet<>(); // populated in clinit + + private static final GamePluginMessageProtocol[] PROTOCOLS_MAP = new GamePluginMessageProtocol[5]; + private static final Set allChannels = new HashSet<>(); + + private GamePluginMessageProtocol(int versionInt, PacketDef... packets) { + ver = versionInt; + channelMap = new Map[] { new HashMap<>(), new HashMap<>() }; + classMap = new Map[] { new HashMap<>(), new HashMap<>() }; + for(int i = 0; i < packets.length; ++i) { + PacketDef pkt = packets[i]; + classMap[pkt.dir].put(pkt.clazz, pkt); + if(pkt.id == -1) { + channelMap[pkt.dir].put(pkt.channel, pkt); + }else { + PacketDef[] map = (PacketDef[])channelMap[pkt.dir].get(pkt.channel); + if(map == null || map.length <= pkt.id) { + PacketDef[] newMap = new PacketDef[((pkt.id + 1) & 0xF0) + 0x0F]; + if(map != null) { + System.arraycopy(map, 0, newMap, 0, map.length); + } + map = newMap; + channelMap[pkt.dir].put(pkt.channel, map); + } + map[pkt.id] = pkt; + } + } + } + + private static PacketDef define(String channel, int id, int dir, Class clazz) { + return new PacketDef(channel, id, dir, clazz); + } + + private static class PacketDef { + + private final String channel; + private final int id; + private final int dir; + private final Class clazz; + + private PacketDef(String channel, int id, int dir, Class clazz) { + this.channel = channel; + this.id = id; + this.dir = dir; + this.clazz = clazz; + } + + } + + public GameMessagePacket readPacket(String channel, int direction, GamePacketInputBuffer buffer) throws IOException { + Object obj = channelMap[direction].get(channel); + if(obj == null) { + return null; + } + PacketDef toRead; + if(obj instanceof PacketDef) { + toRead = (PacketDef)obj; + }else { + int pktId = buffer.readUnsignedByte(); + PacketDef[] pkts = (PacketDef[])obj; + if(pktId < 0 || pktId >= pkts.length || (toRead = pkts[pktId]) == null) { + throw new IOException("[" + channel + "] Unknown packet ID: " + pktId); + } + } + GameMessagePacket ret; + try { + ret = toRead.clazz.newInstance(); + }catch(Throwable t) { + throw new RuntimeException("Reflection failed to call packet constructor! (Is it defined?)", t); + } + ret.readPacket(buffer); + return ret; + } + + public String writePacket(int direction, GamePacketOutputBuffer stream, GameMessagePacket packet) throws IOException { + Class clazz = packet.getClass(); + PacketDef def = classMap[direction].get(clazz); + if(def == null) { + throw new IOException("Unknown packet type or wrong direction: " + clazz); + } + if(def.id != -1) { + stream.writeByte(def.id); + } + packet.writePacket(stream); + return def.channel; + } + + public List filterProtocols(Collection data) { + List ret = new ArrayList<>(data.size()); + for(String str : data) { + if(!notChannelMap.contains(str)) { + ret.add(str); + } + } + return ret; + } + + public static GamePluginMessageProtocol getByVersion(int ver) { + if(ver < 0 || ver > PROTOCOLS_MAP.length) { + return null; + } + return PROTOCOLS_MAP[ver]; + } + + public static Set getAllChannels() { + return allChannels; + } + + static { + GamePluginMessageProtocol[] _values = values(); + PROTOCOLS_MAP[2] = V3; + for(int i = 0; i < _values.length; ++i) { + GamePluginMessageProtocol protocol = _values[i]; + PROTOCOLS_MAP[protocol.ver] = protocol; + allChannels.addAll(protocol.channelMap[CLIENT_TO_SERVER].keySet()); + allChannels.addAll(protocol.channelMap[SERVER_TO_CLIENT].keySet()); + } + for(int i = 0; i < _values.length; ++i) { + GamePluginMessageProtocol protocol = _values[i]; + protocol.notChannelMap.addAll(allChannels); + protocol.notChannelMap.removeAll(protocol.channelMap[CLIENT_TO_SERVER].keySet()); + protocol.notChannelMap.removeAll(protocol.channelMap[SERVER_TO_CLIENT].keySet()); + } + } +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/GameMessageHandler.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/GameMessageHandler.java new file mode 100755 index 0000000..f82abba --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/GameMessageHandler.java @@ -0,0 +1,207 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface GameMessageHandler { + + default void handleClient(CPacketGetOtherCapeEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketGetOtherSkinEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketGetSkinByURLEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketInstallSkinSPEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalConnectEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalDescEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalDisconnectV3EAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalDisconnectV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalDisconnectPeerV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalICEEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketVoiceSignalRequestEAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketGetOtherClientUUIDV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketRequestServerInfoV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketWebViewMessageV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleClient(CPacketWebViewMessageEnV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketEnableFNAWSkinsEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketOtherCapeCustomEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketOtherCapePresetEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketOtherSkinCustomV3EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketOtherSkinCustomV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketOtherSkinPresetEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketUpdateCertEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalAllowedEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalConnectV3EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalConnectV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalConnectAnnounceV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalDescEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalDisconnectPeerEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalGlobalEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketVoiceSignalICEEAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketForceClientSkinPresetV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketForceClientSkinCustomV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketSetServerCookieV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketRedirectClientV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketOtherPlayerClientUUIDV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketForceClientCapePresetV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketForceClientCapeCustomV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketInvalidatePlayerCacheV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketUnforceClientV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketCustomizePauseMenuV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketServerInfoDataChunkV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketWebViewMessageV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketNotifIconsRegisterV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketNotifIconsReleaseV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketNotifBadgeShowV4EAG packet) { + throw new WrongPacketException(); + } + + default void handleServer(SPacketNotifBadgeHideV4EAG packet) { + throw new WrongPacketException(); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/GameMessagePacket.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/GameMessagePacket.java new file mode 100755 index 0000000..5c2f2f7 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/GameMessagePacket.java @@ -0,0 +1,33 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface GameMessagePacket { + + void readPacket(GamePacketInputBuffer buffer) throws IOException; + + void writePacket(GamePacketOutputBuffer buffer) throws IOException; + + void handlePacket(GameMessageHandler handler); + + int length(); + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/WrongPacketException.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/WrongPacketException.java new file mode 100755 index 0000000..fbab52d --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/WrongPacketException.java @@ -0,0 +1,24 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class WrongPacketException extends RuntimeException { + + public WrongPacketException() { + super("Wrong packet type recieved for the current handler!"); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherCapeEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherCapeEAG.java new file mode 100755 index 0000000..d94ee04 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherCapeEAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketGetOtherCapeEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + + public CPacketGetOtherCapeEAG() { + } + + public CPacketGetOtherCapeEAG(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherClientUUIDV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherClientUUIDV4EAG.java new file mode 100755 index 0000000..97e7418 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherClientUUIDV4EAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketGetOtherClientUUIDV4EAG implements GameMessagePacket { + + public int requestId; + public long playerUUIDMost; + public long playerUUIDLeast; + + public CPacketGetOtherClientUUIDV4EAG() { + } + + public CPacketGetOtherClientUUIDV4EAG(int requestId, long playerUUIDMost, long playerUUIDLeast) { + this.requestId = requestId; + this.playerUUIDMost = playerUUIDMost; + this.playerUUIDLeast = playerUUIDLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + requestId = buffer.readVarInt(); + playerUUIDMost = buffer.readLong(); + playerUUIDLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeVarInt(requestId); + buffer.writeLong(playerUUIDMost); + buffer.writeLong(playerUUIDLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return GamePacketOutputBuffer.getVarIntSize(requestId) + 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherSkinEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherSkinEAG.java new file mode 100755 index 0000000..9d61138 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetOtherSkinEAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketGetOtherSkinEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + + public CPacketGetOtherSkinEAG() { + } + + public CPacketGetOtherSkinEAG(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetSkinByURLEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetSkinByURLEAG.java new file mode 100755 index 0000000..7ebca6f --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketGetSkinByURLEAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketGetSkinByURLEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public String url; + + public CPacketGetSkinByURLEAG() { + } + + public CPacketGetSkinByURLEAG(long uuidMost, long uuidLeast, String url) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.url = url; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + url = buffer.readStringEaglerASCII16(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeStringEaglerASCII16(url); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 18 + url.length(); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketInstallSkinSPEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketInstallSkinSPEAG.java new file mode 100755 index 0000000..417b603 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketInstallSkinSPEAG.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketInstallSkinSPEAG implements GameMessagePacket { + + public byte[] customSkin; + + public CPacketInstallSkinSPEAG() { + } + + public CPacketInstallSkinSPEAG(byte[] customSkin) { + this.customSkin = customSkin; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + customSkin = new byte[buffer.readUnsignedShort()]; + buffer.readFully(customSkin); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeShort(customSkin.length); + buffer.write(customSkin); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 2 + customSkin.length; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketRequestServerInfoV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketRequestServerInfoV4EAG.java new file mode 100755 index 0000000..eec1287 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketRequestServerInfoV4EAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketRequestServerInfoV4EAG implements GameMessagePacket { + + public byte[] requestHash; + + public CPacketRequestServerInfoV4EAG() { + } + + public CPacketRequestServerInfoV4EAG(byte[] requestHash) { + this.requestHash = requestHash; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + requestHash = new byte[20]; + buffer.readFully(requestHash); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(requestHash.length != 20) { + throw new IOException("Hash must be 20 bytes! (" + requestHash.length + " given)"); + } + buffer.write(requestHash); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 20; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalConnectEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalConnectEAG.java new file mode 100755 index 0000000..873baf4 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalConnectEAG.java @@ -0,0 +1,48 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalConnectEAG implements GameMessagePacket { + + public CPacketVoiceSignalConnectEAG() { + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 0; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDescEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDescEAG.java new file mode 100755 index 0000000..539bd45 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDescEAG.java @@ -0,0 +1,80 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalDescEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public byte[] desc; + + public CPacketVoiceSignalDescEAG() { + } + + public CPacketVoiceSignalDescEAG(long uuidMost, long uuidLeast, byte[] desc) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.desc = desc; + } + + public CPacketVoiceSignalDescEAG(long uuidMost, long uuidLeast, String desc) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.desc = desc.getBytes(StandardCharsets.UTF_8); + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + int descLen = buffer.readVarInt(); + if(descLen > 32750) { + throw new IOException("Voice signal packet DESC too long!"); + } + desc = new byte[descLen]; + buffer.readFully(desc); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(desc.length > 32750) { + throw new IOException("Voice signal packet DESC too long!"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeVarInt(desc.length); + buffer.write(desc); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 16 + GamePacketOutputBuffer.getArrayMCSize(desc.length); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectPeerV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectPeerV4EAG.java new file mode 100755 index 0000000..caad482 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectPeerV4EAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalDisconnectPeerV4EAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + + public CPacketVoiceSignalDisconnectPeerV4EAG() { + } + + public CPacketVoiceSignalDisconnectPeerV4EAG(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectV3EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectV3EAG.java new file mode 100755 index 0000000..8c99cdf --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectV3EAG.java @@ -0,0 +1,69 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalDisconnectV3EAG implements GameMessagePacket { + + public boolean isPeerType; + public long uuidMost; + public long uuidLeast; + + public CPacketVoiceSignalDisconnectV3EAG() { + } + + public CPacketVoiceSignalDisconnectV3EAG(boolean isPeerType, long uuidMost, long uuidLeast) { + this.isPeerType = isPeerType; + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + if(buffer.available() > 0) { + isPeerType = true; + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + }else { + isPeerType = false; + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(isPeerType) { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return isPeerType ? 16 : 0; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectV4EAG.java new file mode 100755 index 0000000..c65ced8 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalDisconnectV4EAG.java @@ -0,0 +1,48 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalDisconnectV4EAG implements GameMessagePacket { + + public CPacketVoiceSignalDisconnectV4EAG() { + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 0; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalICEEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalICEEAG.java new file mode 100755 index 0000000..8de5fca --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalICEEAG.java @@ -0,0 +1,80 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalICEEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public byte[] ice; + + public CPacketVoiceSignalICEEAG() { + } + + public CPacketVoiceSignalICEEAG(long uuidMost, long uuidLeast, byte[] ice) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.ice = ice; + } + + public CPacketVoiceSignalICEEAG(long uuidMost, long uuidLeast, String ice) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.ice = ice.getBytes(StandardCharsets.UTF_8); + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + int iceLen = buffer.readVarInt(); + if(iceLen > 32750) { + throw new IOException("Voice signal packet ICE too long!"); + } + ice = new byte[iceLen]; + buffer.readFully(ice); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(ice.length > 32750) { + throw new IOException("Voice signal packet ICE too long!"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeVarInt(ice.length); + buffer.write(ice); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 16 + GamePacketOutputBuffer.getArrayMCSize(ice.length); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalRequestEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalRequestEAG.java new file mode 100755 index 0000000..7ccd93f --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketVoiceSignalRequestEAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketVoiceSignalRequestEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + + public CPacketVoiceSignalRequestEAG() { + } + + public CPacketVoiceSignalRequestEAG(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketWebViewMessageEnV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketWebViewMessageEnV4EAG.java new file mode 100755 index 0000000..c355c34 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketWebViewMessageEnV4EAG.java @@ -0,0 +1,71 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketWebViewMessageEnV4EAG implements GameMessagePacket { + + public boolean messageChannelOpen; + public String channelName; + + public CPacketWebViewMessageEnV4EAG() { + } + + public CPacketWebViewMessageEnV4EAG(boolean messageChannelOpen, String channelName) { + this.messageChannelOpen = messageChannelOpen; + this.channelName = channelName; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + messageChannelOpen = buffer.readBoolean(); + if(messageChannelOpen) { + channelName = buffer.readStringEaglerASCII8(); + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeBoolean(messageChannelOpen); + if(messageChannelOpen) { + if(channelName != null) { + if(channelName.length() > 255) { + throw new IOException("Channel name too long! (255 max, " + channelName.length() + " given)"); + } + buffer.writeStringEaglerASCII8(channelName); + }else { + buffer.writeByte(0); + } + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return messageChannelOpen ? 2 + (channelName != null ? channelName.length() : 0) : 1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketWebViewMessageV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketWebViewMessageV4EAG.java new file mode 100755 index 0000000..501390c --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/client/CPacketWebViewMessageV4EAG.java @@ -0,0 +1,74 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CPacketWebViewMessageV4EAG implements GameMessagePacket { + + public static final int TYPE_STRING = 0; + public static final int TYPE_BINARY = 1; + + public int type; + public byte[] data; + + public CPacketWebViewMessageV4EAG() { + } + + public CPacketWebViewMessageV4EAG(int type, byte[] data) { + this.type = type; + this.data = data; + } + + public CPacketWebViewMessageV4EAG(String str) { + this.type = TYPE_STRING; + this.data = str.getBytes(StandardCharsets.UTF_8); + } + + public CPacketWebViewMessageV4EAG(byte[] data) { + this.type = TYPE_BINARY; + this.data = data; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + type = buffer.readUnsignedByte(); + data = buffer.readByteArrayMC(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeByte(type); + buffer.writeByteArrayMC(data); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleClient(this); + } + + @Override + public int length() { + return 1 + GamePacketOutputBuffer.getVarIntSize(data.length) + data.length; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketCustomizePauseMenuV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketCustomizePauseMenuV4EAG.java new file mode 100755 index 0000000..be8e07a --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketCustomizePauseMenuV4EAG.java @@ -0,0 +1,193 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketCustomizePauseMenuV4EAG implements GameMessagePacket { + + public static final int SERVER_INFO_MODE_NONE = 0; + public static final int SERVER_INFO_MODE_EXTERNAL_URL = 1; + public static final int SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP = 2; + public static final int SERVER_INFO_MODE_SHOW_EMBED_OVER_WS = 3; + + public static final int SERVER_INFO_EMBED_PERMS_JAVASCRIPT = 1; + public static final int SERVER_INFO_EMBED_PERMS_MESSAGE_API = 2; + public static final int SERVER_INFO_EMBED_PERMS_STRICT_CSP = 4; + + public static final int DISCORD_MODE_NONE = 0; + public static final int DISCORD_MODE_INVITE_URL = 1; + + public int serverInfoMode; + public String serverInfoButtonText; + public String serverInfoURL; + public byte[] serverInfoHash; + public int serverInfoEmbedPerms; + public String serverInfoEmbedTitle; + public int discordButtonMode; + public String discordButtonText; + public String discordInviteURL; + + public Map imageMappings; + public List imageData; + + public SPacketCustomizePauseMenuV4EAG() { + } + + public SPacketCustomizePauseMenuV4EAG(int serverInfoMode, String serverInfoButtonText, String serverInfoURL, + byte[] serverInfoHash, int serverInfoEmbedPerms, String serverInfoEmbedTitle, int discordButtonMode, + String discordButtonText, String discordInviteURL, Map imageMappings, + List imageData) { + this.serverInfoMode = serverInfoMode; + this.serverInfoButtonText = serverInfoButtonText; + this.serverInfoURL = serverInfoURL; + this.serverInfoHash = serverInfoHash; + this.serverInfoEmbedPerms = serverInfoEmbedPerms; + this.serverInfoEmbedTitle = serverInfoEmbedTitle; + this.discordButtonMode = discordButtonMode; + this.discordButtonText = discordButtonText; + this.discordInviteURL = discordInviteURL; + this.imageMappings = imageMappings; + this.imageData = imageData; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + imageMappings = null; + imageData = null; + int flags = buffer.readUnsignedByte(); + serverInfoMode = (flags & 15); + discordButtonMode = ((flags >> 4) & 15); + switch(serverInfoMode) { + case SERVER_INFO_MODE_EXTERNAL_URL: + serverInfoButtonText = buffer.readStringMC(127); + serverInfoURL = buffer.readStringEaglerASCII16(); + serverInfoEmbedPerms = 0; + serverInfoHash = null; + break; + case SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP: + serverInfoButtonText = buffer.readStringMC(127); + serverInfoURL = buffer.readStringEaglerASCII16(); + serverInfoEmbedPerms = buffer.readUnsignedByte(); + serverInfoHash = null; + serverInfoEmbedTitle = buffer.readStringMC(127); + break; + case SERVER_INFO_MODE_SHOW_EMBED_OVER_WS: + serverInfoButtonText = buffer.readStringMC(127); + serverInfoURL = null; + serverInfoEmbedPerms = buffer.readUnsignedByte(); + serverInfoHash = new byte[20]; + serverInfoEmbedTitle = buffer.readStringMC(127); + buffer.readFully(serverInfoHash); + break; + default: + serverInfoButtonText = null; + serverInfoURL = null; + serverInfoEmbedPerms = 0; + serverInfoHash = null; + break; + } + if(discordButtonMode == DISCORD_MODE_INVITE_URL) { + discordButtonText = buffer.readStringMC(127); + discordInviteURL = buffer.readStringEaglerASCII16(); + }else { + discordButtonText = null; + discordInviteURL = null; + } + int mappingsCount = buffer.readVarInt(); + if(mappingsCount > 0) { + imageMappings = new HashMap<>(); + imageData = new ArrayList<>(); + for(int i = 0; i < mappingsCount; ++i) { + imageMappings.put(buffer.readStringEaglerASCII8(), buffer.readVarInt()); + } + int imageDataCount = buffer.readVarInt(); + for(int i = 0; i < imageDataCount; ++i) { + imageData.add(PacketImageData.readRGB16(buffer)); + } + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeByte(serverInfoMode | (discordButtonMode << 4)); + switch(serverInfoMode) { + case SERVER_INFO_MODE_EXTERNAL_URL: + buffer.writeStringMC(serverInfoButtonText); + buffer.writeStringEaglerASCII16(serverInfoURL); + break; + case SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP: + buffer.writeStringMC(serverInfoButtonText); + buffer.writeStringEaglerASCII16(serverInfoURL); + buffer.writeByte(serverInfoEmbedPerms); + buffer.writeStringMC(serverInfoEmbedTitle); + break; + case SERVER_INFO_MODE_SHOW_EMBED_OVER_WS: + buffer.writeStringMC(serverInfoButtonText); + buffer.writeByte(serverInfoEmbedPerms); + buffer.writeStringMC(serverInfoEmbedTitle); + if(serverInfoHash.length != 20) { + throw new IOException("Hash must be 20 bytes! (" + serverInfoHash.length + " given)"); + } + buffer.write(serverInfoHash); + break; + default: + break; + } + if(discordButtonMode == DISCORD_MODE_INVITE_URL) { + buffer.writeStringMC(discordButtonText); + buffer.writeStringEaglerASCII16(discordInviteURL); + } + if(imageMappings != null && !imageMappings.isEmpty()) { + buffer.writeVarInt(imageMappings.size()); + for(Entry etr : imageMappings.entrySet()) { + buffer.writeStringEaglerASCII8(etr.getKey()); + buffer.writeVarInt(etr.getValue().intValue()); + } + buffer.writeVarInt(imageData.size()); + for(PacketImageData etr : imageData) { + if(etr.width < 1 || etr.width > 64 || etr.height < 1 || etr.height > 64) { + throw new IOException("Invalid image dimensions in packet, must be between 1x1 and 64x64, got " + etr.width + "x" + etr.height); + } + PacketImageData.writeRGB16(buffer, etr); + } + }else { + buffer.writeByte(0); + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return -1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketEnableFNAWSkinsEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketEnableFNAWSkinsEAG.java new file mode 100755 index 0000000..a8b7fe6 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketEnableFNAWSkinsEAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketEnableFNAWSkinsEAG implements GameMessagePacket { + + public boolean enableSkins; + public boolean force; + + public SPacketEnableFNAWSkinsEAG() { + } + + public SPacketEnableFNAWSkinsEAG(boolean enableSkins, boolean force) { + this.enableSkins = enableSkins; + this.force = force; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + int i = buffer.readUnsignedByte(); + enableSkins = (i & 1) != 0; + force = (i & 2) != 0; + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeByte((enableSkins ? 1 : 0) | (force ? 2 : 0)); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientCapeCustomV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientCapeCustomV4EAG.java new file mode 100755 index 0000000..1b46e5f --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientCapeCustomV4EAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketForceClientCapeCustomV4EAG implements GameMessagePacket { + + public byte[] customCape; + + public SPacketForceClientCapeCustomV4EAG() { + } + + public SPacketForceClientCapeCustomV4EAG(byte[] customCape) { + this.customCape = customCape; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + customCape = new byte[1173]; + buffer.readFully(customCape); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(customCape.length != 1173) { + throw new IOException("Custom cape data length is not 1173 bytes! (" + customCape.length + ")"); + } + buffer.write(customCape); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 1173; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientCapePresetV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientCapePresetV4EAG.java new file mode 100755 index 0000000..ed84090 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientCapePresetV4EAG.java @@ -0,0 +1,56 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketForceClientCapePresetV4EAG implements GameMessagePacket { + + public int presetCape; + + public SPacketForceClientCapePresetV4EAG() { + } + + public SPacketForceClientCapePresetV4EAG(int presetCape) { + this.presetCape = presetCape; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + presetCape = buffer.readInt(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeInt(presetCape); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 4; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientSkinCustomV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientSkinCustomV4EAG.java new file mode 100755 index 0000000..2488d70 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientSkinCustomV4EAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketForceClientSkinCustomV4EAG implements GameMessagePacket { + + public int modelID; + public byte[] customSkin; + + public SPacketForceClientSkinCustomV4EAG() { + } + + public SPacketForceClientSkinCustomV4EAG(int modelID, byte[] customSkin) { + this.modelID = modelID; + this.customSkin = customSkin; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + modelID = buffer.readUnsignedByte(); + customSkin = new byte[12288]; + buffer.readFully(customSkin); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(customSkin.length != 12288) { + throw new IOException("Custom skin data length is not 12288 bytes! (" + customSkin.length + ")"); + } + buffer.write(modelID); + buffer.write(customSkin); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 12289; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientSkinPresetV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientSkinPresetV4EAG.java new file mode 100755 index 0000000..d7e8aa7 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketForceClientSkinPresetV4EAG.java @@ -0,0 +1,56 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketForceClientSkinPresetV4EAG implements GameMessagePacket { + + public int presetSkin; + + public SPacketForceClientSkinPresetV4EAG() { + } + + public SPacketForceClientSkinPresetV4EAG(int presetSkin) { + this.presetSkin = presetSkin; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + presetSkin = buffer.readInt(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeInt(presetSkin); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 4; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketInvalidatePlayerCacheV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketInvalidatePlayerCacheV4EAG.java new file mode 100755 index 0000000..414bffb --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketInvalidatePlayerCacheV4EAG.java @@ -0,0 +1,114 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketInvalidatePlayerCacheV4EAG implements GameMessagePacket { + + public Collection players; + + public static class InvalidateRequest { + + public final boolean invalidateSkin; + public final boolean invalidateCape; + public final long uuidMost; + public final long uuidLeast; + + public InvalidateRequest(boolean invalidateSkin, boolean invalidateCape, long uuidMost, long uuidLeast) { + this.invalidateSkin = invalidateSkin; + this.invalidateCape = invalidateCape; + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + } + + public SPacketInvalidatePlayerCacheV4EAG() { + } + + public SPacketInvalidatePlayerCacheV4EAG(Collection players) { + this.players = players; + } + + public SPacketInvalidatePlayerCacheV4EAG(InvalidateRequest... players) { + this.players = Arrays.asList(players); + } + + public SPacketInvalidatePlayerCacheV4EAG(boolean invalidateSkin, boolean invalidateCape, long uuidMost, long uuidLeast) { + this.players = Arrays.asList(new InvalidateRequest(invalidateSkin, invalidateCape, uuidMost, uuidLeast)); + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + int cnt = buffer.readVarInt(); + List userList = (List)(players = new ArrayList<>(cnt)); + if(cnt > 0) { + for(int i = 0; i < cnt; ++i) { + int flags = buffer.readUnsignedByte(); + userList.add(new InvalidateRequest((flags & 1) != 0, (flags & 2) != 0, buffer.readLong(), buffer.readLong())); + } + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(players == null || players.size() == 0) { + buffer.write(0); + }else { + if(players instanceof RandomAccess) { + List userList = (List)players; + int cnt = userList.size(); + buffer.writeVarInt(cnt); + for(int i = 0; i < cnt; ++i) { + InvalidateRequest dt = userList.get(i); + buffer.writeByte((dt.invalidateSkin ? 1 : 0) | (dt.invalidateCape ? 2 : 0)); + buffer.writeLong(dt.uuidMost); + buffer.writeLong(dt.uuidLeast); + } + }else { + buffer.writeVarInt(players.size()); + for(InvalidateRequest dt : players) { + buffer.writeByte((dt.invalidateSkin ? 1 : 0) | (dt.invalidateCape ? 2 : 0)); + buffer.writeLong(dt.uuidMost); + buffer.writeLong(dt.uuidLeast); + } + } + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + int cnt = players.size(); + return GamePacketOutputBuffer.getVarIntSize(cnt) + 17 * cnt; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifBadgeHideV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifBadgeHideV4EAG.java new file mode 100755 index 0000000..2fb67b4 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifBadgeHideV4EAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketNotifBadgeHideV4EAG implements GameMessagePacket { + + public long badgeUUIDMost; + public long badgeUUIDLeast; + + public SPacketNotifBadgeHideV4EAG() { + } + + public SPacketNotifBadgeHideV4EAG(long badgeUUIDMost, long badgeUUIDLeast) { + this.badgeUUIDMost = badgeUUIDMost; + this.badgeUUIDLeast = badgeUUIDLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + badgeUUIDMost = buffer.readLong(); + badgeUUIDLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(badgeUUIDMost); + buffer.writeLong(badgeUUIDLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifBadgeShowV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifBadgeShowV4EAG.java new file mode 100755 index 0000000..2a22028 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifBadgeShowV4EAG.java @@ -0,0 +1,172 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketNotifBadgeShowV4EAG implements GameMessagePacket { + + public static enum EnumBadgePriority { + LOW(0), NORMAL(1), HIGHER(2), HIGHEST(3); + + public final int priority; + + private EnumBadgePriority(int priority) { + this.priority = priority; + } + + private static final EnumBadgePriority[] lookup = new EnumBadgePriority[4]; + + public static EnumBadgePriority getByID(int id) { + if(id >= 0 && id < lookup.length) { + return lookup[id]; + }else { + return NORMAL; + } + } + + static { + EnumBadgePriority[] _values = values(); + for(int i = 0; i < _values.length; ++i) { + lookup[_values[i].priority] = _values[i]; + } + } + } + + public long badgeUUIDMost; + public long badgeUUIDLeast; + public String bodyComponent; + public String titleComponent; + public String sourceComponent; + public long originalTimestampSec; + public boolean silent; + public EnumBadgePriority priority; + public long mainIconUUIDMost; + public long mainIconUUIDLeast; + public long titleIconUUIDMost; + public long titleIconUUIDLeast; + public int hideAfterSec; + public int expireAfterSec; + public int backgroundColor; + public int bodyTxtColor; + public int titleTxtColor; + public int sourceTxtColor; + + public SPacketNotifBadgeShowV4EAG() { + } + + public SPacketNotifBadgeShowV4EAG(long badgeUUIDMost, long badgeUUIDLeast, String bodyComponent, + String titleComponent, String sourceComponent, long originalTimestampSec, boolean silent, + EnumBadgePriority priority, long mainIconUUIDMost, long mainIconUUIDLeast, long titleIconUUIDMost, + long titleIconUUIDLeast, int hideAfterSec, int expireAfterSec, int backgroundColor, int bodyTxtColor, + int titleTxtColor, int sourceTxtColor) { + this.badgeUUIDMost = badgeUUIDMost; + this.badgeUUIDLeast = badgeUUIDLeast; + this.bodyComponent = bodyComponent; + this.titleComponent = titleComponent; + this.sourceComponent = sourceComponent; + this.originalTimestampSec = originalTimestampSec; + this.silent = silent; + this.priority = priority; + this.mainIconUUIDMost = mainIconUUIDMost; + this.mainIconUUIDLeast = mainIconUUIDLeast; + this.titleIconUUIDMost = titleIconUUIDMost; + this.titleIconUUIDLeast = titleIconUUIDLeast; + this.hideAfterSec = hideAfterSec; + this.expireAfterSec = expireAfterSec; + this.backgroundColor = backgroundColor; + this.bodyTxtColor = bodyTxtColor; + this.titleTxtColor = titleTxtColor; + this.sourceTxtColor = sourceTxtColor; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + badgeUUIDMost = buffer.readLong(); + badgeUUIDLeast = buffer.readLong(); + bodyComponent = buffer.readStringMC(32767); + titleComponent = buffer.readStringMC(255); + sourceComponent = buffer.readStringMC(255); + originalTimestampSec = ((long)buffer.readUnsignedShort() << 32l) | ((long)buffer.readInt() & 0xFFFFFFFFl); + int flags = buffer.readUnsignedByte(); + silent = (flags & 1) != 0; + priority = EnumBadgePriority.getByID((flags >>> 1) & 3); + hideAfterSec = buffer.readUnsignedByte(); + expireAfterSec = buffer.readUnsignedShort(); + mainIconUUIDMost = (flags & 8) != 0 ? buffer.readLong() : 0l; + mainIconUUIDLeast = (flags & 8) != 0 ? buffer.readLong() : 0l; + titleIconUUIDMost = (flags & 16) != 0 ? buffer.readLong() : 0l; + titleIconUUIDLeast = (flags & 16) != 0 ? buffer.readLong() : 0l; + backgroundColor = (buffer.readUnsignedByte() << 16) | (buffer.readUnsignedByte() << 8) | buffer.readUnsignedByte(); + bodyTxtColor = (buffer.readUnsignedByte() << 16) | (buffer.readUnsignedByte() << 8) | buffer.readUnsignedByte(); + titleTxtColor = (buffer.readUnsignedByte() << 16) | (buffer.readUnsignedByte() << 8) | buffer.readUnsignedByte(); + sourceTxtColor = (buffer.readUnsignedByte() << 16) | (buffer.readUnsignedByte() << 8) | buffer.readUnsignedByte(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(badgeUUIDMost); + buffer.writeLong(badgeUUIDLeast); + buffer.writeStringMC(bodyComponent); + buffer.writeStringMC(titleComponent); + buffer.writeStringMC(sourceComponent); + buffer.writeShort((int)((originalTimestampSec >>> 32l) & 0xFFFFl)); + buffer.writeInt((int)(originalTimestampSec & 0xFFFFFFFFl)); + int flags = (silent ? 1 : 0); + flags |= ((priority != null ? priority.priority : 1) << 1); + flags |= ((mainIconUUIDMost != 0l || mainIconUUIDLeast != 0l) ? 8 : 0); + flags |= ((titleIconUUIDMost != 0l || titleIconUUIDLeast != 0l) ? 16 : 0); + buffer.writeByte(flags); + buffer.writeByte(hideAfterSec); + buffer.writeShort(expireAfterSec); + if((flags & 8) != 0) { + buffer.writeLong(mainIconUUIDMost); + buffer.writeLong(mainIconUUIDLeast); + } + if((flags & 16) != 0) { + buffer.writeLong(titleIconUUIDMost); + buffer.writeLong(titleIconUUIDLeast); + } + buffer.writeByte((backgroundColor >>> 16) & 0xFF); + buffer.writeByte((backgroundColor >>> 8) & 0xFF); + buffer.writeByte(backgroundColor & 0xFF); + buffer.writeByte((bodyTxtColor >>> 16) & 0xFF); + buffer.writeByte((bodyTxtColor >>> 8) & 0xFF); + buffer.writeByte(bodyTxtColor & 0xFF); + buffer.writeByte((titleTxtColor >>> 16) & 0xFF); + buffer.writeByte((titleTxtColor >>> 8) & 0xFF); + buffer.writeByte(titleTxtColor & 0xFF); + buffer.writeByte((sourceTxtColor >>> 16) & 0xFF); + buffer.writeByte((sourceTxtColor >>> 8) & 0xFF); + buffer.writeByte(sourceTxtColor & 0xFF); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return -1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifIconsRegisterV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifIconsRegisterV4EAG.java new file mode 100755 index 0000000..5644b2a --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifIconsRegisterV4EAG.java @@ -0,0 +1,111 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketNotifIconsRegisterV4EAG implements GameMessagePacket { + + public static class CreateIcon { + + public long uuidMost; + public long uuidLeast; + public PacketImageData imageData; + + public CreateIcon(long uuidMost, long uuidLeast, PacketImageData imageData) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.imageData = imageData; + } + + public int length() { + return 16 + imageData.getByteLengthRGB16(); + } + + } + + public Collection iconsToCreate = null; + + public SPacketNotifIconsRegisterV4EAG() { + } + + public SPacketNotifIconsRegisterV4EAG(Collection iconsToCreate) { + this.iconsToCreate = iconsToCreate; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + int len = buffer.readVarInt(); + iconsToCreate = new ArrayList<>(len); + for(int i = 0; i < len; ++i) { + iconsToCreate.add(new CreateIcon(buffer.readLong(), buffer.readLong(), PacketImageData.readRGB16(buffer))); + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(iconsToCreate instanceof RandomAccess) { + int len = iconsToCreate.size(); + buffer.writeVarInt(len); + List vigg = (List)iconsToCreate; + for(int i = 0, l = vigg.size(); i < l; ++i) { + CreateIcon icn = vigg.get(i); + buffer.writeLong(icn.uuidMost); + buffer.writeLong(icn.uuidLeast); + PacketImageData.writeRGB16(buffer, icn.imageData); + } + }else { + buffer.writeVarInt(iconsToCreate.size()); + for(CreateIcon icn : iconsToCreate) { + buffer.writeLong(icn.uuidMost); + buffer.writeLong(icn.uuidLeast); + PacketImageData.writeRGB16(buffer, icn.imageData); + } + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + int len = GamePacketOutputBuffer.getVarIntSize(iconsToCreate.size()); + if(iconsToCreate instanceof RandomAccess) { + List vigg = (List)iconsToCreate; + for(int i = 0, l = vigg.size(); i < l; ++i) { + len += vigg.get(i).length(); + } + }else { + for(CreateIcon icn : iconsToCreate) { + len += icn.length(); + } + } + return len; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifIconsReleaseV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifIconsReleaseV4EAG.java new file mode 100755 index 0000000..64858fe --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketNotifIconsReleaseV4EAG.java @@ -0,0 +1,92 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketNotifIconsReleaseV4EAG implements GameMessagePacket { + + public static class DestroyIcon { + + public long uuidMost; + public long uuidLeast; + + public DestroyIcon(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + } + + public Collection iconsToDestroy = null; + + public SPacketNotifIconsReleaseV4EAG() { + } + + public SPacketNotifIconsReleaseV4EAG(Collection iconsToDestroy) { + this.iconsToDestroy = iconsToDestroy; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + int len = buffer.readVarInt(); + iconsToDestroy = new ArrayList<>(len); + for(int i = 0; i < len; ++i) { + iconsToDestroy.add(new DestroyIcon(buffer.readLong(), buffer.readLong())); + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(iconsToDestroy instanceof RandomAccess) { + int len = iconsToDestroy.size(); + buffer.writeVarInt(len); + List vigg = (List)iconsToDestroy; + for(int i = 0; i < len; ++i) { + DestroyIcon icn = vigg.get(i); + buffer.writeLong(icn.uuidMost); + buffer.writeLong(icn.uuidLeast); + } + }else { + buffer.writeVarInt(iconsToDestroy.size()); + for(DestroyIcon icn : iconsToDestroy) { + buffer.writeLong(icn.uuidMost); + buffer.writeLong(icn.uuidLeast); + } + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + int len = iconsToDestroy.size(); + return GamePacketOutputBuffer.getVarIntSize(len) + (len << 4); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherCapeCustomEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherCapeCustomEAG.java new file mode 100755 index 0000000..9526662 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherCapeCustomEAG.java @@ -0,0 +1,68 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketOtherCapeCustomEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public byte[] customCape; + + public SPacketOtherCapeCustomEAG() { + } + + public SPacketOtherCapeCustomEAG(long uuidMost, long uuidLeast, byte[] customCape) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.customCape = customCape; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + customCape = new byte[1173]; + buffer.readFully(customCape); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(customCape.length != 1173) { + throw new IOException("Custom cape data length is not 1173 bytes! (" + customCape.length + ")"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.write(customCape); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 1189; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherCapePresetEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherCapePresetEAG.java new file mode 100755 index 0000000..1631188 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherCapePresetEAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketOtherCapePresetEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public int presetCape; + + public SPacketOtherCapePresetEAG() { + } + + public SPacketOtherCapePresetEAG(long uuidMost, long uuidLeast, int presetCape) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.presetCape = presetCape; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + presetCape = buffer.readInt(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeInt(presetCape); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 20; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherPlayerClientUUIDV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherPlayerClientUUIDV4EAG.java new file mode 100755 index 0000000..70593d8 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherPlayerClientUUIDV4EAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketOtherPlayerClientUUIDV4EAG implements GameMessagePacket { + + public int requestId; + public long clientUUIDMost; + public long clientUUIDLeast; + + public SPacketOtherPlayerClientUUIDV4EAG() { + } + + public SPacketOtherPlayerClientUUIDV4EAG(int requestId, long playerUUIDMost, long playerUUIDLeast) { + this.requestId = requestId; + this.clientUUIDMost = playerUUIDMost; + this.clientUUIDLeast = playerUUIDLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + requestId = buffer.readVarInt(); + clientUUIDMost = buffer.readLong(); + clientUUIDLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeVarInt(requestId); + buffer.writeLong(clientUUIDMost); + buffer.writeLong(clientUUIDLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return GamePacketOutputBuffer.getVarIntSize(requestId) + 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinCustomV3EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinCustomV3EAG.java new file mode 100755 index 0000000..24bf190 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinCustomV3EAG.java @@ -0,0 +1,72 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketOtherSkinCustomV3EAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public int modelID; + public byte[] customSkin; + + public SPacketOtherSkinCustomV3EAG() { + } + + public SPacketOtherSkinCustomV3EAG(long uuidMost, long uuidLeast, int modelID, byte[] customSkin) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.modelID = modelID; + this.customSkin = customSkin; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + modelID = buffer.readUnsignedByte(); + customSkin = new byte[16384]; + buffer.readFully(customSkin); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(customSkin.length != 16384) { + throw new IOException("Custom skin data length is not 16384 bytes! (" + customSkin.length + ")"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeByte(modelID); + buffer.write(customSkin); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 16401; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinCustomV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinCustomV4EAG.java new file mode 100755 index 0000000..2ae61e2 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinCustomV4EAG.java @@ -0,0 +1,72 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketOtherSkinCustomV4EAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public int modelID; + public byte[] customSkin; + + public SPacketOtherSkinCustomV4EAG() { + } + + public SPacketOtherSkinCustomV4EAG(long uuidMost, long uuidLeast, int modelID, byte[] customSkin) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.modelID = modelID; + this.customSkin = customSkin; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + modelID = buffer.readUnsignedByte(); + customSkin = new byte[12288]; + buffer.readFully(customSkin); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(customSkin.length != 12288) { + throw new IOException("Custom skin data length is not 12288 bytes! (" + customSkin.length + ")"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.write(modelID); + buffer.write(customSkin); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 12305; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinPresetEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinPresetEAG.java new file mode 100755 index 0000000..7d6b6f4 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketOtherSkinPresetEAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketOtherSkinPresetEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public int presetSkin; + + public SPacketOtherSkinPresetEAG() { + } + + public SPacketOtherSkinPresetEAG(long uuidMost, long uuidLeast, int presetSkin) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.presetSkin = presetSkin; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + presetSkin = buffer.readInt(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeInt(presetSkin); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 20; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketRedirectClientV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketRedirectClientV4EAG.java new file mode 100755 index 0000000..02bc8ca --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketRedirectClientV4EAG.java @@ -0,0 +1,56 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketRedirectClientV4EAG implements GameMessagePacket { + + public String redirectURI; + + public SPacketRedirectClientV4EAG() { + } + + public SPacketRedirectClientV4EAG(String redirectURI) { + this.redirectURI = redirectURI; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + redirectURI = buffer.readStringEaglerASCII16(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeStringEaglerASCII16(redirectURI); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 2 + redirectURI.length(); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketServerInfoDataChunkV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketServerInfoDataChunkV4EAG.java new file mode 100755 index 0000000..60e5ba5 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketServerInfoDataChunkV4EAG.java @@ -0,0 +1,79 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketServerInfoDataChunkV4EAG implements GameMessagePacket { + + public boolean lastChunk; + public int seqId; + public int finalSize; + public byte[] finalHash; + public byte[] data; + + public SPacketServerInfoDataChunkV4EAG() { + } + + public SPacketServerInfoDataChunkV4EAG(boolean lastChunk, int seqId, byte[] finalHash, int finalSize, byte[] data) { + this.lastChunk = lastChunk; + this.seqId = seqId; + this.finalHash = finalHash; + this.finalSize = finalSize; + this.data = data; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + lastChunk = buffer.readBoolean(); + seqId = buffer.readVarInt(); + finalSize = buffer.readVarInt(); + finalHash = new byte[20]; + buffer.readFully(finalHash); + data = new byte[buffer.readVarInt()]; + buffer.readFully(data); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(finalHash.length != 20) { + throw new IOException("Hash must be 20 bytes! (" + finalHash.length + " given)"); + } + buffer.writeBoolean(lastChunk); + buffer.writeVarInt(seqId); + buffer.writeVarInt(finalSize); + buffer.write(finalHash); + buffer.writeVarInt(data.length); + buffer.write(data); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 21 + GamePacketOutputBuffer.getVarIntSize(finalSize) + GamePacketOutputBuffer.getVarIntSize(seqId) + + GamePacketOutputBuffer.getVarIntSize(data.length) + data.length; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketSetServerCookieV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketSetServerCookieV4EAG.java new file mode 100755 index 0000000..cdbc33a --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketSetServerCookieV4EAG.java @@ -0,0 +1,85 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketSetServerCookieV4EAG implements GameMessagePacket { + + public boolean revokeQuerySupported; + public boolean saveCookieToDisk; + public long expires; + public byte[] data; + + public SPacketSetServerCookieV4EAG() { + } + + public SPacketSetServerCookieV4EAG(byte[] data, long expires, boolean revokeQuerySupported, boolean saveCookieToDisk) { + if(data.length > 255) { + throw new IllegalArgumentException("Cookie is too large! (Max 255 bytes)"); + } + this.data = data; + this.expires = expires; + this.revokeQuerySupported = revokeQuerySupported; + this.saveCookieToDisk = saveCookieToDisk; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + byte b = buffer.readByte(); + revokeQuerySupported = (b & 1) != 0; + saveCookieToDisk = (b & 2) != 0; + expires = buffer.readVarLong(); + int len = buffer.readUnsignedByte(); + if(len > 0) { + data = new byte[len]; + buffer.readFully(data); + }else { + data = null; + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(data != null && data.length > 255) { + throw new IOException("Cookie is too large! (Max 255 bytes)"); + } + buffer.writeByte((revokeQuerySupported ? 1 : 0) | (saveCookieToDisk ? 2 : 0)); + buffer.writeVarLong(expires); + if(data != null) { + buffer.writeByte(data.length); + buffer.write(data); + }else { + buffer.writeByte(0); + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return GamePacketOutputBuffer.getVarLongSize(expires) + 2 + data.length; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketUnforceClientV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketUnforceClientV4EAG.java new file mode 100755 index 0000000..a010515 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketUnforceClientV4EAG.java @@ -0,0 +1,63 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketUnforceClientV4EAG implements GameMessagePacket { + + public boolean resetSkin; + public boolean resetCape; + public boolean resetFNAW; + + public SPacketUnforceClientV4EAG() { + } + + public SPacketUnforceClientV4EAG(boolean resetSkin, boolean resetCape, boolean resetFNAW) { + this.resetSkin = resetSkin; + this.resetCape = resetCape; + this.resetFNAW = resetFNAW; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + int i = buffer.readUnsignedByte(); + resetSkin = (i & 1) != 0; + resetCape = (i & 2) != 0; + resetFNAW = (i & 4) != 0; + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeByte((resetSkin ? 1 : 0) | (resetCape ? 2 : 0) | (resetFNAW ? 4 : 0)); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketUpdateCertEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketUpdateCertEAG.java new file mode 100755 index 0000000..9e175fe --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketUpdateCertEAG.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketUpdateCertEAG implements GameMessagePacket { + + public byte[] updateCert; + + public SPacketUpdateCertEAG() { + } + + public SPacketUpdateCertEAG(byte[] updateCert) { + this.updateCert = updateCert; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + updateCert = buffer.toByteArray(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + // note: its better to send the raw bytes directly instead of + // using this class when programming a backend (for efficiency) + buffer.write(updateCert); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return updateCert.length; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalAllowedEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalAllowedEAG.java new file mode 100755 index 0000000..1a8d267 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalAllowedEAG.java @@ -0,0 +1,79 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalAllowedEAG implements GameMessagePacket { + + public boolean allowed; + public String[] iceServers; + + public SPacketVoiceSignalAllowedEAG() { + } + + public SPacketVoiceSignalAllowedEAG(boolean allowed, String[] iceServers) { + this.allowed = allowed; + this.iceServers = iceServers; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + allowed = buffer.readBoolean(); + if(allowed) { + int numIce = buffer.readVarInt(); + if(numIce > 64) { + throw new IOException("Too many STUN/TURN servers recieved! (" + numIce + ", max is 64!)"); + } + iceServers = new String[numIce]; + for(int i = 0; i < iceServers.length; ++i) { + iceServers[i] = buffer.readStringMC(1024); + } + }else { + iceServers = null; + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(allowed && iceServers.length > 64) { + throw new IOException("Too many STUN/TURN servers to send! (" + iceServers.length + ", max is 64!)"); + } + buffer.writeBoolean(allowed); + if(allowed) { + buffer.writeVarInt(iceServers.length); + for(int i = 0; i < iceServers.length; ++i) { + buffer.writeStringMC(iceServers[i]); + } + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return -1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectAnnounceV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectAnnounceV4EAG.java new file mode 100755 index 0000000..13d5cdd --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectAnnounceV4EAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalConnectAnnounceV4EAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + + public SPacketVoiceSignalConnectAnnounceV4EAG() { + } + + public SPacketVoiceSignalConnectAnnounceV4EAG(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectV3EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectV3EAG.java new file mode 100755 index 0000000..2eec170 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectV3EAG.java @@ -0,0 +1,73 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalConnectV3EAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public boolean isAnnounceType; + public boolean offer; + + public SPacketVoiceSignalConnectV3EAG() { + } + + public SPacketVoiceSignalConnectV3EAG(long uuidMost, long uuidLeast, boolean isAnnounceType, boolean offer) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.isAnnounceType = isAnnounceType; + this.offer = offer; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + if(buffer.available() > 0) { + isAnnounceType = false; + offer = buffer.readBoolean(); + }else { + isAnnounceType = true; + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + if(!isAnnounceType) { + buffer.writeBoolean(offer); + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return isAnnounceType ? 16 : 17; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectV4EAG.java new file mode 100755 index 0000000..014ec2c --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalConnectV4EAG.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalConnectV4EAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public boolean offer; + + public SPacketVoiceSignalConnectV4EAG() { + } + + public SPacketVoiceSignalConnectV4EAG(long uuidMost, long uuidLeast, boolean offer) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.offer = offer; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + offer = buffer.readBoolean(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeBoolean(offer); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 17; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalDescEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalDescEAG.java new file mode 100755 index 0000000..d7db44e --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalDescEAG.java @@ -0,0 +1,73 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalDescEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public byte[] desc; + + public SPacketVoiceSignalDescEAG() { + } + + public SPacketVoiceSignalDescEAG(long uuidMost, long uuidLeast, byte[] desc) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.desc = desc; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + int descLen = buffer.readVarInt(); + if(descLen > 32750) { + throw new IOException("Voice signal packet DESC too long!"); + } + desc = new byte[descLen]; + buffer.readFully(desc); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(desc.length > 32750) { + throw new IOException("Voice signal packet DESC too long!"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeVarInt(desc.length); + buffer.write(desc); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 16 + GamePacketOutputBuffer.getArrayMCSize(desc.length); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalDisconnectPeerEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalDisconnectPeerEAG.java new file mode 100755 index 0000000..7534675 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalDisconnectPeerEAG.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalDisconnectPeerEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + + public SPacketVoiceSignalDisconnectPeerEAG() { + } + + public SPacketVoiceSignalDisconnectPeerEAG(long uuidMost, long uuidLeast) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 16; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalGlobalEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalGlobalEAG.java new file mode 100755 index 0000000..dc028db --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalGlobalEAG.java @@ -0,0 +1,110 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalGlobalEAG implements GameMessagePacket { + + public Collection users; + + public static class UserData { + + public long uuidMost; + public long uuidLeast; + public String username; + + public UserData(long uuidMost, long uuidLeast, String username) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.username = username; + } + + } + + public SPacketVoiceSignalGlobalEAG() { + } + + public SPacketVoiceSignalGlobalEAG(Collection users) { + this.users = users; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + int cnt = buffer.readVarInt(); + List userList = (List)(users = new ArrayList<>(cnt)); + if(cnt > 0) { + for(int i = 0; i < cnt; ++i) { + userList.add(new UserData(buffer.readLong(), buffer.readLong(), null)); + } + if(buffer.available() > 0) { + for(int i = 0; i < cnt; ++i) { + userList.get(i).username = buffer.readStringMC(16); + } + } + } + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(users == null || users.size() == 0) { + buffer.write(0); + }else { + if(users instanceof RandomAccess) { + List userList = (List)users; + int cnt = userList.size(); + buffer.writeVarInt(cnt); + for(int i = 0; i < cnt; ++i) { + UserData dt = userList.get(i); + buffer.writeLong(dt.uuidMost); + buffer.writeLong(dt.uuidLeast); + } + for(int i = 0; i < cnt; ++i) { + buffer.writeStringMC(userList.get(i).username); + } + }else { + buffer.writeVarInt(users.size()); + for(UserData dt : users) { + buffer.writeLong(dt.uuidMost); + buffer.writeLong(dt.uuidLeast); + } + for(UserData dt : users) { + buffer.writeStringMC(dt.username); + } + } + } + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return -1; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalICEEAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalICEEAG.java new file mode 100755 index 0000000..91043f8 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketVoiceSignalICEEAG.java @@ -0,0 +1,73 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketVoiceSignalICEEAG implements GameMessagePacket { + + public long uuidMost; + public long uuidLeast; + public byte[] ice; + + public SPacketVoiceSignalICEEAG() { + } + + public SPacketVoiceSignalICEEAG(long uuidMost, long uuidLeast, byte[] ice) { + this.uuidMost = uuidMost; + this.uuidLeast = uuidLeast; + this.ice = ice; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + uuidMost = buffer.readLong(); + uuidLeast = buffer.readLong(); + int iceLen = buffer.readVarInt(); + if(iceLen > 32750) { + throw new IOException("Voice signal packet ICE too long!"); + } + ice = new byte[iceLen]; + buffer.readFully(ice); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + if(ice.length > 32750) { + throw new IOException("Voice signal packet ICE too long!"); + } + buffer.writeLong(uuidMost); + buffer.writeLong(uuidLeast); + buffer.writeVarInt(ice.length); + buffer.write(ice); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 16 + GamePacketOutputBuffer.getArrayMCSize(ice.length); + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketWebViewMessageV4EAG.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketWebViewMessageV4EAG.java new file mode 100755 index 0000000..7bc6f66 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/pkt/server/SPacketWebViewMessageV4EAG.java @@ -0,0 +1,74 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SPacketWebViewMessageV4EAG implements GameMessagePacket { + + public static final int TYPE_STRING = 0; + public static final int TYPE_BINARY = 1; + + public int type; + public byte[] data; + + public SPacketWebViewMessageV4EAG() { + } + + public SPacketWebViewMessageV4EAG(int type, byte[] data) { + this.type = type; + this.data = data; + } + + public SPacketWebViewMessageV4EAG(String str) { + this.type = TYPE_STRING; + this.data = str.getBytes(StandardCharsets.UTF_8); + } + + public SPacketWebViewMessageV4EAG(byte[] data) { + this.type = TYPE_BINARY; + this.data = data; + } + + @Override + public void readPacket(GamePacketInputBuffer buffer) throws IOException { + type = buffer.readUnsignedByte(); + data = buffer.readByteArrayMC(); + } + + @Override + public void writePacket(GamePacketOutputBuffer buffer) throws IOException { + buffer.writeByte(type); + buffer.writeByteArrayMC(data); + } + + @Override + public void handlePacket(GameMessageHandler handler) { + handler.handleServer(this); + } + + @Override + public int length() { + return 1 + GamePacketOutputBuffer.getVarIntSize(data.length) + data.length; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/PacketImageData.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/PacketImageData.java new file mode 100755 index 0000000..8e9b1a0 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/PacketImageData.java @@ -0,0 +1,79 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.util; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PacketImageData { + + public final int width; + public final int height; + public final int[] rgba; + + public PacketImageData(int width, int height, int[] rgba) { + this.width = width; + this.height = height; + this.rgba = rgba; + } + + public int getByteLengthRGB16() { + return 2 + (rgba.length << 1); + } + + public static PacketImageData readRGB16(GamePacketInputBuffer buffer) throws IOException { + int w = buffer.readUnsignedByte(); + int h = buffer.readUnsignedByte(); + int pixelCount = w * h; + int[] pixels = new int[pixelCount]; + for(int j = 0, p, pR, pG, pB; j < pixelCount; ++j) { + p = buffer.readUnsignedShort(); + pR = (p >>> 11) & 0x1F; + pG = (p >>> 5) & 0x3F; + pB = p & 0x1F; + if(pR + pG + pB > 0) { + pB = (int)((pB - 1) * 8.5f); + pixels[j] = 0xFF000000 | (pR << 19) | (pG << 10) | pB; + }else { + pixels[j] = 0; + } + } + return new PacketImageData(w, h, pixels); + } + + public static void writeRGB16(GamePacketOutputBuffer buffer, PacketImageData imageData) throws IOException { + if(imageData.width < 1 || imageData.width > 255 || imageData.height < 1 || imageData.height > 255) { + throw new IOException("Invalid image dimensions in packet, must be between 1x1 and 255x255, got " + imageData.width + "x" + imageData.height); + } + buffer.writeByte(imageData.width); + buffer.writeByte(imageData.height); + int pixelCount = imageData.width * imageData.height; + for(int j = 0, p, pR, pG, pB; j < pixelCount; ++j) { + p = imageData.rgba[j]; + if((p >>> 24) > 0x7F) { + pR = (p >>> 19) & 0x1F; + pG = (p >>> 10) & 0x3F; + pB = (int)((p & 0xFF) * 0.1176471f) + 1; + buffer.writeShort((pR << 11) | (pG << 5) | pB); + }else { + buffer.writeShort(0); + } + } + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/ReusableByteArrayInputStream.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/ReusableByteArrayInputStream.java new file mode 100755 index 0000000..0b6e88f --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/ReusableByteArrayInputStream.java @@ -0,0 +1,80 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.util; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ReusableByteArrayInputStream extends InputStream { + + private volatile byte[] currentBuffer = null; + private int idx = 0; + private int markIDX = 0; + + public void feedBuffer(byte[] b) { + currentBuffer = b; + idx = 0; + markIDX = 0; + } + + @Override + public int read() throws IOException { + if(currentBuffer.length <= idx) throw new IOException("ReusableByteArrayInputStream buffer underflow, no bytes remaining"); + return (int)currentBuffer[idx++] & 0xFF; + } + + @Override + public int read(byte b[], int off, int len) throws IOException { + if(idx + len > currentBuffer.length) { + throw new IOException( + "ReusableByteArrayInputStream buffer underflow, tried to read " + len + " when there are only " + + (currentBuffer.length - idx) + " bytes remaining", + new ArrayIndexOutOfBoundsException(idx + len - 1)); + } + if(off + len > b.length) { + throw new ArrayIndexOutOfBoundsException(off + len - 1); + } + System.arraycopy(currentBuffer, idx, b, off, len); + idx += len; + return len; + } + + public void mark() { + markIDX = idx; + } + + public void reset() { + idx = markIDX; + } + + public int getReaderIndex() { + return idx; + } + + public int available() { + return Math.max(currentBuffer.length - idx, 0); + } + + public void setReaderIndex(int i) { + idx = i; + markIDX = idx; + } + + public boolean markSupported() { + return true; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/ReusableByteArrayOutputStream.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/ReusableByteArrayOutputStream.java new file mode 100755 index 0000000..3392e13 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/ReusableByteArrayOutputStream.java @@ -0,0 +1,81 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ReusableByteArrayOutputStream extends OutputStream { + + private volatile byte[] currentBuffer = null; + private int idx = 0; + private int originalSize = 0; + + public void feedBuffer(byte[] buf) { + currentBuffer = buf; + idx = 0; + originalSize = buf == null ? 0 : buf.length; + } + + public boolean hasGrown() { + return currentBuffer.length != originalSize; + } + + public byte[] returnBuffer() { + return currentBuffer.length == idx ? currentBuffer : Arrays.copyOf(currentBuffer, idx); + } + + private void growBuffer(int i) { + int ii = currentBuffer.length; + int iii = i - ii; + if(iii > 0) { + int j = ii + (ii >> 1); + while(j < i) { + j += (j >> 1); + } + byte[] n = new byte[j]; + System.arraycopy(currentBuffer, 0, n, 0, ii); + currentBuffer = n; + } + } + + public int getWriterIndex() { + return idx; + } + + public void setWriterIndex(int i) { + idx = i; + } + + @Override + public void write(int b) throws IOException { + if(idx >= currentBuffer.length) { + growBuffer(idx + 1); + } + currentBuffer[idx++] = (byte) b; + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if(idx + len > currentBuffer.length) { + growBuffer(idx + len); + } + System.arraycopy(b, off, currentBuffer, idx, len); + idx += len; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SimpleInputBufferImpl.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SimpleInputBufferImpl.java new file mode 100755 index 0000000..8af6cf7 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SimpleInputBufferImpl.java @@ -0,0 +1,209 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SimpleInputBufferImpl extends DataInputStream implements GamePacketInputBuffer { + + protected byte[] toByteArrayReturns; + + public SimpleInputBufferImpl(InputStream in) { + super(in); + this.toByteArrayReturns = null; + } + + public SimpleInputBufferImpl(InputStream in, byte[] toByteArrayReturns) { + super(in); + this.toByteArrayReturns = toByteArrayReturns; + } + + public void setStream(InputStream parent) { + in = parent; + toByteArrayReturns = null; + } + + public void setToByteArrayReturns(byte[] toByteArrayReturns) { + this.toByteArrayReturns = toByteArrayReturns; + } + + @Override + public void skipAllBytes(int n) throws IOException { + if(skipBytes(n) != n) { + throw new EOFException(); + } + } + + @Override + public int readVarInt() throws IOException { + int i = 0; + int j = 0; + + while (true) { + int b0 = in.read(); + if(b0 < 0) { + throw new EOFException(); + } + i |= (b0 & 127) << j++ * 7; + if (j > 5) { + throw new IOException("VarInt too big"); + } + + if ((b0 & 128) != 128) { + break; + } + } + + return i; + } + + @Override + public long readVarLong() throws IOException { + long i = 0L; + int j = 0; + + while (true) { + int b0 = in.read(); + if(b0 < 0) { + throw new EOFException(); + } + i |= (long) (b0 & 127) << j++ * 7; + if (j > 10) { + throw new IOException("VarLong too big"); + } + + if ((b0 & 128) != 128) { + break; + } + } + + return i; + } + + @Override + public String readStringMC(int maxLen) throws IOException { + int i = this.readVarInt(); + if (i > (maxLen << 2)) { + throw new IOException("The received encoded string buffer length is longer than maximum allowed (" + i + + " > " + (maxLen << 2) + ")"); + } else if (i < 0) { + throw new IOException("The received encoded string buffer length is less than zero! Weird string!"); + } else { + byte[] toRead = new byte[i]; + this.readFully(toRead); + String s = new String(toRead, StandardCharsets.UTF_8); + if (s.length() > maxLen) { + throw new IOException( + "The received string length is longer than maximum allowed (" + i + " > " + maxLen + ")"); + } else { + return s; + } + } + } + + @Override + public String readStringEaglerASCII8() throws IOException { + int len = in.read(); + if(len < 0) { + throw new EOFException(); + } + char[] ret = new char[len]; + for(int i = 0, j; i < len; ++i) { + j = in.read(); + if(j < 0) { + throw new EOFException(); + } + ret[i] = (char)j; + } + return new String(ret); + } + + @Override + public String readStringEaglerASCII16() throws IOException { + int len = readUnsignedShort(); + char[] ret = new char[len]; + for(int i = 0, j; i < len; ++i) { + j = in.read(); + if(j < 0) { + throw new EOFException(); + } + ret[i] = (char)j; + } + return new String(ret); + } + + @Override + public byte[] readByteArrayMC() throws IOException { + byte[] abyte = new byte[this.readVarInt()]; + this.readFully(abyte); + return abyte; + } + + @Override + public InputStream stream() { + return in; + } + + @Override + public byte[] toByteArray() throws IOException { + if(toByteArrayReturns != null) { + return toByteArrayReturns; + }else if(in instanceof ByteArrayInputStream) { + ByteArrayInputStream bis = (ByteArrayInputStream)in; + byte[] ret = new byte[bis.available()]; + bis.read(ret); + return ret; + }else { + ByteArrayOutputStream bao = null; + byte[] copyBuffer = new byte[in.available()]; + int i = in.read(copyBuffer); + if(i == copyBuffer.length) { + int j = in.read(); + if(j == -1) { + return copyBuffer; + }else { + int k = Math.max(copyBuffer.length, 64); + bao = new ByteArrayOutputStream(k + 1); + bao.write(copyBuffer); + bao.write(j); + if(k != copyBuffer.length) { + copyBuffer = new byte[k]; + } + } + }else { + int j = Math.max(copyBuffer.length, 64); + bao = new ByteArrayOutputStream(j); + bao.write(copyBuffer); + if(j != copyBuffer.length) { + copyBuffer = new byte[j]; + } + } + while((i = in.read(copyBuffer)) != -1) { + bao.write(copyBuffer, 0, i); + } + return bao.toByteArray(); + } + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SimpleOutputBufferImpl.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SimpleOutputBufferImpl.java new file mode 100755 index 0000000..7fcd7f1 --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SimpleOutputBufferImpl.java @@ -0,0 +1,107 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.util; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SimpleOutputBufferImpl extends DataOutputStream implements GamePacketOutputBuffer { + + public SimpleOutputBufferImpl(OutputStream out) { + super(out); + } + + public void setStream(OutputStream parent) { + out = parent; + } + + @Override + public void writeVarInt(int i) throws IOException { + while ((i & -128) != 0) { + out.write(i & 127 | 128); + i >>>= 7; + } + out.write(i); + } + + @Override + public void writeVarLong(long i) throws IOException { + while ((i & -128L) != 0L) { + out.write((int) (i & 127L) | 128); + i >>>= 7; + } + out.write((int) i); + } + + @Override + public void writeStringMC(String str) throws IOException { + byte[] abyte = str.getBytes(StandardCharsets.UTF_8); + if (abyte.length > 32767) { + throw new IOException("String too big (was " + str.length() + " bytes encoded, max " + 32767 + ")"); + } else { + this.writeVarInt(abyte.length); + this.write(abyte); + } + } + + @Override + public void writeStringEaglerASCII8(String str) throws IOException { + int len = str.length(); + if(len > 255) { + throw new IOException("String is longer than 255 chars! (" + len + ")"); + } + out.write(len); + for(int i = 0, j; i < len; ++i) { + j = (int)str.charAt(i); + if(j > 255) { + j = (int)'?'; + } + out.write(j); + } + } + + @Override + public void writeStringEaglerASCII16(String str) throws IOException { + int len = str.length(); + if(len > 65535) { + throw new IOException("String is longer than 65535 chars! (" + len + ")"); + } + writeShort(len); + for(int i = 0, j; i < len; ++i) { + j = (int)str.charAt(i); + if(j > 255) { + j = (int)'?'; + } + out.write(j); + } + } + + @Override + public void writeByteArrayMC(byte[] bytes) throws IOException { + this.writeVarInt(bytes.length); + this.write(bytes); + } + + @Override + public OutputStream stream() { + return out; + } + +} diff --git a/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SkinPacketVersionCache.java b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SkinPacketVersionCache.java new file mode 100755 index 0000000..7374c4a --- /dev/null +++ b/src/protocol-game/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/util/SkinPacketVersionCache.java @@ -0,0 +1,360 @@ +package net.lax1dude.eaglercraft.v1_8.socket.protocol.util; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SkinPacketVersionCache { + + public GameMessagePacket skinPacketV3; + public GameMessagePacket skinPacketV4; + + public SkinPacketVersionCache(GameMessagePacket skinPacketV3, GameMessagePacket skinPacketV4) { + this.skinPacketV3 = skinPacketV3; + this.skinPacketV4 = skinPacketV4; + } + + public static SkinPacketVersionCache createPreset(long uuidMost, long uuidLeast) { + long hilo = uuidMost ^ uuidLeast; + GameMessagePacket pkt = new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, + ((((int) (hilo >> 32)) ^ (int) hilo) & 1) != 0 ? 1 : 0); + return new SkinPacketVersionCache(pkt, pkt); + } + + public static SkinPacketVersionCache createPreset(long uuidMost, long uuidLeast, int presetID) { + GameMessagePacket pkt = new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, presetID); + return new SkinPacketVersionCache(pkt, pkt); + } + + public static SkinPacketVersionCache createCustomV3(long uuidMost, long uuidLeast, int modelID, byte[] texture) { + return new SkinPacketVersionCache(new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, modelID, texture), null); + } + + public static SkinPacketVersionCache createCustomV4(long uuidMost, long uuidLeast, int modelID, byte[] texture) { + return new SkinPacketVersionCache(null, new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, modelID, texture)); + } + + public GameMessagePacket get(GamePluginMessageProtocol vers) { + switch(vers) { + case V3: + return getV3(); + case V4: + return getV4(); + default: + return null; + } + } + + public GameMessagePacket get(GamePluginMessageProtocol vers, long rewriteUUIDMost, long rewriteUUIDLeast) { + switch(vers) { + case V3: + return getV3(rewriteUUIDMost, rewriteUUIDLeast); + case V4: + return getV4(rewriteUUIDMost, rewriteUUIDLeast); + default: + return null; + } + } + + public GameMessagePacket get(GamePluginMessageProtocol vers, long rewriteUUIDMost, long rewriteUUIDLeast, int rewriteModel) { + switch(vers) { + case V3: + return getV3(rewriteUUIDMost, rewriteUUIDLeast, rewriteModel); + case V4: + return getV4(rewriteUUIDMost, rewriteUUIDLeast, rewriteModel); + default: + return null; + } + } + + public GameMessagePacket getV3() { + if(skinPacketV3 != null) { + return skinPacketV3; + }else { + if(skinPacketV4 == null) { + return null; + } + return skinPacketV3 = convertToV3(skinPacketV4); + } + } + + public GameMessagePacket getV4() { + if(skinPacketV4 != null) { + return skinPacketV4; + }else { + if(skinPacketV3 == null) { + return null; + } + return skinPacketV4 = convertToV4(skinPacketV3); + } + } + + public GameMessagePacket getV3(long rewriteUUIDMost, long rewriteUUIDLeast) { + if(skinPacketV3 != null) { + return rewriteUUID(skinPacketV3, rewriteUUIDMost, rewriteUUIDLeast); + }else { + if(skinPacketV4 == null) { + return null; + } + return skinPacketV3 = convertToV3RewriteUUID(skinPacketV4, rewriteUUIDMost, rewriteUUIDLeast); + } + } + + public GameMessagePacket getV4(long rewriteUUIDMost, long rewriteUUIDLeast) { + if(skinPacketV4 != null) { + return rewriteUUID(skinPacketV4, rewriteUUIDMost, rewriteUUIDLeast); + }else { + if(skinPacketV3 == null) { + return null; + } + return skinPacketV4 = convertToV4RewriteUUID(skinPacketV3, rewriteUUIDMost, rewriteUUIDLeast); + } + } + + public GameMessagePacket getV3(long rewriteUUIDMost, long rewriteUUIDLeast, int rewriteModel) { + if(skinPacketV3 != null) { + return rewriteUUIDModel(skinPacketV3, rewriteUUIDMost, rewriteUUIDLeast, rewriteModel); + }else { + if(skinPacketV4 == null) { + return null; + } + return skinPacketV3 = convertToV3RewriteUUIDModel(skinPacketV4, rewriteUUIDMost, rewriteUUIDLeast, rewriteModel); + } + } + + public GameMessagePacket getV4(long rewriteUUIDMost, long rewriteUUIDLeast, int rewriteModel) { + if(skinPacketV4 != null) { + return rewriteUUIDModel(skinPacketV4, rewriteUUIDMost, rewriteUUIDLeast, rewriteModel); + }else { + if(skinPacketV3 == null) { + return null; + } + return skinPacketV4 = convertToV4RewriteUUIDModel(skinPacketV3, rewriteUUIDMost, rewriteUUIDLeast, rewriteModel); + } + } + + public GameMessagePacket getForceClientV4() { + if(skinPacketV4 != null) { + return convertToForceV4(skinPacketV4); + }else { + if(skinPacketV3 == null) { + return null; + } + return convertToForceV4(skinPacketV4 = convertToV4(skinPacketV3)); + } + } + + public byte[] getV3HandshakeData() { + GameMessagePacket packetV3 = getV3(); + if(packetV3 instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt = (SPacketOtherSkinCustomV3EAG)packetV3; + byte[] tex = pkt.customSkin; + byte[] ret = new byte[2 + tex.length]; + ret[0] = (byte)2; + ret[1] = (byte)pkt.modelID; + System.arraycopy(tex, 0, ret, 2, tex.length); + return ret; + }else { + SPacketOtherSkinPresetEAG pkt = (SPacketOtherSkinPresetEAG)packetV3; + int p = pkt.presetSkin; + byte[] ret = new byte[5]; + ret[0] = (byte)1; + ret[1] = (byte)(p >>> 24); + ret[2] = (byte)(p >>> 16); + ret[3] = (byte)(p >>> 8); + ret[4] = (byte)(p & 0xFF); + return ret; + } + } + + public int getModelId() { + if(skinPacketV4 != null) { + if(skinPacketV4 instanceof SPacketOtherSkinCustomV4EAG) { + return ((SPacketOtherSkinCustomV4EAG)skinPacketV4).modelID; + } + }else if(skinPacketV3 != null) { + if(skinPacketV3 instanceof SPacketOtherSkinCustomV3EAG) { + return ((SPacketOtherSkinCustomV3EAG)skinPacketV3).modelID; + } + } + return -1; + } + + public static GameMessagePacket rewriteUUID(GameMessagePacket pkt, long uuidMost, long uuidLeast) { + if(pkt instanceof SPacketOtherSkinPresetEAG) { + return new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, ((SPacketOtherSkinPresetEAG)pkt).presetSkin); + }else if(pkt instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt2 = (SPacketOtherSkinCustomV4EAG)pkt; + return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, pkt2.modelID, pkt2.customSkin); + }else if(pkt instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt2 = (SPacketOtherSkinCustomV3EAG)pkt; + return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, pkt2.modelID, pkt2.customSkin); + }else { + return pkt; + } + } + + public static GameMessagePacket rewriteUUIDModel(GameMessagePacket pkt, long uuidMost, long uuidLeast, int modelID) { + if(pkt instanceof SPacketOtherSkinPresetEAG) { + return new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, ((SPacketOtherSkinPresetEAG)pkt).presetSkin); + }else if(pkt instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt2 = (SPacketOtherSkinCustomV4EAG)pkt; + return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, modelID, pkt2.customSkin); + }else if(pkt instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt2 = (SPacketOtherSkinCustomV3EAG)pkt; + return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, modelID, pkt2.customSkin); + }else { + return pkt; + } + } + + public static byte[] convertToV3Raw(byte[] v4data) { + byte[] v3data = new byte[16384]; + for(int i = 0, j, k; i < 4096; ++i) { + j = i * 3; + k = i << 2; + v3data[k + 1] = v4data[j]; + v3data[k + 2] = v4data[j + 1]; + v3data[k + 3] = (byte)(v4data[j + 2] << 1); + v3data[k] = (v4data[j + 2] & 0x80) != 0 ? (byte)0xFF : (byte)0; + } + return v3data; + } + + public static byte[] convertToV4Raw(byte[] v3data) { + byte[] v4data = new byte[12288]; + for(int i = 0, j, k; i < 4096; ++i) { + j = i << 2; + k = i * 3; + v4data[k] = v3data[j + 1]; + v4data[k + 1] = v3data[j + 2]; + v4data[k + 2] = (byte)((v3data[j + 3] >>> 1) | (v3data[j] & 0x80)); + } + return v4data; + } + + public static GameMessagePacket convertToV3(GameMessagePacket v4pkt) { + if(v4pkt instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt = (SPacketOtherSkinCustomV4EAG)v4pkt; + return new SPacketOtherSkinCustomV3EAG(pkt.uuidMost, pkt.uuidLeast, pkt.modelID, convertToV3Raw(pkt.customSkin)); + }else { + return v4pkt; + } + } + + public static GameMessagePacket convertToV4(GameMessagePacket v3pkt) { + if(v3pkt instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt = (SPacketOtherSkinCustomV3EAG)v3pkt; + return new SPacketOtherSkinCustomV4EAG(pkt.uuidMost, pkt.uuidLeast, pkt.modelID, convertToV4Raw(pkt.customSkin)); + }else { + return v3pkt; + } + } + + public static GameMessagePacket convertToV3RewriteUUID(GameMessagePacket v4pkt, long uuidMost, long uuidLeast) { + if(v4pkt instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt = (SPacketOtherSkinCustomV4EAG)v4pkt; + return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, pkt.modelID, convertToV3Raw(pkt.customSkin)); + }else { + return v4pkt; + } + } + + public static GameMessagePacket convertToV4RewriteUUID(GameMessagePacket v3pkt, long uuidMost, long uuidLeast) { + if(v3pkt instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt = (SPacketOtherSkinCustomV3EAG)v3pkt; + return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, pkt.modelID, convertToV4Raw(pkt.customSkin)); + }else { + return v3pkt; + } + } + + public static GameMessagePacket convertToV3RewriteUUIDModel(GameMessagePacket v4pkt, long uuidMost, long uuidLeast, int modelID) { + if(v4pkt instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt = (SPacketOtherSkinCustomV4EAG)v4pkt; + return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, modelID, convertToV3Raw(pkt.customSkin)); + }else { + return v4pkt; + } + } + + public static GameMessagePacket convertToV4RewriteUUIDModel(GameMessagePacket v3pkt, long uuidMost, long uuidLeast, int modelID) { + if(v3pkt instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt = (SPacketOtherSkinCustomV3EAG)v3pkt; + return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, modelID, convertToV4Raw(pkt.customSkin)); + }else { + return v3pkt; + } + } + + public static SkinPacketVersionCache rewriteUUID(SkinPacketVersionCache pkt, long uuidMost, long uuidLeast) { + GameMessagePacket rv3 = null; + GameMessagePacket rv4 = null; + if(pkt.skinPacketV3 != null) { + if(pkt.skinPacketV3 instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt2 = (SPacketOtherSkinCustomV3EAG)pkt.skinPacketV3; + rv3 = new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, pkt2.modelID, pkt2.customSkin); + }else { + rv3 = pkt.skinPacketV3; + } + } + if(pkt.skinPacketV4 != null) { + if(pkt.skinPacketV4 instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt2 = (SPacketOtherSkinCustomV4EAG)pkt.skinPacketV4; + rv4 = new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, pkt2.modelID, pkt2.customSkin); + }else { + rv4 = pkt.skinPacketV4; + } + } + return new SkinPacketVersionCache(rv3, rv4); + } + + public static SkinPacketVersionCache rewriteUUIDModel(SkinPacketVersionCache pkt, long uuidMost, long uuidLeast, int model) { + GameMessagePacket rv3 = null; + GameMessagePacket rv4 = null; + if(pkt.skinPacketV3 != null) { + if(pkt.skinPacketV3 instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt2 = (SPacketOtherSkinCustomV3EAG)pkt.skinPacketV3; + rv3 = new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, model, pkt2.customSkin); + }else { + rv3 = pkt.skinPacketV3; + } + } + if(pkt.skinPacketV4 != null) { + if(pkt.skinPacketV4 instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt2 = (SPacketOtherSkinCustomV4EAG)pkt.skinPacketV4; + rv4 = new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, model, pkt2.customSkin); + }else { + rv4 = pkt.skinPacketV4; + } + } + return new SkinPacketVersionCache(rv3, rv4); + } + + public static GameMessagePacket convertToForceV4(GameMessagePacket v4pkt) { + if(v4pkt instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt = (SPacketOtherSkinCustomV4EAG)v4pkt; + return new SPacketForceClientSkinCustomV4EAG(pkt.modelID, pkt.customSkin); + }else if(v4pkt instanceof SPacketOtherSkinPresetEAG) { + return new SPacketForceClientSkinPresetV4EAG(((SPacketOtherSkinPresetEAG)v4pkt).presetSkin); + }else { + return v4pkt; + } + } + +} diff --git a/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IRelayLogger.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IRelayLogger.java new file mode 100755 index 0000000..f9046e7 --- /dev/null +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IRelayLogger.java @@ -0,0 +1,30 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IRelayLogger { + + void debug(String msg, Object...args); + + void info(String msg, Object...args); + + void warn(String msg, Object...args); + + void error(String msg, Object...args); + + void error(Throwable th); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket.java similarity index 51% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket.java index 33dc3a0..604b26f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket.java @@ -1,165 +1,211 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket { - - private static final Logger logger = LogManager.getLogger("RelayPacket"); - - private static final Map> definedPacketClasses = new HashMap(); - private static final Map,Integer> definedPacketIds = new HashMap(); - - private static void register(int id, Class clazz) { - definedPacketClasses.put(id, clazz); - definedPacketIds.put(clazz, id); - } - - static { - register(0x00, IPacket00Handshake.class); - register(0x01, IPacket01ICEServers.class); - register(0x02, IPacket02NewClient.class); - register(0x03, IPacket03ICECandidate.class); - register(0x04, IPacket04Description.class); - register(0x05, IPacket05ClientSuccess.class); - register(0x06, IPacket06ClientFailure.class); - register(0x07, IPacket07LocalWorlds.class); - register(0x69, IPacket69Pong.class); - register(0x70, IPacket70SpecialUpdate.class); - register(0xFE, IPacketFEDisconnectClient.class); - register(0xFF, IPacketFFErrorCode.class); - } - - public static IPacket readPacket(DataInputStream input) throws IOException { - int i = input.read(); - try { - Class clazz = definedPacketClasses.get(i); - if(clazz == null) { - throw new IOException("Unknown packet type: " + i); - } - IPacket pkt = clazz.newInstance(); - pkt.read(input); - return pkt; - } catch (InstantiationException | IllegalAccessException e) { - throw new IOException("Unknown packet type: " + i); - } - } - - public static byte[] writePacket(IPacket packet) throws IOException { - Integer i = definedPacketIds.get(packet.getClass()); - if(i != null) { - int len = packet.packetLength(); - EaglerOutputStream bao = len == -1 ? new EaglerOutputStream() : - new EaglerOutputStream(len + 1); - bao.write(i); - packet.write(new DataOutputStream(bao)); - byte[] ret = bao.toByteArray(); - if(len != -1 && ret.length != len + 1) { - logger.error("writePacket buffer for packet {} {} by {} bytes", packet.getClass().getSimpleName(), - (len + 1 < ret.length ? "overflowed" : "underflowed"), - (len + 1 < ret.length ? ret.length - len - 1 : len + 1 - ret.length)); - } - return ret; - }else { - throw new IOException("Unknown packet type: " + packet.getClass().getSimpleName()); - } - } - - public void read(DataInputStream input) throws IOException { - } - - public void write(DataOutputStream output) throws IOException { - } - - public int packetLength() { - return -1; - } - - public static String readASCII(InputStream is, int len) throws IOException { - char[] ret = new char[len]; - for(int i = 0; i < len; ++i) { - int j = is.read(); - if(j < 0) { - return null; - } - ret[i] = (char)j; - } - return new String(ret); - } - - public static void writeASCII(OutputStream is, String txt) throws IOException { - for(int i = 0, l = txt.length(); i < l; ++i) { - is.write((int)txt.charAt(i)); - } - } - - public static String readASCII8(InputStream is) throws IOException { - int i = is.read(); - if(i < 0) { - return null; - }else { - return readASCII(is, i); - } - } - - public static void writeASCII8(OutputStream is, String txt) throws IOException { - if(txt == null) { - is.write(0); - }else { - int l = txt.length(); - is.write(l); - for(int i = 0; i < l; ++i) { - is.write((int)txt.charAt(i)); - } - } - } - - public static String readASCII16(InputStream is) throws IOException { - int hi = is.read(); - int lo = is.read(); - if(hi < 0 || lo < 0) { - return null; - }else { - return readASCII(is, (hi << 8) | lo); - } - } - - public static void writeASCII16(OutputStream is, String txt) throws IOException { - if(txt == null) { - is.write(0); - is.write(0); - }else { - int l = txt.length(); - is.write((l >>> 8) & 0xFF); - is.write(l & 0xFF); - for(int i = 0; i < l; ++i) { - is.write((int)txt.charAt(i)); - } - } - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket { + + private static final Map> definedPacketClasses = new HashMap<>(); + private static final Map,Integer> definedPacketIds = new HashMap<>(); + + private static void register(int id, Class clazz) { + definedPacketClasses.put(id, clazz); + definedPacketIds.put(clazz, id); + } + + static { + register(0x00, RelayPacket00Handshake.class); + register(0x01, RelayPacket01ICEServers.class); + register(0x02, RelayPacket02NewClient.class); + register(0x03, RelayPacket03ICECandidate.class); + register(0x04, RelayPacket04Description.class); + register(0x05, RelayPacket05ClientSuccess.class); + register(0x06, RelayPacket06ClientFailure.class); + register(0x07, RelayPacket07LocalWorlds.class); + register(0x69, RelayPacket69Pong.class); + register(0x70, RelayPacket70SpecialUpdate.class); + register(0xFE, RelayPacketFEDisconnectClient.class); + register(0xFF, RelayPacketFFErrorCode.class); + } + + public static RelayPacket readPacket(DataInputStream input, IRelayLogger logger) throws IOException { + int i = input.read(); + try { + Class clazz = definedPacketClasses.get(i); + if(clazz == null) { + throw new IOException("Unknown packet type: " + i); + } + RelayPacket pkt = clazz.newInstance(); + pkt.read(input); + int j = input.available(); + if(j > 0) { + throw new IOException("Packet type " + i + " had " + j + " remaining bytes"); + } + return pkt; + } catch (InstantiationException | IllegalAccessException e) { + logger.error("Could not instanciate packet {}", i); + logger.error(e); + throw new IOException("Unknown packet type: " + i); + } + } + + public static byte[] writePacket(RelayPacket packet, IRelayLogger logger) throws IOException { + Integer i = definedPacketIds.get(packet.getClass()); + if(i != null) { + int len = packet.packetLength(); + ByteArrayOutputStream bao = len == -1 ? new ByteArrayOutputStream() : + new ByteArrayOutputStream(len + 1); + bao.write(i); + packet.write(new DataOutputStream(bao)); + byte[] ret = bao.toByteArray(); + if(len != -1 && ret.length != len + 1) { + logger.debug("writePacket buffer for packet {} {} by {} bytes", packet.getClass().getSimpleName(), + len + 1 < ret.length ? "overflowed" : "underflowed", + len + 1 < ret.length ? ret.length - len - 1 : len + 1 - ret.length); + } + return ret; + }else { + throw new IOException("Unknown packet type: " + packet.getClass().getSimpleName()); + } + } + + public void read(DataInputStream input) throws IOException { + } + + public void write(DataOutputStream output) throws IOException { + } + + public int packetLength() { + return -1; + } + + public static String readASCII(InputStream is, int len) throws IOException { + char[] ret = new char[len]; + for(int i = 0; i < len; ++i) { + int j = is.read(); + if(j < 0) { + throw new EOFException(); + } + ret[i] = (char)j; + } + return new String(ret); + } + + public static void writeASCII(OutputStream is, String txt) throws IOException { + for(int i = 0, l = txt.length(); i < l; ++i) { + is.write((int)txt.charAt(i)); + } + } + + public static String readASCII8(InputStream is) throws IOException { + int i = is.read(); + if(i < 0) { + throw new EOFException(); + }else { + return readASCII(is, i); + } + } + + public static void writeASCII8(OutputStream is, String txt) throws IOException { + if(txt == null) { + is.write(0); + }else { + int l = txt.length(); + is.write(l); + for(int i = 0; i < l; ++i) { + is.write((int)txt.charAt(i)); + } + } + } + + public static String readASCII16(InputStream is) throws IOException { + int hi = is.read(); + int lo = is.read(); + if(hi < 0 || lo < 0) { + throw new EOFException(); + }else { + return readASCII(is, (hi << 8) | lo); + } + } + + public static void writeASCII16(OutputStream is, String txt) throws IOException { + if(txt == null) { + is.write(0); + is.write(0); + }else { + int l = txt.length(); + is.write((l >>> 8) & 0xFF); + is.write(l & 0xFF); + for(int i = 0; i < l; ++i) { + is.write((int)txt.charAt(i)); + } + } + } + + public static byte[] readBytes16(InputStream is) throws IOException { + int hi = is.read(); + int lo = is.read(); + if(hi < 0 || lo < 0) { + throw new EOFException(); + }else { + byte[] ret = new byte[(hi << 8) | lo]; + is.read(ret); + return ret; + } + } + + public static void writeBytes16(OutputStream is, byte[] arr) throws IOException { + if(arr == null) { + is.write(0); + is.write(0); + }else { + is.write((arr.length >>> 8) & 0xFF); + is.write(arr.length & 0xFF); + for(int i = 0; i < arr.length; ++i) { + is.write(arr[i]); + } + } + } + + public static byte[] toASCIIBin(String txt) { + if(txt == null) { + return new byte[0]; + }else { + byte[] ret = new byte[txt.length()]; + for(int i = 0; i < ret.length; ++i) { + ret[i] = (byte)txt.charAt(i); + } + return ret; + } + } + + public static String toASCIIStr(byte[] bin) { + char[] charRet = new char[bin.length]; + for(int i = 0; i < charRet.length; ++i) { + charRet[i] = (char)((int)bin[i] & 0xFF); + } + return new String(charRet); + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket00Handshake.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket00Handshake.java similarity index 83% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket00Handshake.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket00Handshake.java index 1c22490..61999e8 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket00Handshake.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket00Handshake.java @@ -5,7 +5,7 @@ import java.io.DataOutputStream; import java.io.IOException; /** - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -19,16 +19,16 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPacket00Handshake extends IPacket { +public class RelayPacket00Handshake extends RelayPacket { public int connectionType = 0; public int connectionVersion = 1; public String connectionCode = null; - public IPacket00Handshake() { + public RelayPacket00Handshake() { } - public IPacket00Handshake(int connectionType, int connectionVersion, + public RelayPacket00Handshake(int connectionType, int connectionVersion, String connectionCode) { this.connectionType = connectionType; this.connectionVersion = connectionVersion; @@ -39,14 +39,14 @@ public class IPacket00Handshake extends IPacket { public void read(DataInputStream input) throws IOException { connectionType = input.read(); connectionVersion = input.read(); - connectionCode = IPacket.readASCII8(input); + connectionCode = RelayPacket.readASCII8(input); } @Override public void write(DataOutputStream output) throws IOException { output.write(connectionType); output.write(connectionVersion); - IPacket.writeASCII8(output, connectionCode); + RelayPacket.writeASCII8(output, connectionCode); } @Override diff --git a/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket01ICEServers.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket01ICEServers.java new file mode 100755 index 0000000..5c69882 --- /dev/null +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket01ICEServers.java @@ -0,0 +1,105 @@ +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket01ICEServers extends RelayPacket { + + public final Collection servers; + + public static class RelayServer { + + public String address; + public RelayType type; + public String username; + public String password; + + public RelayServer(String address, RelayType type, String username, String password) { + this.address = address; + this.type = type; + this.username = username; + this.password = password; + } + + public String getICEString() { + if(username == null) { + return address; + }else { + return address + ";" + username + ";" + password; + } + } + + } + + public static enum RelayType { + NO_PASSWD, PASSWD; + } + + public RelayPacket01ICEServers() { + this.servers = new ArrayList<>(); + } + + public RelayPacket01ICEServers(Collection servers) { + this.servers = servers; + } + + public void write(DataOutputStream output) throws IOException { + int l = servers.size(); + output.writeShort(l); + Iterator itr = servers.iterator(); + while(itr.hasNext()) { + RelayServer srv = itr.next(); + if(srv.type == RelayType.NO_PASSWD) { + output.write('S'); + }else if(srv.type == RelayType.PASSWD) { + output.write('T'); + }else { + throw new IOException("Unknown/Unsupported Relay Type: " + srv.type.name()); + } + writeASCII16(output, srv.address); + writeASCII8(output, srv.username); + writeASCII8(output, srv.password); + } + } + + public void read(DataInputStream input) throws IOException { + servers.clear(); + int l = input.readUnsignedShort(); + for(int i = 0; i < l; ++i) { + char type = (char)input.read(); + RelayType typeEnum; + if(type == 'S') { + typeEnum = RelayType.NO_PASSWD; + }else if(type == 'T') { + typeEnum = RelayType.PASSWD; + }else { + throw new IOException("Unknown/Unsupported Relay Type: '" + type + "'"); + } + servers.add(new RelayServer( + readASCII16(input), + typeEnum, + readASCII8(input), + readASCII8(input) + )); + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket06ClientFailure.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket02NewClient.java similarity index 86% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket06ClientFailure.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket02NewClient.java index 9506c37..0f7b534 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket06ClientFailure.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket02NewClient.java @@ -5,7 +5,7 @@ import java.io.DataOutputStream; import java.io.IOException; /** - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -19,24 +19,24 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPacket06ClientFailure extends IPacket { +public class RelayPacket02NewClient extends RelayPacket { public String clientId; - public IPacket06ClientFailure() { + public RelayPacket02NewClient(String clientId) { + this.clientId = clientId; } - public IPacket06ClientFailure(String clientId) { - this.clientId = clientId; + public RelayPacket02NewClient() { + } + + public void write(DataOutputStream output) throws IOException { + writeASCII8(output, clientId); } public void read(DataInputStream input) throws IOException { clientId = readASCII8(input); } - - public void write(DataOutputStream output) throws IOException { - writeASCII8(output, clientId); - } public int packetLength() { return 1 + clientId.length(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket03ICECandidate.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket03ICECandidate.java similarity index 65% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket03ICECandidate.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket03ICECandidate.java index 985f040..ccdd1f1 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket03ICECandidate.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket03ICECandidate.java @@ -1,49 +1,58 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket03ICECandidate extends IPacket { - - public String peerId; - public String candidate; - - public IPacket03ICECandidate(String peerId, String desc) { - this.peerId = peerId; - this.candidate = desc; - } - - public IPacket03ICECandidate() { - } - - public void read(DataInputStream input) throws IOException { - peerId = readASCII8(input); - candidate = readASCII16(input); - } - - public void write(DataOutputStream output) throws IOException { - writeASCII8(output, peerId); - writeASCII16(output, candidate); - } - - public int packetLength() { - return 1 + peerId.length() + 2 + candidate.length(); - } - +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket03ICECandidate extends RelayPacket { + + public String peerId; + public byte[] candidate; + + public RelayPacket03ICECandidate() { + } + + public RelayPacket03ICECandidate(String peerId, String desc) { + this.peerId = peerId; + this.candidate = toASCIIBin(desc); + } + + public RelayPacket03ICECandidate(String peerId, byte[] desc) { + this.peerId = peerId; + this.candidate = desc; + } + + public void read(DataInputStream input) throws IOException { + peerId = readASCII8(input); + candidate = readBytes16(input); + } + + public void write(DataOutputStream output) throws IOException { + writeASCII8(output, peerId); + writeBytes16(output, candidate); + } + + public String getCandidateString() { + return candidate == null ? null : toASCIIStr(candidate); + } + + public int packetLength() { + return 1 + peerId.length() + 2 + candidate.length; + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket04Description.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket04Description.java similarity index 65% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket04Description.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket04Description.java index 6bc3f8f..b46c970 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket04Description.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket04Description.java @@ -1,49 +1,58 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket04Description extends IPacket { - - public String peerId; - public String description; - - public IPacket04Description(String peerId, String desc) { - this.peerId = peerId; - this.description = desc; - } - - public IPacket04Description() { - } - - public void read(DataInputStream input) throws IOException { - peerId = readASCII8(input); - description = readASCII16(input); - } - - public void write(DataOutputStream output) throws IOException { - writeASCII8(output, peerId); - writeASCII16(output, description); - } - - public int packetLength() { - return 1 + peerId.length() + 2 + description.length(); - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket04Description extends RelayPacket { + + public String peerId; + public byte[] description; + + public RelayPacket04Description() { + } + + public RelayPacket04Description(String peerId, String desc) { + this.peerId = peerId; + this.description = toASCIIBin(desc); + } + + public RelayPacket04Description(String peerId, byte[] desc) { + this.peerId = peerId; + this.description = desc; + } + + public void read(DataInputStream input) throws IOException { + peerId = readASCII8(input); + description = readBytes16(input); + } + + public void write(DataOutputStream output) throws IOException { + writeASCII8(output, peerId); + writeBytes16(output, description); + } + + public String getDescriptionString() { + return description == null ? null : toASCIIStr(description); + } + + public int packetLength() { + return 1 + peerId.length() + 2 + description.length; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket05ClientSuccess.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket05ClientSuccess.java similarity index 85% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket05ClientSuccess.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket05ClientSuccess.java index 240e964..e49d2fe 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket05ClientSuccess.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket05ClientSuccess.java @@ -5,7 +5,7 @@ import java.io.DataOutputStream; import java.io.IOException; /** - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -19,14 +19,14 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPacket05ClientSuccess extends IPacket { +public class RelayPacket05ClientSuccess extends RelayPacket { public String clientId; - public IPacket05ClientSuccess() { + public RelayPacket05ClientSuccess() { } - public IPacket05ClientSuccess(String clientId) { + public RelayPacket05ClientSuccess(String clientId) { this.clientId = clientId; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket02NewClient.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket06ClientFailure.java similarity index 70% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket02NewClient.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket06ClientFailure.java index 76ab9f5..9b3d948 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket02NewClient.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket06ClientFailure.java @@ -1,36 +1,45 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.IOException; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket02NewClient extends IPacket { - - public String clientId; - - public IPacket02NewClient(String clientId) { - this.clientId = clientId; - } - - public IPacket02NewClient() { - } - - public void read(DataInputStream input) throws IOException { - clientId = readASCII8(input); - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket06ClientFailure extends RelayPacket { + + public String clientId; + + public RelayPacket06ClientFailure() { + } + + public RelayPacket06ClientFailure(String clientId) { + this.clientId = clientId; + } + + public void read(DataInputStream input) throws IOException { + clientId = readASCII8(input); + } + + public void write(DataOutputStream output) throws IOException { + writeASCII8(output, clientId); + } + + public int packetLength() { + return 1 + clientId.length(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket07LocalWorlds.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket07LocalWorlds.java similarity index 55% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket07LocalWorlds.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket07LocalWorlds.java index 923e179..3baeee2 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket07LocalWorlds.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket07LocalWorlds.java @@ -1,50 +1,86 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket07LocalWorlds extends IPacket { - - public static class LocalWorld { - - public final String worldName; - public final String worldCode; - - public LocalWorld(String worldName, String worldCode) { - this.worldName = worldName; - this.worldCode = worldCode; - } - - } - - public final List worldsList; - - public IPacket07LocalWorlds() { - this.worldsList = new ArrayList(); - } - - public void read(DataInputStream input) throws IOException { - int l = input.read(); - for(int i = 0; i < l; ++i) { - worldsList.add(new LocalWorld(readASCII8(input), readASCII8(input))); - } - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket07LocalWorlds extends RelayPacket { + + public static class LocalWorld { + + public final String worldName; + public final String worldCode; + + public LocalWorld(String worldName, String worldCode) { + this.worldName = worldName; + this.worldCode = worldCode; + } + + } + + public List worldsList; + + public RelayPacket07LocalWorlds() { + } + + public RelayPacket07LocalWorlds(List worldsList) { + this.worldsList = worldsList; + } + + public void write(DataOutputStream output) throws IOException { + if(worldsList == null) { + output.write(0); + }else { + int i = worldsList.size(); + if(i > 255) { + i = 255; + } + output.write(i); + for(int j = 0; j < i; ++j) { + LocalWorld w = worldsList.get(j); + writeASCII8(output, w.worldName); + writeASCII8(output, w.worldCode); + } + } + } + + public void read(DataInputStream input) throws IOException { + int l = input.read(); + if(worldsList == null) { + worldsList = new ArrayList<>(l); + }else { + worldsList.clear(); + } + for(int i = 0; i < l; ++i) { + worldsList.add(new LocalWorld(readASCII8(input), readASCII8(input))); + } + } + + public int packetLength() { + int accum = 1; + if(worldsList != null) { + for(int i = 0, l = worldsList.size(); i < l; ++i) { + LocalWorld j = worldsList.get(i); + accum += 2 + j.worldName.length() + j.worldCode.length(); + } + } + return accum; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket69Pong.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket69Pong.java similarity index 70% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket69Pong.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket69Pong.java index 589ca92..ac43db8 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket69Pong.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket69Pong.java @@ -1,45 +1,56 @@ -package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; - -import java.io.DataInputStream; -import java.io.IOException; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class IPacket69Pong extends IPacket { - - public int protcolVersion; - public String comment; - public String brand; - - public IPacket69Pong(int protcolVersion, String comment, String brand) { - if(comment.length() > 255) { - comment = comment.substring(0, 256); - } - this.protcolVersion = protcolVersion; - this.comment = comment; - this.brand = brand; - } - - public IPacket69Pong() { - } - - public void read(DataInputStream output) throws IOException { - protcolVersion = output.read(); - comment = readASCII8(output); - brand = readASCII8(output); - } - -} +package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayPacket69Pong extends RelayPacket { + + public int protcolVersion; + public String comment; + public String brand; + + public RelayPacket69Pong(int protcolVersion, String comment, String brand) { + if(comment.length() > 255) { + comment = comment.substring(0, 256); + } + this.protcolVersion = protcolVersion; + this.comment = comment; + this.brand = brand; + } + + public RelayPacket69Pong() { + } + + public void write(DataOutputStream output) throws IOException { + output.write(protcolVersion); + writeASCII8(output, comment); + writeASCII8(output, brand); + } + + public void read(DataInputStream output) throws IOException { + protcolVersion = output.read(); + comment = readASCII8(output); + brand = readASCII8(output); + } + + public int packetLength() { + return 3 + comment.length() + brand.length(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket70SpecialUpdate.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket70SpecialUpdate.java similarity index 90% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket70SpecialUpdate.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket70SpecialUpdate.java index 1bdd463..5d52292 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacket70SpecialUpdate.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacket70SpecialUpdate.java @@ -19,17 +19,17 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPacket70SpecialUpdate extends IPacket { +public class RelayPacket70SpecialUpdate extends RelayPacket { public static final int OPERATION_UPDATE_CERTIFICATE = 0x69; public int operation; public byte[] updatePacket; - public IPacket70SpecialUpdate() { + public RelayPacket70SpecialUpdate() { } - public IPacket70SpecialUpdate(int operation, byte[] updatePacket) { + public RelayPacket70SpecialUpdate(int operation, byte[] updatePacket) { this.operation = operation; this.updatePacket = updatePacket; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacketFEDisconnectClient.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacketFEDisconnectClient.java similarity index 87% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacketFEDisconnectClient.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacketFEDisconnectClient.java index 77a787f..866a0af 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacketFEDisconnectClient.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacketFEDisconnectClient.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.nio.ByteBuffer; /** - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -20,7 +20,7 @@ import java.nio.ByteBuffer; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPacketFEDisconnectClient extends IPacket { +public class RelayPacketFEDisconnectClient extends RelayPacket { public static final int TYPE_FINISHED_SUCCESS = 0x00; public static final int TYPE_FINISHED_FAILED = 0x01; @@ -34,10 +34,10 @@ public class IPacketFEDisconnectClient extends IPacket { public int code; public String reason; - public IPacketFEDisconnectClient() { + public RelayPacketFEDisconnectClient() { } - public IPacketFEDisconnectClient(String clientId, int code, String reason) { + public RelayPacketFEDisconnectClient(String clientId, int code, String reason) { this.clientId = clientId; this.code = code; this.reason = reason; @@ -48,7 +48,7 @@ public class IPacketFEDisconnectClient extends IPacket { code = input.read(); reason = readASCII16(input); } - + public void write(DataOutputStream output) throws IOException { writeASCII8(output, clientId); output.write(code); @@ -56,9 +56,9 @@ public class IPacketFEDisconnectClient extends IPacket { } public int packetLength() { - return -1; + return 1 + 1 + 2 + clientId.length() + (reason != null ? reason.length() : 0); } - + public static final ByteBuffer ratelimitPacketTooMany = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x00 }); public static final ByteBuffer ratelimitPacketBlock = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x01 }); public static final ByteBuffer ratelimitPacketBlockLock = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x02 }); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacketFFErrorCode.java b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacketFFErrorCode.java similarity index 92% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacketFFErrorCode.java rename to src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacketFFErrorCode.java index 4475d75..9ca959c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/IPacketFFErrorCode.java +++ b/src/protocol-relay/java/net/lax1dude/eaglercraft/v1_8/sp/relay/pkt/RelayPacketFFErrorCode.java @@ -5,7 +5,7 @@ import java.io.DataOutputStream; import java.io.IOException; /** - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -19,7 +19,7 @@ import java.io.IOException; * POSSIBILITY OF SUCH DAMAGE. * */ -public class IPacketFFErrorCode extends IPacket { +public class RelayPacketFFErrorCode extends RelayPacket { public static final int TYPE_INTERNAL_ERROR = 0x00; public static final int TYPE_PROTOCOL_VERSION = 0x01; @@ -29,9 +29,9 @@ public class IPacketFFErrorCode extends IPacket { public static final int TYPE_INCORRECT_CODE = 0x05; public static final int TYPE_SERVER_DISCONNECTED = 0x06; public static final int TYPE_UNKNOWN_CLIENT = 0x07; - + public static final String[] packetTypes = new String[0x08]; - + static { packetTypes[TYPE_INTERNAL_ERROR] = "TYPE_INTERNAL_ERROR"; packetTypes[TYPE_PROTOCOL_VERSION] = "TYPE_PROTOCOL_VERSION"; @@ -42,7 +42,7 @@ public class IPacketFFErrorCode extends IPacket { packetTypes[TYPE_SERVER_DISCONNECTED] = "TYPE_SERVER_DISCONNECTED"; packetTypes[TYPE_UNKNOWN_CLIENT] = "TYPE_UNKNOWN_CLIENT"; } - + public static String code2string(int i) { if(i >= 0 || i < packetTypes.length) { return packetTypes[i]; @@ -50,18 +50,18 @@ public class IPacketFFErrorCode extends IPacket { return "UNKNOWN"; } } - + public int code; public String desc; - - public IPacketFFErrorCode() { + + public RelayPacketFFErrorCode() { } - - public IPacketFFErrorCode(int code, String desc) { + + public RelayPacketFFErrorCode(int code, String desc) { this.code = code; this.desc = desc; } - + @Override public void read(DataInputStream input) throws IOException { code = input.read(); diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuAssets.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuAssets.java new file mode 100755 index 0000000..4061a3a --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuAssets.java @@ -0,0 +1,35 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuAssets { + + public static String loadResourceString(String res) { + return EagRuntime.getResourceString(res); + } + + public static byte[] loadResourceBytes(String res) { + return EagRuntime.getResourceBytes(res); + } + + public static void freeBootMenuResourceRepo() { + PlatformAssets.freeAssetRepoTeaVM(); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuConstants.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuConstants.java new file mode 100755 index 0000000..ce16955 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuConstants.java @@ -0,0 +1,53 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuConstants { + + public static final EaglercraftUUID UUID_ORIGIN_UNSIGNED_CLASSES_JS = new EaglercraftUUID(0x738248F88FF1446EL, 0xA834D40120DD8EB5L); + public static final EaglercraftUUID UUID_ORIGIN_SIGNED_SIGNATURE = new EaglercraftUUID(0xB55252A38A6F4291L, 0x8DB68BF94B3E6FEDL); + public static final EaglercraftUUID UUID_ORIGIN_SIGNED_BUNDLE = new EaglercraftUUID(0xCE298D98E9084597L, 0x9EB2501EAC6D720BL); + + public static final EaglercraftUUID UUID_CLIENT_DATA_ORIGIN = new EaglercraftUUID(0xB673DAD0EF4407BL, 0xBE12C8E5BD5A2CBDL); + public static final EaglercraftUUID UUID_CLIENT_LAUNCH_ORIGIN = new EaglercraftUUID(0x74FB063984A24D1AL, 0x8E1D2FC39C21EA1EL); + + public static final String client_projectForkName = EaglercraftVersion.projectForkName; + public static final String client_projectForkVendor = EaglercraftVersion.projectForkVendor; + public static final String client_projectForkVersion = EaglercraftVersion.projectForkVersion; + + public static final String client_projectOriginName = EaglercraftVersion.projectOriginName; + public static final String client_projectOriginAuthor = EaglercraftVersion.projectOriginAuthor; + public static final String client_projectOriginVersion = EaglercraftVersion.projectOriginVersion; + public static final String client_projectOriginRevision = EaglercraftVersion.projectOriginRevision; + + public static final String cssClassPrefix = "_eaglercraftX_"; + public static final String cssClassPrefixBootMenu = cssClassPrefix + "boot_menu_"; + + public static final String bootMenuDatabaseName = "_net_lax1dude_eaglercraft_v1_8_boot_menu_BootMenuDatastore_1_8_8_main"; + + public static String getBootMenuFlagsKeyName() { + String pfx = EagRuntime.getConfiguration().getLocalStorageNamespace(); + if(pfx == null) { + pfx = EaglercraftVersion.localStorageNamespace; + } + return pfx + ".showBootMenu"; + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDOM.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDOM.java new file mode 100755 index 0000000..25282e8 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDOM.java @@ -0,0 +1,203 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.HashMap; +import java.util.Map; + +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLInputElement; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuDOM { + + public final HTMLElement content_view_selection; + public final HTMLElement content_selection; + public final HTMLElement content_view_editor; + public final HTMLElement header_title; + public final HTMLElement launch_conf_val_profile_name; + public final HTMLElement launch_conf_val_data_format; + public final HTMLElement launch_conf_val_launch_type; + public final Map launch_conf_val_launch_type_opts; + public final HTMLElement launch_conf_join_server; + public final HTMLElement launch_conf_val_join_server; + public final HTMLElement launch_conf_opts_name; + public final HTMLElement launch_conf_val_opts_name; + public final HTMLElement launch_conf_assetsURI; + public final HTMLElement launch_conf_val_assetsURI; + public final HTMLElement launch_conf_container; + public final HTMLElement launch_conf_val_container; + public final HTMLElement launch_conf_main_func; + public final HTMLElement launch_conf_val_main_func; + public final HTMLElement launch_conf_clear_cookies; + public final HTMLElement launch_conf_val_clear_cookies; + public final HTMLElement launch_opt_editor; + public final HTMLElement popup; + public final HTMLElement popup_view_confirm; + public final HTMLElement popup_confirm_title; + public final HTMLElement popup_confirm_opts; + public final HTMLElement popup_view_selection; + public final HTMLElement popup_selection_title; + public final HTMLElement popup_selection; + public final HTMLElement popup_view_input; + public final HTMLElement popup_input_title; + public final HTMLElement popup_input_val; + public final HTMLElement popup_input_opt_cancel; + public final HTMLElement popup_input_opt_done; + public final HTMLElement footer_text_boot_select; + public final HTMLElement footer_text_boot_select_count; + public final HTMLElement footer_text_boot_countdown; + public final HTMLElement footer_text_menu_select; + public final HTMLElement footer_text_opts_editor; + public final HTMLElement footer_text_opts_editor_alt; + public final HTMLElement footer_text_boot_order; + + public BootMenuDOM(HTMLElement parentElement) { + content_view_selection = selectHelper(parentElement, "content_view_selection"); + content_selection = selectHelper(parentElement, "content_selection"); + content_view_editor = selectHelper(parentElement, "content_view_editor"); + header_title = selectHelper(parentElement, "header_title"); + launch_conf_val_profile_name = selectHelper(parentElement, "launch_conf_val_profile_name"); + launch_conf_val_data_format = selectHelper(parentElement, "launch_conf_val_data_format"); + launch_conf_val_launch_type = selectHelper(parentElement, "launch_conf_val_launch_type"); + launch_conf_val_launch_type_opts = new HashMap<>(); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.EAGLERX_V1, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=EAGLERX_V1]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.EAGLERX_SIGNED_V1, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=EAGLERX_SIGNED_V1]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.EAGLER_1_5_V2, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=EAGLER_1_5_V2]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.EAGLER_1_5_V1, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=EAGLER_1_5_V1]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.EAGLER_BETA_V1, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=EAGLER_BETA_V1]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.PEYTON_V1, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=PEYTON_V1]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.PEYTON_V2, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=PEYTON_V2]")); + launch_conf_val_launch_type_opts.put(EnumClientLaunchType.STANDARD_OFFLINE_V1, selectHelper(parentElement, "launch_conf_val_launch_type_opt[value=STANDARD_OFFLINE_V1]")); + launch_conf_join_server = selectHelper(parentElement, "launch_conf_join_server"); + launch_conf_val_join_server = selectHelper(parentElement, "launch_conf_val_join_server"); + launch_conf_opts_name = selectHelper(parentElement, "launch_conf_opts_name"); + launch_conf_val_opts_name = selectHelper(parentElement, "launch_conf_val_opts_name"); + launch_conf_assetsURI = selectHelper(parentElement, "launch_conf_assetsURI"); + launch_conf_val_assetsURI = selectHelper(parentElement, "launch_conf_val_assetsURI"); + launch_conf_container = selectHelper(parentElement, "launch_conf_container"); + launch_conf_val_container = selectHelper(parentElement, "launch_conf_val_container"); + launch_conf_main_func = selectHelper(parentElement, "launch_conf_main_func"); + launch_conf_val_main_func = selectHelper(parentElement, "launch_conf_val_main_func"); + launch_conf_clear_cookies = selectHelper(parentElement, "launch_conf_clear_cookies"); + launch_conf_val_clear_cookies = selectHelper(parentElement, "launch_conf_val_clear_cookies"); + launch_opt_editor = selectHelper(parentElement, "launch_opt_editor"); + popup = selectHelper(parentElement, "popup"); + popup_view_confirm = selectHelper(parentElement, "popup_view_confirm"); + popup_confirm_title = selectHelper(parentElement, "popup_confirm_title"); + popup_confirm_opts = selectHelper(parentElement, "popup_confirm_opts"); + popup_view_selection = selectHelper(parentElement, "popup_view_selection"); + popup_selection_title = selectHelper(parentElement, "popup_selection_title"); + popup_selection = selectHelper(parentElement, "popup_selection"); + popup_view_input = selectHelper(parentElement, "popup_view_input"); + popup_input_title = selectHelper(parentElement, "popup_input_title"); + popup_input_val = selectHelper(parentElement, "popup_input_val"); + popup_input_opt_cancel = selectHelper(parentElement, "popup_input_opt_cancel"); + popup_input_opt_done = selectHelper(parentElement, "popup_input_opt_done"); + footer_text_boot_select = selectHelper(parentElement, "footer_text_boot_select"); + footer_text_boot_select_count = selectHelper(parentElement, "footer_text_boot_select_count"); + footer_text_boot_countdown = selectHelper(parentElement, "footer_text_boot_countdown"); + footer_text_menu_select = selectHelper(parentElement, "footer_text_menu_select"); + footer_text_opts_editor = selectHelper(parentElement, "footer_text_opts_editor"); + footer_text_opts_editor_alt = selectHelper(parentElement, "footer_text_opts_editor_alt"); + footer_text_boot_order = selectHelper(parentElement, "footer_text_boot_order"); + } + + public void registerEventHandlers() { + launch_conf_val_profile_name.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_profile_name); + }); + launch_conf_val_data_format.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_data_format); + }); + launch_conf_val_launch_type.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_launch_type); + }); + launch_conf_val_join_server.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_join_server); + }); + launch_conf_val_opts_name.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_opts_name); + }); + launch_conf_val_assetsURI.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_assetsURI); + }); + launch_conf_val_container.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_container); + }); + launch_conf_val_main_func.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_main_func); + }); + launch_conf_val_clear_cookies.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_conf_val_clear_cookies); + }); + launch_opt_editor.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(launch_opt_editor); + }); + popup_input_val.addEventListener("change", (evt) -> { + BootMenuMain.fireChangeEvent(popup_input_val); + }); + popup_input_opt_cancel.addEventListener("click", (evt) -> { + BootMenuMain.fireClickEvent(popup_input_opt_cancel); + }); + popup_input_opt_done.addEventListener("click", (evt) -> { + BootMenuMain.fireClickEvent(popup_input_opt_done); + }); + popup_input_opt_cancel.addEventListener("mouseover", (evt) -> { + BootMenuMain.fireMouseOverEvent(popup_input_opt_cancel); + }); + popup_input_opt_done.addEventListener("mouseover", (evt) -> { + BootMenuMain.fireMouseOverEvent(popup_input_opt_done); + }); + } + + public static void show(HTMLElement el) { + el.getStyle().setProperty("display", "block"); + } + + public static void hide(HTMLElement el) { + el.getStyle().setProperty("display", "none"); + } + + public static void setValue(HTMLElement el, String value) { + ((HTMLInputElement)el).setValue(value); + } + + public static String getValue(HTMLElement el) { + return ((HTMLInputElement)el).getValue(); + } + + public static void setChecked(HTMLElement el, boolean checked) { + ((HTMLInputElement)el).setChecked(checked); + } + + public static boolean getChecked(HTMLElement el) { + return ((HTMLInputElement)el).isChecked(); + } + + public static void setDisabled(HTMLElement el, boolean disabled) { + ((HTMLInputElement)el).setDisabled(disabled); + } + + private static HTMLElement selectHelper(HTMLElement parent, String name) { + name = "." + BootMenuConstants.cssClassPrefixBootMenu + name; + HTMLElement ret = parent.querySelector(name); + if(ret == null) { + throw new RuntimeException("Failed to select \"" + name + "\" from boot menu!"); + } + return ret; + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDataManager.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDataManager.java new file mode 100755 index 0000000..3d12626 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDataManager.java @@ -0,0 +1,464 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.teavm.jso.browser.Storage; +import org.teavm.jso.browser.Window; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuDataManager { + + protected static final Logger logger = LogManager.getLogger("BootMenuDataManager"); + + protected BootMenuDatastore datastore; + + public final Set existingBlobs = new HashSet<>(); + public final Map clientDatas = new HashMap<>(); + public final Map launchDatas = new HashMap<>(); + public final List launchDatasList = new ArrayList<>(); + public final List launchOrderList = new ArrayList<>(); + + public int confBootTimeout = 0; + public String confMenuTitle = "EaglercraftX 1.8 Boot Manager"; + + public BootMenuDataManager(BootMenuDatastore datastore) { + this.datastore = datastore; + this.loadAllData(); + this.loadAdditionalConf(); + } + + public void installNewClientData(LaunchConfigEntry launchConfig, ClientDataEntry clientData, Map clientBlobs, boolean rotateUUIDs) { + if(rotateUUIDs) { + EaglercraftUUID rotatedLaunchUUID = EaglercraftUUID.randomUUID(); + EaglercraftUUID rotatedClientUUID = EaglercraftUUID.randomUUID(); + launchConfig = launchConfig.rotateUUIDs(rotatedLaunchUUID, rotatedClientUUID); + clientData = clientData.rotateUUID(rotatedClientUUID); + } + if(launchDatas.containsKey(launchConfig.uuid) || BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN.equals(launchConfig.uuid)) { + throw new IllegalArgumentException("Launch data UUID \"" + launchConfig.uuid + "\" already exists!"); + } + if(clientDatas.containsKey(clientData.uuid) || BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(launchConfig.uuid)) { + throw new IllegalArgumentException("Client data UUID \"" + clientData.uuid + "\" already exists!"); + } + if(!launchConfig.clientDataUUID.equals(clientData.uuid)) { + throw new IllegalArgumentException("Mismatched client data UUID and launch configuration!"); + } + logger.info("Installing new client data \"{}\"...", clientData.uuid); + if(clientBlobs != null && !clientBlobs.isEmpty()) { + for(Entry etr : clientBlobs.entrySet()) { + EaglercraftUUID k = etr.getKey(); + byte[] v = etr.getValue(); + String name = "blobs/" + k; + if(!datastore.containsKey(name)) { + logger.info(" - Adding blob to datastore \"{}\" ({} bytes long)", name, v.length); + datastore.setItem(name, v); + existingBlobs.add(k); + }else { + logger.info(" - Skipping blob \"{}\" because it already exists", name); + } + } + } + String name = "clientDatas/" + clientData.uuid; + logger.info(" - Writing client data: \"{}\"", name); + JSONObject obj = new JSONObject(); + clientData.writeJSON(obj); + datastore.setItem(name, obj.toString().getBytes(StandardCharsets.UTF_8)); + clientDatas.put(clientData.uuid, clientData); + installNewLaunchConfig(launchConfig, false); + } + + public void installNewLaunchConfig(LaunchConfigEntry launchConfig, ClientDataEntry clientData, Map clientBlobs, boolean rotateUUIDs) { + if(rotateUUIDs) { + EaglercraftUUID rotatedLaunchUUID = EaglercraftUUID.randomUUID(); + launchConfig = launchConfig.rotateUUIDs(rotatedLaunchUUID, launchConfig.clientDataUUID); + } + if(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN.equals(launchConfig.uuid)) { + throw new IllegalArgumentException("The origin launch configuration cannot be overwritten!"); + } + if(!clientDatas.containsKey(launchConfig.clientDataUUID) && !BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(launchConfig.clientDataUUID)) { + logger.info("Installing new client data \"{}\"...", clientData.uuid); + if(clientBlobs != null && !clientBlobs.isEmpty()) { + for(Entry etr : clientBlobs.entrySet()) { + EaglercraftUUID k = etr.getKey(); + byte[] v = etr.getValue(); + String name = "blobs/" + k; + if(!datastore.containsKey(name)) { + logger.info(" - Adding blob to datastore \"{}\" ({} bytes long)", name, v.length); + datastore.setItem(name, v); + existingBlobs.add(k); + }else { + logger.info(" - Skipping blob \"{}\" because it already exists", name); + } + } + } + String name = "clientDatas/" + clientData.uuid; + logger.info(" - Writing client data: \"{}\"", name); + JSONObject obj = new JSONObject(); + clientData.writeJSON(obj); + datastore.setItem(name, obj.toString().getBytes(StandardCharsets.UTF_8)); + clientDatas.put(clientData.uuid, clientData); + } + logger.info("Installing new launch config \"{}\"...", launchConfig.uuid); + String name = "launchDatas/" + launchConfig.uuid; + JSONObject obj = new JSONObject(); + launchConfig.writeJSON(obj); + datastore.setItem(name, obj.toString().getBytes(StandardCharsets.UTF_8)); + boolean writeManifest = false; + if(launchDatas.put(launchConfig.uuid, launchConfig) == null) { + launchDatasList.add(launchConfig.uuid); + writeManifest = true; + } + if(!launchOrderList.contains(launchConfig.uuid)) { + launchOrderList.add(launchConfig.uuid); + writeManifest = true; + } + if(writeManifest) { + writeManifest(); + } + } + + public void installNewLaunchConfig(LaunchConfigEntry launchConfig, boolean rotateUUIDs) { + if(rotateUUIDs) { + EaglercraftUUID rotatedLaunchUUID = EaglercraftUUID.randomUUID(); + launchConfig = launchConfig.rotateUUIDs(rotatedLaunchUUID, launchConfig.clientDataUUID); + } + if(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN.equals(launchConfig.uuid)) { + throw new IllegalArgumentException("The origin launch configuration cannot be overwritten!"); + } + if(!clientDatas.containsKey(launchConfig.clientDataUUID) && !BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(launchConfig.uuid)) { + throw new IllegalArgumentException("Client data UUID \"" + launchConfig.clientDataUUID + "\" does not exist!"); + } + logger.info("Installing new launch config \"{}\"...", launchConfig.uuid); + String name = "launchDatas/" + launchConfig.uuid; + JSONObject obj = new JSONObject(); + launchConfig.writeJSON(obj); + datastore.setItem(name, obj.toString().getBytes(StandardCharsets.UTF_8)); + boolean writeManifest = false; + if(launchDatas.put(launchConfig.uuid, launchConfig) == null) { + launchDatasList.add(launchConfig.uuid); + writeManifest = true; + } + if(!launchOrderList.contains(launchConfig.uuid)) { + launchOrderList.add(launchConfig.uuid); + writeManifest = true; + } + if(writeManifest) { + writeManifest(); + } + } + + public void deleteLaunchConfig(EaglercraftUUID launchConfig) { + if(launchDatas.remove(launchConfig) != null) { + boolean removed = false; + while(launchDatasList.remove(launchConfig)) { + removed = true; + } + while(launchOrderList.remove(launchConfig)) { + removed = true; + } + String name = "launchDatas/" + launchConfig; + logger.info("Deleting launch config \"{}\" from datastore", name); + if(!datastore.deleteItem(name)) { + logger.warn("Failed to delete file! Removing it from the list anyway..."); + } + if(removed) { + writeManifest(); + } + garbageCollectClientDatas(); + } + } + + protected void loadAllData() { + logger.info("Loading custom boot configurations from datastore..."); + existingBlobs.clear(); + clientDatas.clear(); + launchDatas.clear(); + launchDatasList.clear(); + launchOrderList.clear(); + byte[] manifestBytes = datastore.getItem("manifest.json"); + if(manifestBytes == null) { + return; + } + List profilesToLoad; + try { + JSONArray arr = (new JSONObject(new String(manifestBytes, StandardCharsets.UTF_8))).getJSONArray("launchProfiles"); + profilesToLoad = new ArrayList<>(arr.length()); + for(int i = 0, l = arr.length(); i < l; ++i) { + profilesToLoad.add(EaglercraftUUID.fromString(arr.getString(i))); + } + arr = (new JSONObject(new String(manifestBytes, StandardCharsets.UTF_8))).getJSONArray("launchOrderList"); + for(int i = 0, l = arr.length(); i < l; ++i) { + launchOrderList.add(EaglercraftUUID.fromString(arr.getString(i))); + } + }catch(JSONException | IllegalArgumentException exp) { + logger.error("Manifest is corrupt!"); + logger.error(exp); + return; + } + for(EaglercraftUUID uuid : profilesToLoad) { + if(loadLaunchDataFromStore(uuid) != null) { + launchDatasList.add(uuid); + } + } + logger.info("Loading {} client(s) successfully", clientDatas.size()); + logger.info("Loading {} profile(s) successfully", launchDatas.size()); + } + + protected LaunchConfigEntry loadLaunchDataFromStore(EaglercraftUUID uuid) { + LaunchConfigEntry ret = launchDatas.get(uuid); + if(ret != null) { + return ret; + } + String name = "launchDatas/" + uuid; + byte[] fileData = datastore.getItem(name); + if(fileData == null) { + logger.error("Could not locate launch data \"{}\"!", name); + return null; + } + try { + ret = new LaunchConfigEntry(uuid, new JSONObject(new String(fileData, StandardCharsets.UTF_8))); + }catch(JSONException | IllegalArgumentException exp) { + logger.error("Launch data \"{}\" is corrupt!", name); + logger.error(exp); + return null; + } + if(!BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(ret.clientDataUUID)) { + if(loadClientDataFromStore(ret.clientDataUUID) == null) { + logger.error("Client data \"{}\" for launch data \"{}\" is missing/corrupt!", ret.clientDataUUID, name); + return null; + } + } + launchDatas.put(uuid, ret); + return ret; + } + + protected ClientDataEntry loadClientDataFromStore(EaglercraftUUID uuid) { + ClientDataEntry ret = clientDatas.get(uuid); + if(ret != null) { + return ret; + } + String name = "clientDatas/" + uuid; + byte[] fileData = datastore.getItem(name); + if(fileData == null) { + logger.error("Could not locate client data \"{}\"!", name); + return null; + } + try { + ret = new ClientDataEntry(uuid, new JSONObject(new String(fileData, StandardCharsets.UTF_8))); + }catch(JSONException | IllegalArgumentException exp) { + logger.error("Client data \"{}\" is corrupt!", name); + logger.error(exp); + return null; + } + existingBlobs.addAll(ret.getReferencedBlobs()); + clientDatas.put(uuid, ret); + return ret; + } + + public void writeManifest() { + JSONObject manifest = new JSONObject(); + JSONArray launchProfileArray = new JSONArray(); + for(EaglercraftUUID uuid : launchDatasList) { + launchProfileArray.put(uuid.toString()); + } + manifest.put("launchProfiles", launchProfileArray); + JSONArray launchOrderListArray = new JSONArray(); + for(EaglercraftUUID uuid : launchOrderList) { + launchOrderListArray.put(uuid.toString()); + } + manifest.put("launchOrderList", launchOrderListArray); + datastore.setItem("manifest.json", manifest.toString().getBytes(StandardCharsets.UTF_8)); + } + + protected void garbageCollectClientDatas() { + Set referencedClientData = new HashSet<>(); + for(LaunchConfigEntry etr : launchDatas.values()) { + referencedClientData.add(etr.clientDataUUID); + } + Set toDelete = new HashSet<>(clientDatas.keySet()); + toDelete.removeAll(referencedClientData); + boolean garbageCollectBlobs = !toDelete.isEmpty(); + if(garbageCollectBlobs) { + for(EaglercraftUUID del : toDelete) { + clientDatas.remove(del); + String name = "clientDatas/" + del; + logger.info("Deleting orphaned client \"{}\" from datastore", name); + datastore.deleteItem(name); + } + garbageCollectClientBlobs(); + } + } + + protected void garbageCollectClientBlobs() { + Set referencedClientBlob = new HashSet<>(); + for(ClientDataEntry etr : clientDatas.values()) { + referencedClientBlob.addAll(etr.getReferencedBlobs()); + } + Set toDelete = new HashSet<>(existingBlobs); + toDelete.removeAll(referencedClientBlob); + if(!toDelete.isEmpty()) { + for(EaglercraftUUID del : toDelete) { + existingBlobs.remove(del); + String name = "blobs/" + del; + logger.info("Deleting orphaned blob \"{}\" from datastore", name); + if(!datastore.deleteItem(name)) { + logger.warn("Failed to delete file! Skipping..."); + } + } + } + } + + public void garbageCollectAll() { + logger.info("Garbage-collecting boot menu data store"); + Set existingClients = new HashSet<>(); + Set existingLaunches = new HashSet<>(); + Set existingBlobs = new HashSet<>(); + Set orphanedFiles = new HashSet<>(); + datastore.iterateAllKeys((str) -> { + if(str.startsWith("clientDatas/")) { + try { + existingClients.add(EaglercraftUUID.fromString(str.substring(12))); + }catch(IllegalArgumentException ex) { + orphanedFiles.add(str); + } + }else if(str.startsWith("launchDatas/")) { + try { + existingLaunches.add(EaglercraftUUID.fromString(str.substring(12))); + }catch(IllegalArgumentException ex) { + orphanedFiles.add(str); + } + }else if(str.startsWith("blobs/")) { + try { + existingBlobs.add(EaglercraftUUID.fromString(str.substring(6))); + }catch(IllegalArgumentException ex) { + orphanedFiles.add(str); + } + } + }); + Set toDelete = new HashSet<>(existingLaunches); + toDelete.removeAll(launchDatas.keySet()); + if(!toDelete.isEmpty()) { + for(EaglercraftUUID del : toDelete) { + String name = "launchDatas/" + del; + logger.info("Deleting orphaned launch \"{}\" from datastore", name); + if(!datastore.deleteItem(name)) { + logger.warn("Failed to delete file! Skipping..."); + } + } + } + Set referencedData = new HashSet<>(); + for(LaunchConfigEntry etr : launchDatas.values()) { + referencedData.add(etr.clientDataUUID); + } + toDelete = new HashSet<>(existingClients); + toDelete.removeAll(referencedData); + if(!toDelete.isEmpty()) { + for(EaglercraftUUID del : toDelete) { + clientDatas.remove(del); + String name = "clientDatas/" + del; + logger.info("Deleting orphaned client \"{}\" from datastore", name); + if(!datastore.deleteItem(name)) { + logger.warn("Failed to delete file! Skipping..."); + } + } + } + referencedData.clear(); + for(ClientDataEntry etr : clientDatas.values()) { + referencedData.addAll(etr.getReferencedBlobs()); + } + toDelete.clear(); + toDelete.addAll(existingBlobs); + toDelete.removeAll(referencedData); + if(!toDelete.isEmpty()) { + for(EaglercraftUUID del : toDelete) { + existingBlobs.remove(del); + String name = "blobs/" + del; + logger.info("Deleting orphaned blob \"{}\" from datastore", name); + if(!datastore.deleteItem(name)) { + logger.warn("Failed to delete file! Skipping..."); + } + } + } + } + + public void loadAdditionalConf() { + logger.info("Loading config.json"); + byte[] dat = datastore.getItem("config.json"); + if(dat != null) { + try { + JSONObject obj = new JSONObject(new String(dat, StandardCharsets.UTF_8)); + confBootTimeout = obj.getInt("confBootTimeout"); + confMenuTitle = obj.getString("confMenuTitle"); + }catch(JSONException ex) { + logger.error("Invalid config.json!"); + logger.error(ex); + } + } + } + + public void saveAdditionalConf() { + JSONObject confJSON = new JSONObject(); + confJSON.put("confBootTimeout", confBootTimeout); + confJSON.put("confMenuTitle", confMenuTitle); + byte[] confBytes = confJSON.toString().getBytes(StandardCharsets.UTF_8); + datastore.setItem("config.json", confBytes); + } + + public static int getBootMenuFlags(Window win) { + try { + Storage stor = win.getLocalStorage(); + if(stor != null) { + String itm = stor.getItem(BootMenuConstants.getBootMenuFlagsKeyName()); + if(itm != null) { + return Integer.parseInt(itm); + } + } + }catch(Throwable t) { + } + return -1; + } + + public static void setBootMenuFlags(Window win, int flags) { + try { + Storage stor = win.getLocalStorage(); + if(stor != null) { + String key = BootMenuConstants.getBootMenuFlagsKeyName(); + if(flags != -1) { + stor.setItem(key, Integer.toString(flags)); + }else { + stor.removeItem(key); + } + } + }catch(Throwable t) { + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDatastore.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDatastore.java new file mode 100755 index 0000000..1a82c64 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuDatastore.java @@ -0,0 +1,362 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.function.Consumer; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.indexeddb.EventHandler; +import org.teavm.jso.indexeddb.IDBCountRequest; +import org.teavm.jso.indexeddb.IDBCursor; +import org.teavm.jso.indexeddb.IDBCursorRequest; +import org.teavm.jso.indexeddb.IDBDatabase; +import org.teavm.jso.indexeddb.IDBFactory; +import org.teavm.jso.indexeddb.IDBGetRequest; +import org.teavm.jso.indexeddb.IDBObjectStoreParameters; +import org.teavm.jso.indexeddb.IDBOpenDBRequest; +import org.teavm.jso.indexeddb.IDBRequest; +import org.teavm.jso.indexeddb.IDBTransaction; +import org.teavm.jso.indexeddb.IDBVersionChangeEvent; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.BooleanResult; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuDatastore { + + private static final Logger logger = LogManager.getLogger("BootMenuDatastore"); + + public static final String bootMenuDatabaseName = BootMenuConstants.bootMenuDatabaseName; + + private static final Object instanceLock = new Object(); + private static BootMenuDatastore instance = null; + + public static class DatastoreLockedException extends RuntimeException { + public DatastoreLockedException(String message) { + super(message); + } + } + + public static class DatastoreInitializationException extends RuntimeException { + public DatastoreInitializationException(String message) { + super(message); + } + } + + public static class DatastoreOperationException extends RuntimeException { + public DatastoreOperationException(String message) { + super(message); + } + } + + public static BootMenuDatastore openDatastore() { + synchronized(instanceLock) { + if(instance != null) { + ++instance.openCount; + return instance; + } + return instance = openDatastore0(); + } + } + + private static BootMenuDatastore openDatastore0() { + DatabaseOpen openResult = AsyncHandlers + .openDB(bootMenuDatabaseName + (BootMenuEntryPoint.isSignedClient() ? "_sig" : "_unsig") + + (IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients() ? "_1" : "_0")); + if(openResult.failedLocked) { + throw new DatastoreLockedException(openResult.failedError); + } + if(openResult.failedInit) { + throw new DatastoreInitializationException(openResult.failedError); + } + if(openResult.database == null) { + throw new NullPointerException("IDBDatabase is null!"); + } + return new BootMenuDatastore(openResult.database); + } + + private IDBDatabase database; + private int openCount; + + private BootMenuDatastore(IDBDatabase database) { + this.database = database; + this.openCount = 1; + } + + public byte[] getItem(String key) { + if(database != null) { + return TeaVMUtils.wrapByteArrayBuffer(AsyncHandlers.readWholeFile(database, key)); + }else { + return null; + } + } + + public void setItem(String key, byte[] data) { + if(database != null) { + if(data != null) { + if(!AsyncHandlers.writeWholeFile(database, key, TeaVMUtils.unwrapArrayBuffer(data)).bool) { + throw new DatastoreOperationException("Failed to write to datastore: \"" + key + "\" (" + data.length + " bytes)"); + } + }else { + AsyncHandlers.deleteFile(database, key); + } + } + } + + public boolean containsKey(String key) { + if(database != null) { + return AsyncHandlers.fileExists(database, key).bool; + }else { + return false; + } + } + + public boolean deleteItem(String key) { + if(database != null) { + return AsyncHandlers.deleteFile(database, key).bool; + }else { + return false; + } + } + + public void closeDatastore() { + if(--openCount == 0) { + synchronized(instanceLock) { + if(instance == this) { + instance = null; + } + } + closeDatastore0(); + } + } + + private void closeDatastore0() { + if(database != null) { + database.close(); + } + } + + public void iterateAllKeys(Consumer itr) { + if(database != null) { + AsyncHandlers.iterateFiles(database, itr); + } + } + + protected static class DatabaseOpen { + + protected final boolean failedInit; + protected final boolean failedLocked; + protected final String failedError; + + protected final IDBDatabase database; + + protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) { + failedInit = init; + failedLocked = locked; + failedError = error; + database = db; + } + + } + + @JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;") + protected static native IDBFactory createIDBFactory(); + + @JSFunctor + protected static interface OpenErrorCallback extends JSObject { + void call(String str); + } + + @JSBody(params = { "factory", "name", "ii", "errCB" }, script = "try { return factory.open(name, ii); } catch(err) { errCB(\"\" + err); return null; }") + protected static native IDBOpenDBRequest safeOpen(IDBFactory factory, String name, int i, OpenErrorCallback errCB); + + protected static class AsyncHandlers { + + @Async + protected static native DatabaseOpen openDB(String name); + + private static void openDB(String name, final AsyncCallback cb) { + IDBFactory i = createIDBFactory(); + if(i == null) { + cb.complete(new DatabaseOpen(true, false, "window.indexedDB was null or undefined", null)); + return; + } + final String[] errorHolder = new String[] { null }; + final IDBOpenDBRequest f = safeOpen(i, name, 1, (e) -> errorHolder[0] = e); + if(f == null || TeaVMUtils.isNotTruthy(f)) { + cb.complete(new DatabaseOpen(true, false, errorHolder[0] != null ? errorHolder[0] : "database open request was null or undefined", null)); + return; + } + TeaVMUtils.addEventListener(f, "blocked", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(new DatabaseOpen(false, true, null, null)); + } + }); + TeaVMUtils.addEventListener(f, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(new DatabaseOpen(false, false, null, f.getResult())); + } + }); + TeaVMUtils.addEventListener(f, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(new DatabaseOpen(true, false, "open error", null)); + } + }); + TeaVMUtils.addEventListener(f, "upgradeneeded", new EventListener() { + @Override + public void handleEvent(IDBVersionChangeEvent evt) { + f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path")); + } + }); + } + + @Async + protected static native BooleanResult deleteFile(IDBDatabase db, String name); + + private static void deleteFile(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readwrite"); + final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.TRUE); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.FALSE); + } + }); + } + + @JSBody(params = { "obj" }, script = "return (typeof obj === \"undefined\") ? null : ((typeof obj.data === \"undefined\") ? null : obj.data);") + protected static native ArrayBuffer readRow(JSObject obj); + + @JSBody(params = { "obj" }, script = "return [obj];") + private static native JSObject makeTheFuckingKeyWork(String k); + + @Async + protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name); + + private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(readRow(r.getResult())); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(null); + } + }); + + } + + @Async + protected static native BooleanResult fileExists(IDBDatabase db, String name); + + private static void fileExists(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult._new(r.getResult() > 0)); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.FALSE); + } + }); + } + + @JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));") + private static native String readKey(JSObject k); + + @Async + protected static native Integer iterateFiles(IDBDatabase db, final Consumer itr); + + private static void iterateFiles(IDBDatabase db, final Consumer itr, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBCursorRequest r = tx.objectStore("filesystem").openCursor(); + final int[] res = new int[1]; + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + IDBCursor c = r.getResult(); + if(c == null || c.getKey() == null || c.getValue() == null) { + cb.complete(res[0]); + return; + } + String k = readKey(c.getKey()); + if(k != null) { + ++res[0]; + itr.accept(k); + } + c.doContinue(); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(res[0] > 0 ? res[0] : -1); + } + }); + } + + @JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };") + protected static native JSObject writeRow(String name, ArrayBuffer data); + + @Async + protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data); + + private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readwrite"); + final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data)); + + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.TRUE); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.FALSE); + } + }); + } + + } +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java new file mode 100755 index 0000000..d03bea4 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java @@ -0,0 +1,178 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.teavm.jso.JSBody; +import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformUpdateSvc; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain.EPKFileEntry; +import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuEntryPoint { + + @JSBody(params = {}, script = "if((typeof window.__isEaglerX188BootMenuAlreadyShow === \"string\") && window.__isEaglerX188BootMenuAlreadyShow === \"yes\") return true; window.__isEaglerX188BootMenuAlreadyShow = \"yes\"; return false;") + private static native boolean getHasAlreadyBooted(); + + @JSBody(params = {}, script = "window.__isEaglerX188BootMenuAlreadyShow = \"yes\";") + private static native void setHasAlreadyBooted(); + + public static boolean checkShouldLaunchFlag(Window win) { + int flag = BootMenuDataManager.getBootMenuFlags(win); + if(flag == -1) { + return IBootMenuConfigAdapter.instance.isShowBootMenuOnLaunch() && !getHasAlreadyBooted(); + } + if((flag & 2) != 0) { + BootMenuDataManager.setBootMenuFlags(win, flag & ~2); + setHasAlreadyBooted(); + return true; + } + return ((flag & 1) != 0 || IBootMenuConfigAdapter.instance.isShowBootMenuOnLaunch()) && !getHasAlreadyBooted(); + } + + private static byte[] signatureData = null; + private static byte[] bundleData = null; + + public static void launchMenu(Window parentWindow, HTMLElement parentElement) { + signatureData = PlatformUpdateSvc.getClientSignatureData(); + bundleData = PlatformUpdateSvc.getClientBundleData(); + BootMenuMain.launchMenu(parentWindow, parentElement); + } + + public static void bootOriginClient() { + bootOriginClient(null); + } + + public static void bootOriginClient(Runnable doBeforeBoot) { + (new Thread(() -> { + if(doBeforeBoot != null) { + doBeforeBoot.run(); + } + ClientMain._main(); + }, "main")).start(); + } + + public static boolean isSignedClient() { + return signatureData != null && bundleData != null; + } + + public static byte[] getSignedClientSignature() { + return signatureData; + } + + public static byte[] getSignedClientBundle() { + return bundleData; + } + + public static byte[] getUnsignedClientClassesJS() { + return OfflineDownloadFactory.removeClientScriptElement(ClientPlatformSingleplayer.getIntegratedServerSourceTeaVM(), true); + } + + public static class UnsignedClientEPKLoader { + + public final List list; + public final Map> loaders; + + public UnsignedClientEPKLoader(List list, Map> loaders) { + this.list = list; + this.loaders = loaders; + } + + public byte[] loadEntry(EaglercraftUUID epkUUID) { + Supplier sup = loaders.get(epkUUID); + return sup != null ? sup.get() : null; + } + + } + + public static JSONArray getUnsignedClientAssetsEPKRaw() { + JSONArray ret = new JSONArray(ClientMain.configEPKFiles.length); + for (int i = 0; i < ClientMain.configEPKFiles.length; ++i) { + EPKFileEntry etr = ClientMain.configEPKFiles[i]; + JSONObject obj = new JSONObject(); + obj.put("url", etr.url); + obj.put("path", etr.path); + ret.put(obj); + } + return ret; + } + + public static UnsignedClientEPKLoader getUnsignedClientAssetsEPK() { + List list = new ArrayList<>(2); + Map> loaders = new HashMap<>(2); + for (int i = 0; i < ClientMain.configEPKFiles.length; ++i) { + EPKFileEntry etr = ClientMain.configEPKFiles[i]; + EaglercraftUUID uuidGen = EaglercraftUUID + .nameUUIDFromBytes(("EPKURL:" + etr.url).getBytes(StandardCharsets.UTF_8)); + list.add(new EPKDataEntry(etr.path, uuidGen)); + loaders.put(uuidGen, + () -> TeaVMUtils.wrapByteArrayBuffer(PlatformRuntime.downloadRemoteURI(etr.url, true))); + } + return new UnsignedClientEPKLoader(list, loaders); + } + + public static String getOriginLaunchOpts() { + return ((TeaVMClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).toJSONObject().put("bootMenuBlocksUnsignedClients", false).toString(4); + } + + public static JSONObject getOriginLaunchOptsJSON() { + return ((TeaVMClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).toJSONObject(); + } + + public static String getOriginContainer() { + return ClientMain.configRootElementId; + } + + public static void installSignedClientAtRuntime(String displayName, Window win, byte[] clientCert, + byte[] clientPayload, boolean setDefault, boolean setTimeout) { + SignedClientInstaller.installSignedClientAtRuntime(displayName, win, clientCert, clientPayload, setDefault, + setTimeout); + } + + public static void setDisplayBootMenuNextRefresh(Window win, boolean en) { + int i = BootMenuDataManager.getBootMenuFlags(win); + if(i == -1) i = 0; + if(en) { + i |= 2; + }else { + i &= ~2; + } + BootMenuDataManager.setBootMenuFlags(win, i); + } + + public static void clearCookies() { + PlatformApplication.setLocalStorage(ServerCookieDataStore.localStorageKey, null, false); + ServerCookieDataStore.clearCookiesLow(); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuFatOfflineLoader.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuFatOfflineLoader.java new file mode 100755 index 0000000..49e8479 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuFatOfflineLoader.java @@ -0,0 +1,93 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuFatOfflineLoader { + + protected static final Logger logger = LogManager.getLogger("BootMenuFatOfflineLoader"); + + public final HTMLElement parentElement; + + public final Map clientDatas = new HashMap<>(); + public final List launchDatas = new ArrayList<>(); + + public BootMenuFatOfflineLoader(HTMLElement parentElement) { + this.parentElement = parentElement; + this.loadAllData(); + } + + protected void loadAllData() { + String manifest = loadDataString("manifest_v1"); + if(manifest != null) { + JSONObject json = new JSONObject(manifest); + JSONArray launches = json.getJSONArray("launchData"); + JSONArray clients = json.getJSONArray("clientData"); + for(int i = 0, l = clients.length(); i < l; ++i) { + JSONObject obj = clients.getJSONObject(i); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(obj.getString("uuid")); + if(!theUUID.equals(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN)) { + clientDatas.put(theUUID, new ClientDataEntry(theUUID, obj)); + } + } + for(int i = 0, l = launches.length(); i < l; ++i) { + JSONObject obj = launches.getJSONObject(i); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(obj.getString("uuid")); + if(!theUUID.equals(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN)) { + LaunchConfigEntry theEtr = new LaunchConfigEntry(theUUID, obj); + if(clientDatas.containsKey(theEtr.clientDataUUID) || BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(theEtr.clientDataUUID)) { + launchDatas.add(theEtr); + }else { + logger.warn("Skipping launch config {} because the client data {} is missing!", theUUID, theEtr.clientDataUUID); + } + } + } + logger.info("Loading {} client(s) successfully", clientDatas.size()); + logger.info("Loading {} profile(s) successfully", launchDatas.size()); + } + } + + public String loadDataString(String key) { + HTMLElement ret = parentElement.querySelector("#_eaglerFatOffline_" + key); + return ret != null ? ret.getInnerText() : null; + } + + public byte[] loadDataBinary(String key) { + HTMLElement ret = parentElement.querySelector("#_eaglerFatOffline_" + key); + if(ret == null) { + return null; + } + try { + return Base64.decodeBase64(ret.getInnerText()); + }catch(Throwable t) { + return null; + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuMain.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuMain.java new file mode 100755 index 0000000..29b8003 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuMain.java @@ -0,0 +1,304 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.teavm.jso.JSBody; +import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.events.KeyboardEvent; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Lists; +import com.google.common.html.HtmlEscapers; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.LegacyKeycodeTranslator; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuMain { + + private static final Logger logger = LogManager.getLogger("BootMenuMain"); + + public static Window win = null; + public static HTMLDocument doc = null; + public static HTMLElement parent = null; + public static BootMenuDOM bootMenuDOM = null; + public static MenuState currentState = null; + public static BootMenuMetadata bootMenuMetadata = null; + public static BootMenuDatastore bootMenuDatastore = null; + public static BootMenuDataManager bootMenuDataManager = null; + public static BootMenuFatOfflineLoader bootMenuFatOfflineLoader = null; + + private static EventListener windowKeyDownListener = null; + private static EventListener windowKeyUpListener = null; + + private static final List eventQueue = new LinkedList<>(); + + private static boolean runUpdateLoop = true; + + @JSBody(params = { "e" }, script = "return (typeof e.which === \"number\") ? e.which : ((typeof e.keyCode === \"number\") ? e.keyCode : 0);") + private static native int getWhich(KeyboardEvent e); + + @JSBody(params = { "evt" }, script = "return (typeof evt.key === \"string\");") + private static native boolean hasKeyVar(KeyboardEvent evt); + + @JSBody(params = { "evt" }, script = "return (typeof evt.code === \"string\");") + private static native boolean hasCodeVar(KeyboardEvent evt); + + public static void launchMenu(Window parentWindow, HTMLElement parentElement) { + win = parentWindow; + doc = parentWindow.getDocument(); + parent = parentElement; + logger.info("Integrated boot menu is loading"); + String renderedMarkup; + try { + renderedMarkup = TemplateLoader.loadTemplate("/assets/eagler/boot_menu/boot_menu_markup.html"); + }catch(IOException ex) { + logger.error("Failed to render template!"); + logger.error(ex); + parentElement.setInnerHTML("

Failed to render template!

" + + HtmlEscapers.htmlEscaper().escape(ex.toString()) + + "

Check the console for more details

"); + return; + } + parentElement.setInnerHTML(renderedMarkup); + bootMenuDOM = new BootMenuDOM(parentElement); + logger.info("Registering event handlers"); + win.addEventListener("keydown", windowKeyDownListener = (evt) -> { + if(currentState != null) { + LegacyKeycodeTranslator.LegacyKeycode keyCode = null; + Map keyCodeTranslatorMap = PlatformInput.getKeyCodeTranslatorMapTeaVM(); + if(keyCodeTranslatorMap != null && hasCodeVar(evt)) { + keyCode = keyCodeTranslatorMap.get(evt.getCode()); + } + final int which; + if(keyCode != null) { + which = keyCode.keyCode; + }else { + which = getWhich(evt); + } + if(!evt.isRepeat()) { + runLater(() -> { + if(currentState != null) { + currentState.doHandleKeyDown(which); + } + }); + }else { + runLater(() -> { + if(currentState != null) { + currentState.doHandleKeyRepeat(which); + } + }); + } + } + }); + win.addEventListener("keyup", windowKeyUpListener = (evt) -> { + if(currentState != null) { + if(!evt.isRepeat()) { + LegacyKeycodeTranslator.LegacyKeycode keyCode = null; + Map keyCodeTranslatorMap = PlatformInput.getKeyCodeTranslatorMapTeaVM(); + if(keyCodeTranslatorMap != null && hasCodeVar(evt)) { + keyCode = keyCodeTranslatorMap.get(evt.getCode()); + } + final int which; + if(keyCode != null) { + which = keyCode.keyCode; + }else { + which = getWhich(evt); + } + runLater(() -> { + if(currentState != null) { + currentState.doHandleKeyUp(which); + } + }); + } + } + }); + bootMenuDOM.registerEventHandlers(); + bootMenuMetadata = new BootMenuMetadata("/assets/eagler/boot_menu/"); + bootMenuDatastore = BootMenuDatastore.openDatastore(); //TODO: error handling + bootMenuDataManager = new BootMenuDataManager(bootMenuDatastore); + bootMenuDOM.header_title.setInnerText(bootMenuDataManager.confMenuTitle); + bootMenuFatOfflineLoader = new BootMenuFatOfflineLoader(parentWindow.getDocument().getHead()); + logger.info("Entering boot menu display state"); + eventQueue.clear(); + changeState(new MenuStateBoot(true)); + enterUpdateLoop(); + } + + private static void enterUpdateLoop() { + runUpdateLoop = true; + while(runUpdateLoop) { + if(currentState != null) { + currentState.doUpdate(); + } + List eq = null; + synchronized(eventQueue) { + if(!eventQueue.isEmpty()) { + eq = Lists.newArrayList(eventQueue); + eventQueue.clear(); + } + } + if(eq != null) { + for(Runnable run : eq) { + try { + run.run(); + }catch(Throwable t) { + logger.error("Caught error in event queue!"); + logger.error(t); + } + } + } + EagUtils.sleep(50l); + } + } + + public static void runLater(Runnable run) { + if(runUpdateLoop) { + synchronized(eventQueue) { + eventQueue.add(run); + } + } + } + + public static void runLaterMS(Runnable run, int millis) { + Window.setTimeout(() -> runLater(run), millis); + } + + public static void unregisterEventHandlers() { + if(windowKeyDownListener != null) { + win.removeEventListener("keydown", windowKeyDownListener); + windowKeyDownListener = null; + } + if(windowKeyUpListener != null) { + win.removeEventListener("keyup", windowKeyUpListener); + windowKeyUpListener = null; + } + } + + public static void changeState(MenuState newState) { + if(currentState != null) { + currentState.doExitState(); + currentState = null; + } + currentState = newState; + if(newState != null) { + newState.doEnterState(); + } + } + + public static void continueBootToOriginClient() { + continueBootToOriginClient(BootMenuMain::sanitizeEaglercraftXOpts); + } + + public static void continueBootToOriginClient(Runnable doBeforeBoot) { + destroyBootMenuRuntime(); + stopEventLoop(); + BootMenuEntryPoint.bootOriginClient(doBeforeBoot); + } + + @JSBody(params = { }, script = "try { window.eaglercraftXOptsHints.bootMenuBlocksUnsignedClients = true; }catch(_ex){} try { window.eaglercraftXOpts.bootMenuBlocksUnsignedClients = true; }catch(_ex){}") + private static native void doSanitizeSignatureRequired(); + + public static void sanitizeEaglercraftXOpts() { + if(IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients()) { + doSanitizeSignatureRequired(); + } + } + + public static String createRootElementForClient() { + EaglercraftRandom randomCharGenerator = new EaglercraftRandom(); + char[] randomChars = new char[16]; + String charSel = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for(int i = 0; i < randomChars.length; ++i) { + randomChars[i] = charSel.charAt(randomCharGenerator.nextInt(charSel.length())); + } + String randomId = "game_frame_" + new String(randomChars); + HTMLElement parentTemp = parent; + HTMLElement newRoot = doc.createElement("div"); + newRoot.getStyle().setProperty("width", "100%"); + newRoot.getStyle().setProperty("height", "100%"); + newRoot.setAttribute("id", randomId); + destroyBootMenuRuntime(); + parentTemp.appendChild(newRoot); + return randomId; + + } + + public static void destroyBootMenuRuntime() { + unregisterEventHandlers(); + bootMenuDOM = null; + currentState = null; + bootMenuMetadata = null; + if(bootMenuDatastore != null) { + bootMenuDatastore.closeDatastore(); + bootMenuDatastore = null; + } + bootMenuDataManager = null; + bootMenuFatOfflineLoader = null; + BootMenuAssets.freeBootMenuResourceRepo(); + win = null; + doc = null; + while(parent.getLastChild() != null) { + parent.removeChild(parent.getLastChild()); + } + parent = null; + } + + public static void stopEventLoop() { + runUpdateLoop = false; + } + + public static void fireChangeEvent(HTMLElement element) { + if(currentState != null) { + runLater(() -> { + if(currentState != null) { + currentState.doHandleOnChange(element); + } + }); + } + } + + public static void fireClickEvent(HTMLElement element) { + if(currentState != null) { + runLater(() -> { + if(currentState != null) { + currentState.doHandleOnClick(element); + } + }); + } + } + + public static void fireMouseOverEvent(HTMLElement element) { + if(currentState != null) { + runLater(() -> { + if(currentState != null) { + currentState.doHandleOnMouseOver(element); + } + }); + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuMetadata.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuMetadata.java new file mode 100755 index 0000000..a05dc83 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuMetadata.java @@ -0,0 +1,224 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.json.JSONArray; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.cache.EaglerLoadingCache; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootMenuMetadata { + + protected static final Logger logger = LogManager.getLogger("BootMenuMetadata"); + + public static class LaunchTemplate { + + public final EnumClientLaunchType type; + public final String joinServer; + public final String launchOptsVar; + public final String launchOptsAssetsURIVar; + public final String launchOptsContainerVar; + public final String mainFunction; + public final String launchOpts; + public final boolean clearCookiedBeforeLaunch; + + protected LaunchTemplate(EnumClientLaunchType type, String joinServer, String launchOptsVar, + String launchOptsAssetsURIVar, String launchOptsContainerVar, String mainFunction, String launchOpts, + boolean clearCookiedBeforeLaunch) { + this.type = type; + this.joinServer = joinServer; + this.launchOptsVar = launchOptsVar; + this.launchOptsAssetsURIVar = launchOptsAssetsURIVar; + this.launchOptsContainerVar = launchOptsContainerVar; + this.mainFunction = mainFunction; + this.launchOpts = launchOpts; + this.clearCookiedBeforeLaunch = clearCookiedBeforeLaunch; + } + + protected LaunchTemplate(JSONObject jsonObject) { + type = EnumClientLaunchType.valueOf(jsonObject.getString("client_launch_type")); + joinServer = jsonObject.optString("join_server"); + launchOptsVar = jsonObject.optString("client_launch_opts_var"); + launchOptsAssetsURIVar = jsonObject.optString("client_launch_opts_assetsURI_var"); + launchOptsContainerVar = jsonObject.optString("client_launch_opts_container_var"); + mainFunction = jsonObject.optString("client_launch_main_func"); + clearCookiedBeforeLaunch = jsonObject.optBoolean("clear_cookies_before_launch"); + launchOpts = null; + } + + protected LaunchTemplate mutateOpts(String newOpts) { + if(newOpts == launchOpts) { + return this; + } + return new LaunchTemplate(type, joinServer, launchOptsVar, launchOptsAssetsURIVar, launchOptsContainerVar, + mainFunction, newOpts, clearCookiedBeforeLaunch); + } + + public LaunchConfigEntry createLaunchConfig(EaglercraftUUID uuid, EaglercraftUUID clientDataUUID, String displayName) { + return new LaunchConfigEntry(uuid, clientDataUUID, displayName, type, joinServer, launchOptsVar, + launchOptsAssetsURIVar, launchOptsContainerVar, mainFunction, launchOpts, clearCookiedBeforeLaunch); + } + + public void configureLaunchConfig(LaunchConfigEntry etr) { + switch(type) { + case STANDARD_OFFLINE_V1: + etr.launchOpts = launchOpts; + etr.launchOptsVar = launchOptsVar; + etr.launchOptsAssetsURIVar = launchOptsAssetsURIVar; + etr.launchOptsContainerVar = launchOptsContainerVar; + etr.mainFunction = mainFunction; + break; + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + etr.launchOpts = launchOpts; + break; + case EAGLER_1_5_V1: + etr.launchOpts = launchOpts; + etr.joinServer = joinServer; + break; + case EAGLER_BETA_V1: + etr.joinServer = joinServer; + break; + case PEYTON_V1: + break; + default: //? + break; + } + } + + } + + public static class DefaultLaunchTemplate { + + public final String templateName; + public final Set supportedFormats; + public final Set parseTypes; + public final LaunchTemplate templateState; + + protected DefaultLaunchTemplate(String templateName, Set supportedFormats, + Set parseTypes, LaunchTemplate templateState) { + this.templateName = templateName; + this.supportedFormats = supportedFormats; + this.parseTypes = parseTypes; + this.templateState = templateState; + } + + public LaunchConfigEntry createLaunchConfig(EaglercraftUUID uuid, EaglercraftUUID clientDataUUID) { + return templateState.createLaunchConfig(uuid, clientDataUUID, templateName); + } + + @Override + public String toString() { + return templateName; + } + } + + public final String basePath; + + public final Map formatDefaultOptsMap = new HashMap<>(); + public final List defaultLaunchTemplates = new ArrayList<>(); + + public BootMenuMetadata(String basePath) { + this.basePath = basePath; + this.loadAllData(); + } + + protected void loadAllData() { + logger.info("Loading client templates and default settings..."); + formatDefaultOptsMap.clear(); + defaultLaunchTemplates.clear(); + EaglerLoadingCache optsFileLoader = new EaglerLoadingCache<>(this::loadDataFileString); + EaglerLoadingCache templateFileLoader = new EaglerLoadingCache<>(this::loadDataFileLaunchTemplate); + byte[] data = BootMenuAssets.loadResourceBytes(basePath + "meta_opts_templates.json"); + if(data == null) { + throw new RuntimeException("Missing metadata file: meta_opts_templates.json"); + } + JSONObject jsonObject = new JSONObject(new String(data, StandardCharsets.UTF_8)); + JSONObject defaults = jsonObject.getJSONObject("defaults"); + for(String str : defaults.keySet()) { + EnumClientLaunchType fmt = EnumClientLaunchType.valueOf(str); + JSONObject etr = defaults.getJSONObject(str); + LaunchTemplate launchTemplateBase = templateFileLoader.get(etr.getString("conf")); + String optsFileName = etr.optString("opts", null); + String eagOpts = optsFileName != null ? optsFileLoader.get(optsFileName) : null; + formatDefaultOptsMap.put(fmt, launchTemplateBase.mutateOpts(eagOpts)); + } + JSONArray templates = jsonObject.getJSONArray("templates"); + for(int i = 0, l = templates.length(); i < l; ++i) { + JSONObject obj = templates.getJSONObject(i); + LaunchTemplate launchTemplateBase = templateFileLoader.get(obj.getString("conf")); + String optsFileName = obj.optString("opts", null); + String eagOpts = optsFileName != null ? optsFileLoader.get(optsFileName) : null; + JSONArray allowList = obj.getJSONArray("allow"); + Set toAllow = new HashSet<>(allowList.length()); + for(int j = 0, m = allowList.length(); j < m; ++j) { + toAllow.add(EnumClientFormatType.valueOf(allowList.getString(j))); + } + JSONArray parseTypesList = obj.getJSONArray("parseTypes"); + Set toParseTypes = new HashSet<>(parseTypesList.length()); + for(int j = 0, m = parseTypesList.length(); j < m; ++j) { + toParseTypes.add(EnumOfflineParseType.valueOf(parseTypesList.getString(j))); + } + defaultLaunchTemplates.add(new DefaultLaunchTemplate(obj.getString("name"), toAllow, toParseTypes, launchTemplateBase.mutateOpts(eagOpts))); + } + } + + public List getTemplatesForClientData(EnumClientFormatType formatType) { + List ret = new ArrayList<>(); + for(DefaultLaunchTemplate template : defaultLaunchTemplates) { + if(template.supportedFormats.contains(formatType)) { + ret.add(template); + } + } + return ret; + } + + public List getTemplatesForParseType(EnumOfflineParseType parseType) { + List ret = new ArrayList<>(); + for(DefaultLaunchTemplate template : defaultLaunchTemplates) { + if(template.parseTypes.contains(parseType)) { + ret.add(template); + } + } + return ret; + } + + protected String loadDataFileString(String name) { + byte[] data = BootMenuAssets.loadResourceBytes(basePath + name); + if(data == null) { + throw new RuntimeException("Missing metadata file: " + name); + } + return new String(data, StandardCharsets.UTF_8); + } + + protected LaunchTemplate loadDataFileLaunchTemplate(String name) { + return new LaunchTemplate(new JSONObject(loadDataFileString(name))); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootableClientEntry.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootableClientEntry.java new file mode 100755 index 0000000..2dbffe4 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootableClientEntry.java @@ -0,0 +1,366 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint.UnsignedClientEPKLoader; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class BootableClientEntry { + + public static enum EnumDataType { + SITE_ORIGIN, EMBEDDED, LOCAL_STORAGE; + } + + public static interface BootAdapter { + String getDisplayName(); + void bootClient(IProgressMsgCallback cb); + void downloadOffline(IProgressMsgCallback cb); + void downloadEPK(IProgressMsgCallback cb); + ClientDataEntry getClientDataEntry(); + LaunchConfigEntry getLaunchConfigEntry(); + Map> getBlobLoaders(); + } + + public final EnumDataType dataType; + public final BootAdapter bootAdapter; + + public BootableClientEntry(EnumDataType dataType, BootAdapter bootAdapter) { + this.dataType = dataType; + this.bootAdapter = bootAdapter; + } + + public static BootableClientEntry getOriginClient() { + final ClientDataEntry originClientDat; + final LaunchConfigEntry originLaunchDat; + final Map> siteOriginLoader = new HashMap<>(2); + if(BootMenuEntryPoint.isSignedClient()) { + siteOriginLoader.put(BootMenuConstants.UUID_ORIGIN_SIGNED_SIGNATURE, BootMenuEntryPoint::getSignedClientSignature); + siteOriginLoader.put(BootMenuConstants.UUID_ORIGIN_SIGNED_BUNDLE, BootMenuEntryPoint::getSignedClientBundle); + originClientDat = new ClientDataEntry(EnumClientFormatType.EAGLER_SIGNED_OFFLINE, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, BootMenuConstants.UUID_ORIGIN_SIGNED_BUNDLE, null, + BootMenuConstants.UUID_ORIGIN_SIGNED_SIGNATURE, null); + originLaunchDat = new LaunchConfigEntry(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, + BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + + " " + BootMenuConstants.client_projectOriginVersion, + EnumClientLaunchType.EAGLERX_SIGNED_V1, null, null, null, null, null, + BootMenuEntryPoint.getOriginLaunchOpts(), false); + }else { + siteOriginLoader.put(BootMenuConstants.UUID_ORIGIN_UNSIGNED_CLASSES_JS, BootMenuEntryPoint::getUnsignedClientClassesJS); + UnsignedClientEPKLoader loader = BootMenuEntryPoint.getUnsignedClientAssetsEPK(); + siteOriginLoader.putAll(loader.loaders); + originClientDat = new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, BootMenuConstants.UUID_ORIGIN_UNSIGNED_CLASSES_JS, null, + null, loader.list); + originLaunchDat = new LaunchConfigEntry(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, + BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + + " " + BootMenuConstants.client_projectOriginVersion, + EnumClientLaunchType.EAGLERX_V1, null, null, null, null, null, + BootMenuEntryPoint.getOriginLaunchOpts(), false); + } + return new BootableClientEntry(EnumDataType.SITE_ORIGIN, new BootAdapter() { + + private final String displayName = BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + " " + + BootMenuConstants.client_projectOriginVersion + " (site origin)"; + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public void bootClient(IProgressMsgCallback cb) { + BootMenuMain.continueBootToOriginClient(BootMenuMain::sanitizeEaglercraftXOpts); + } + + @Override + public void downloadOffline(IProgressMsgCallback cb) { + OfflineDownloadFactory.downloadOffline(originLaunchDat, originClientDat, siteOriginLoader, cb); + } + + @Override + public void downloadEPK(IProgressMsgCallback cb) { + EPKClientFactory.downloadEPKClient(originLaunchDat, originClientDat, siteOriginLoader, cb); + } + + @Override + public ClientDataEntry getClientDataEntry() { + return originClientDat; + } + + @Override + public LaunchConfigEntry getLaunchConfigEntry() { + return originLaunchDat; + } + + @Override + public Map> getBlobLoaders() { + return siteOriginLoader; + } + + }); + } + + public static List enumerateBootableClients() { + List ret = new ArrayList<>(16); + final Map> siteOriginLoader = new HashMap<>(2); + final ClientDataEntry originClientDat; + final LaunchConfigEntry originLaunchDat; + if(BootMenuEntryPoint.isSignedClient()) { + siteOriginLoader.put(BootMenuConstants.UUID_ORIGIN_SIGNED_SIGNATURE, BootMenuEntryPoint::getSignedClientSignature); + siteOriginLoader.put(BootMenuConstants.UUID_ORIGIN_SIGNED_BUNDLE, BootMenuEntryPoint::getSignedClientBundle); + originClientDat = new ClientDataEntry(EnumClientFormatType.EAGLER_SIGNED_OFFLINE, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, BootMenuConstants.UUID_ORIGIN_SIGNED_BUNDLE, null, + BootMenuConstants.UUID_ORIGIN_SIGNED_SIGNATURE, null); + originLaunchDat = new LaunchConfigEntry(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, + BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + + " " + BootMenuConstants.client_projectOriginVersion, + EnumClientLaunchType.EAGLERX_SIGNED_V1, null, null, null, null, null, + BootMenuEntryPoint.getOriginLaunchOpts(), false); + }else { + siteOriginLoader.put(BootMenuConstants.UUID_ORIGIN_UNSIGNED_CLASSES_JS, BootMenuEntryPoint::getUnsignedClientClassesJS); + UnsignedClientEPKLoader loader = BootMenuEntryPoint.getUnsignedClientAssetsEPK(); + siteOriginLoader.putAll(loader.loaders); + originClientDat = new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, BootMenuConstants.UUID_ORIGIN_UNSIGNED_CLASSES_JS, null, + null, loader.list); + originLaunchDat = new LaunchConfigEntry(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN, + BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, + BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + + " " + BootMenuConstants.client_projectOriginVersion, + EnumClientLaunchType.EAGLERX_V1, null, null, null, null, null, + BootMenuEntryPoint.getOriginLaunchOpts(), false); + } + ret.add(new BootableClientEntry(EnumDataType.SITE_ORIGIN, new BootAdapter() { + + private final String displayName = BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + " " + + BootMenuConstants.client_projectOriginVersion + " (site origin)"; + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public void bootClient(IProgressMsgCallback cb) { + BootMenuMain.continueBootToOriginClient(BootMenuMain::sanitizeEaglercraftXOpts); + } + + @Override + public void downloadOffline(IProgressMsgCallback cb) { + OfflineDownloadFactory.downloadOffline(originLaunchDat, originClientDat, siteOriginLoader, cb); + } + + @Override + public void downloadEPK(IProgressMsgCallback cb) { + EPKClientFactory.downloadEPKClient(originLaunchDat, originClientDat, siteOriginLoader, cb); + } + + @Override + public ClientDataEntry getClientDataEntry() { + return originClientDat; + } + + @Override + public LaunchConfigEntry getLaunchConfigEntry() { + return originLaunchDat; + } + + @Override + public Map> getBlobLoaders() { + return siteOriginLoader; + } + + })); + for(final LaunchConfigEntry etr : BootMenuMain.bootMenuFatOfflineLoader.launchDatas) { + final ClientDataEntry etr2 = BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(etr.clientDataUUID) + ? originClientDat + : BootMenuMain.bootMenuFatOfflineLoader.clientDatas.get(etr.clientDataUUID); + if(etr2 != null) { + Collection refBlobs = etr2.getReferencedBlobs(); + final Map> loaderMap = new HashMap<>(2); + for(final EaglercraftUUID uuid : refBlobs) { + loaderMap.put(uuid, () -> { + Supplier sup = siteOriginLoader.get(uuid); + if(sup != null) { + byte[] ret2 = sup.get(); + if(ret2 != null) { + return ret2; + } + } + return BootMenuMain.bootMenuFatOfflineLoader.loadDataBinary(uuid.toString()); + }); + } + ret.add(new BootableClientEntry(EnumDataType.EMBEDDED, new BootAdapter() { + + private final String displayName = etr.displayName + " (embedded" + + (etr.type == EnumClientLaunchType.EAGLERX_SIGNED_V1 ? ", signed)" : ")"); + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public void bootClient(IProgressMsgCallback cb) { + ClientBootFactory.bootClient(etr, etr2, loaderMap, cb); + } + + @Override + public void downloadOffline(IProgressMsgCallback cb) { + OfflineDownloadFactory.downloadOffline(etr, etr2, loaderMap, cb); + } + + @Override + public void downloadEPK(IProgressMsgCallback cb) { + EPKClientFactory.downloadEPKClient(etr, etr2, loaderMap, cb); + } + + @Override + public ClientDataEntry getClientDataEntry() { + return etr2; + } + + @Override + public LaunchConfigEntry getLaunchConfigEntry() { + return etr; + } + + @Override + public Map> getBlobLoaders() { + return loaderMap; + } + + })); + } + } + for(EaglercraftUUID etrUUID : BootMenuMain.bootMenuDataManager.launchDatasList) { + final LaunchConfigEntry etr = BootMenuMain.bootMenuDataManager.launchDatas.get(etrUUID); + if(etr != null) { + final ClientDataEntry etr2 = BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(etr.clientDataUUID) + ? originClientDat + : BootMenuMain.bootMenuDataManager.clientDatas.get(etr.clientDataUUID); + if(etr2 != null) { + Collection refBlobs = etr2.getReferencedBlobs(); + final Map> loaderMap = new HashMap<>(2); + for(final EaglercraftUUID uuid : refBlobs) { + loaderMap.put(uuid, () -> { + Supplier sup = siteOriginLoader.get(uuid); + if(sup != null) { + byte[] ret2 = sup.get(); + if(ret2 != null) { + return ret2; + } + } + return BootMenuMain.bootMenuDatastore.getItem("blobs/" + uuid); + }); + } + ret.add(new BootableClientEntry(EnumDataType.LOCAL_STORAGE, new BootAdapter() { + + private final String displayName = etr.displayName + " (local storage" + + (etr.type == EnumClientLaunchType.EAGLERX_SIGNED_V1 ? ", signed)" : ")"); + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public void bootClient(IProgressMsgCallback cb) { + ClientBootFactory.bootClient(etr, etr2, loaderMap, cb); + } + + @Override + public void downloadOffline(IProgressMsgCallback cb) { + OfflineDownloadFactory.downloadOffline(etr, etr2, loaderMap, cb); + } + + @Override + public void downloadEPK(IProgressMsgCallback cb) { + EPKClientFactory.downloadEPKClient(etr, etr2, loaderMap, cb); + } + + @Override + public ClientDataEntry getClientDataEntry() { + return etr2; + } + + @Override + public LaunchConfigEntry getLaunchConfigEntry() { + return etr; + } + + @Override + public Map> getBlobLoaders() { + return loaderMap; + } + + })); + } + } + } + return ret; + } + + /** + * returns true if uuidsList has changed + */ + public static boolean applyClientOrdering(List uuidsList, List bootableClients) { + if(bootableClients.isEmpty()) return false; + + List bootableClientsCopy = new ArrayList<>(bootableClients); + Set uuidsSet = new HashSet<>(uuidsList); + Map bootableClientsLookup = new HashMap<>(bootableClients.size()); + bootableClients.clear(); + + for(int i = 0, l = bootableClientsCopy.size(); i < l; ++i) { + BootableClientEntry etr = bootableClientsCopy.get(i); + bootableClientsLookup.put(etr.bootAdapter.getLaunchConfigEntry().uuid, etr); + } + + for(int i = 0, l = uuidsList.size(); i < l; ++i) { + BootableClientEntry etr = bootableClientsLookup.get(uuidsList.get(i)); + if(etr != null) { + bootableClients.add(etr); + } + } + + boolean flag = false; + for(int i = 0, l = bootableClientsCopy.size(); i < l; ++i) { + BootableClientEntry etr = bootableClientsCopy.get(i); + EaglercraftUUID uuid = etr.bootAdapter.getLaunchConfigEntry().uuid; + if(!uuidsSet.contains(uuid)) { + bootableClients.add(etr); + uuidsList.add(uuid); + flag = true; + } + } + + return flag; + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/CheckboxListController.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/CheckboxListController.java new file mode 100755 index 0000000..7e08f44 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/CheckboxListController.java @@ -0,0 +1,108 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.Arrays; +import java.util.List; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class CheckboxListController + extends SelectionListController { + + protected static final SelectionListController.ListItem LIST_ITEM_CANCEL = new SelectionListController.ListItem() { + @Override + public String getName() { + return "Cancel"; + } + }; + + protected static final SelectionListController.ListItem LIST_ITEM_DONE = new SelectionListController.ListItem() { + @Override + public String getName() { + return "Done"; + } + }; + + protected static class ListItemWrapper implements SelectionListController.ListItem { + + protected final SelectionListController.ListItem parent; + + protected ListItemWrapper(SelectionListController.ListItem parent) { + this.parent = parent; + } + + @Override + public String getName() { + return (parent.getAlwaysSelected() ? "[X] " : "[ ] ") + parent.getName(); + } + + @Override + public boolean getAlwaysSelected() { + return parent.getAlwaysSelected(); + } + + } + + public CheckboxListController(HTMLElement parent, List selectionList) { + super(parent, Lists.newArrayList(Iterators.concat(Iterators.transform(selectionList.iterator(), ListItemWrapper::new), + Arrays.asList(LIST_ITEM_CANCEL, LIST_ITEM_DONE).iterator()))); + } + + public List getSelectedItems() { + return Lists.newArrayList(Collections2.transform( + Collections2.filter(selectionEnableList, (e) -> (e.userVal && !e.listItem.getAlwaysSelected())), + (e) -> (T) ((ListItemWrapper) e.listItem).parent)); + } + + protected void itemSelectedLow(ListItemInstance item) { + if(item.listItem == LIST_ITEM_CANCEL) { + cancelSelected(); + }else if(item.listItem == LIST_ITEM_DONE) { + doneSelected(getSelectedItems()); + }else { + if(!item.listItem.getAlwaysSelected()) { + item.userVal = !item.userVal; + item.element.setInnerText((item.userVal ? "[X]" : "[ ]") + item.element.getInnerText().substring(3)); + } + } + } + + public void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_SPACE) { + if(currentSelected >= 0 && currentSelected < selectionEnableList.size()) { + if(!selectionEnableList.get(currentSelected).listItem.getAlwaysSelected()) { + fireSelect(); + } + } + return; + } + super.handleKeyDown(keyCode); + } + + @Override + protected final void itemSelected(SelectionListController.ListItem item) { + } + + protected abstract void cancelSelected(); + + protected abstract void doneSelected(List selectedItems); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java new file mode 100755 index 0000000..58f4edc --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java @@ -0,0 +1,725 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.teavm.jso.JSBody; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLScriptElement; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.cache.EaglerLoadingCache; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLHandle; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTException; +import net.minecraft.nbt.NBTTagCompound; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClientBootFactory { + + private static final Logger logger = LogManager.getLogger("ClientBootFactory"); + + private static class UUIDStringPair { + + private final EaglercraftUUID uuid; + private final String str; + + private UUIDStringPair(EaglercraftUUID uuid, String str) { + this.uuid = uuid; + this.str = str; + } + + @Override + public int hashCode() { + return (31 + uuid.hashCode()) * 31 + str.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) + return true; + if(obj == null || !(obj instanceof UUIDStringPair)) + return false; + UUIDStringPair other = (UUIDStringPair) obj; + return Objects.equals(str, other.str) && Objects.equals(uuid, other.uuid); + } + + } + + public static void bootClient(LaunchConfigEntry launchConf, ClientDataEntry clientData, + Map> loaders, IProgressMsgCallback cb) { + if(launchConf.clearCookiesBeforeLaunch) { + BootMenuEntryPoint.clearCookies(); + } + if(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(clientData.uuid)) { + bootOriginClient(launchConf, clientData, cb); + return; + } + EaglerLoadingCache loadingCacheA = new EaglerLoadingCache((uuid) -> { + Supplier sup = loaders.get(uuid); + return sup != null ? sup.get() : null; + }); + EaglerLoadingCache loadingCacheB = new EaglerLoadingCache((uuidMime) -> { + byte[] b = loadingCacheA.get(uuidMime.uuid); + return b != null ? TeaVMBlobURLManager.registerNewURLByte(b, uuidMime.str) : null; + }); + switch(launchConf.type) { + case STANDARD_OFFLINE_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + bootClientStandardOffline(launchConf, clientData, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for STANDARD_OFFLINE_V1!"); + } + break; + case EAGLERX_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + bootClientEaglerX18(launchConf, clientData, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLERX_V1!"); + } + break; + case EAGLERX_SIGNED_V1: + if(clientData.type == EnumClientFormatType.EAGLER_SIGNED_OFFLINE) { + bootClientEaglerX18Signed(launchConf, clientData, loadingCacheA, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLERX_SIGNED_V1!"); + } + break; + case EAGLER_1_5_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + bootClientEagler15Old(launchConf, clientData, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLER_1_5_V1!"); + } + break; + case EAGLER_1_5_V2: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_1_5_OFFLINE) { + bootClientEagler15New(launchConf, clientData, loadingCacheA, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLER_1_5_V2!"); + } + break; + case EAGLER_BETA_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + bootClientEaglerB13(launchConf, clientData, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLER_1_5_V2!"); + } + break; + case PEYTON_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + bootClientPeytonIndev(launchConf, clientData, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for PEYTON_V1!"); + } + break; + case PEYTON_V2: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + bootClientPeytonAlphaBeta(launchConf, clientData, loadingCacheB, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for PEYTON_V2!"); + } + break; + } + } + + private static void doUpdateMessage(IProgressMsgCallback cb, String str) { + logger.info(str); + cb.updateMessage(str); + } + + @JSBody(params = { "mainName" }, script = "window[mainName] = null;") + private static native boolean clearMain(String mainName); + + @JSBody(params = { "mainName" }, script = "return (typeof window[mainName] === \"function\");") + private static native boolean isMainReady(String mainName); + + @JSBody(params = { "optsVarName", "optsVarJSON", "mainName", "blockUnsigned" }, script = "setTimeout(function() { window[optsVarName] = JSON.parse(optsVarJSON); if(blockUnsigned) window[optsVarName].bootMenuBlocksUnsignedClients = true; window[mainName](); }, 1);") + private static native void callMain(String optsVarName, String optsVarJSON, String mainName, boolean blockUnsigned); + + @JSBody(params = { "clientSigURL", "clientPayloadURL", "launchOptsJSON", "blockUnsigned" }, script = "window.eaglercraftXOptsHints = JSON.parse(launchOptsJSON); if(blockUnsigned) window.eaglercraftXOptsHints.bootMenuBlocksUnsignedClients = true; window.eaglercraftXClientSignature = clientSigURL; window.eaglercraftXClientBundle = clientPayloadURL;") + private static native void setupSignedClientOpts(String clientSigURL, String clientPayloadURL, String launchOptsJSON, boolean blockUnsigned); + + @JSBody(params = { "optsVarName", "containerId", "assetsEPKURL", "b64Opts", "joinServer", "mainName", "blockUnsigned" }, script = "setTimeout(function() { window[optsVarName] = [ containerId, assetsEPKURL, b64Opts ]; if(blockUnsigned && (typeof window.eaglercraftXOpts === \"object\")) window.eaglercraftXOpts.bootMenuBlocksUnsignedClients = true; if(joinServer.length > 0) window[optsVarName].push(joinServer); window[mainName](); }, 1);") + private static native void callMainOld15(String optsVarName, String containerId, String assetsEPKURL, String b64Opts, String joinServer, String mainName, boolean blockUnsigned); + + @JSBody(params = { "optsVarName", "containerId", "assetsEPKURL", "joinServer", "mainName", "blockUnsigned" }, script = "setTimeout(function() { window[optsVarName] = [ containerId, assetsEPKURL ]; if(blockUnsigned && (typeof window.eaglercraftXOpts === \"object\")) window.eaglercraftXOpts.bootMenuBlocksUnsignedClients = true; if(joinServer.length > 0) window[optsVarName].push(joinServer); window[mainName](); }, 1);") + private static native void callMainOldB13(String optsVarName, String containerId, String assetsEPKURL, String joinServer, String mainName, boolean blockUnsigned); + + @JSBody(params = { "optsVarName", "optsVarJSON", "blockUnsigned" }, script = "window[optsVarName] = JSON.parse(optsVarJSON); if(blockUnsigned) window[optsVarName].bootMenuBlocksUnsignedClients = true;") + private static native void setJSONOpts(String optsVarName, String optsVarJSON, boolean blockUnsigned); + + private static void bootOriginClient(LaunchConfigEntry launchConf, ClientDataEntry clientData, + IProgressMsgCallback cb) { + doUpdateMessage(cb, "Preparing launch opts..."); + JSONObject launchOpts = new JSONObject(launchConf.launchOpts); + launchOpts.put("container", BootMenuEntryPoint.getOriginContainer()); + launchOpts.put("assetsURI", BootMenuEntryPoint.getUnsignedClientAssetsEPKRaw()); + final String launchOptsStr = RelayRandomizeHelper.replaceRelayMacroWithConstant(launchOpts.toString()); + BootMenuMain.continueBootToOriginClient(() -> { + setJSONOpts("eaglercraftXOpts", launchOptsStr, IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients()); + }); + } + + private static void bootClientEaglerX18(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + JSONObject launchOpts = new JSONObject(launchConf.launchOpts); + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + JSONArray epkFiles = new JSONArray(); + for(EPKDataEntry etr : clientData.epkFiles) { + doUpdateMessage(cb, "Resolving assets.epk (" + etr.dataUUID + ", path: /" + etr.extractTo + ")"); + TeaVMBlobURLHandle epkURL = loadingCache.get(new UUIDStringPair(etr.dataUUID, "application/octet-stream")); + if(epkURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + etr.dataUUID + ", path: /" + etr.extractTo + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(epkURL); + JSONObject epkEntry = new JSONObject(); + epkEntry.put("url", epkURL.toExternalForm()); + epkEntry.put("path", etr.extractTo); + epkFiles.put(epkEntry); + } + launchOpts.put("assetsURI", epkFiles); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + launchOpts.put("container", BootMenuMain.createRootElementForClient()); + final String launchOptsStr = RelayRandomizeHelper.replaceRelayMacroWithConstant(launchOpts.toString()); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMain("eaglercraftXOpts", launchOptsStr, "main", blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + + private static void bootClientEaglerX18Signed(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCacheBytes, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + JSONObject launchOpts = new JSONObject(launchConf.launchOpts); + if(!launchOpts.has("hintsVersion")) { + launchOpts.put("hintsVersion", 1); + } + doUpdateMessage(cb, "Resolving client signature (" + clientData.clientSignature + ")"); + byte[] clientSigBytes = loadingCacheBytes.get(clientData.clientSignature); + final TeaVMBlobURLHandle clientSigURL = loadingCache.get(new UUIDStringPair(clientData.clientSignature, "application/octet-stream")); + List toCleanOnException = new ArrayList<>(); + if(clientSigURL == null) { + clientSigBytes = null; + String msg = "Could not resolve client signature! (" + clientData.clientSignature + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(clientSigURL); + doUpdateMessage(cb, "Resolving client payload (" + clientData.mainPayload + ")"); + byte[] clientPayloadBytes = loadingCacheBytes.get(clientData.mainPayload); + final TeaVMBlobURLHandle clientPayloadURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "application/octet-stream")); + if(clientPayloadURL == null) { + clientSigBytes = null; + clientPayloadBytes = null; + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve client payload! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(clientPayloadURL); + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + doUpdateMessage(cb, "Verifying signature..."); + boolean valid = SignatureCheckHelper.checkSignatureValid(clientSigBytes, clientPayloadBytes); + if(!valid) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + throw new UnsignedBootException(); + } + } + doUpdateMessage(cb, "Decompressing payload..."); + EaglerOutputStream bao = new EaglerOutputStream(0x1000000); + try(InputStream is = EaglerZLIB.newGZIPInputStream(new EaglerInputStream(clientPayloadBytes))) { + byte[] copybuffer = new byte[16384]; + int i; + while((i = is.read(copybuffer)) != -1) { + bao.write(copybuffer, 0, i); + } + }catch(IOException ex) { + clientSigBytes = null; + clientPayloadBytes = null; + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + throw new IllegalArgumentException("Could not decompress signed client payload!"); + } + byte[] decompressed = bao.toByteArray(); + bao = null; + clientSigBytes = null; + clientPayloadBytes = null; + final TeaVMBlobURLHandle clientPayloadURLDecompress = TeaVMBlobURLManager.registerNewURLByte(decompressed, "text/javascript"); + toCleanOnException.add(clientPayloadURLDecompress); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + HTMLDocument docTemp = BootMenuMain.doc; + launchOpts.put("container", BootMenuMain.createRootElementForClient()); + final String launchOptsStr = RelayRandomizeHelper.replaceRelayMacroWithConstant(launchOpts.toString()); + setupSignedClientOpts(clientSigURL.toExternalForm(), clientPayloadURL.toExternalForm(), launchOptsStr, blockUnsigned); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(clientPayloadURLDecompress.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + BootMenuMain.stopEventLoop(); + }, 250); + } + + private static void bootClientEagler15New(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCacheBytes, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + JSONObject launchOpts = new JSONObject(launchConf.launchOpts); + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + doUpdateMessage(cb, "Resolving classes_server.js (" + clientData.integratedServer + ")"); + byte[] classesServerJS = loadingCacheBytes.get(clientData.integratedServer); + if(classesServerJS == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve classes_server.js! (" + clientData.integratedServer + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + classesServerJS = OfflineDownloadFactory.removeUseStrict(classesServerJS); + byte[] appendToClassesServerJS = "\"use strict\";var eaglercraftServerOpts;onmessage = function(o) { eaglercraftServerOpts = o.data; main(); };".getBytes(StandardCharsets.UTF_8); + byte[] concatClassesServerJS = new byte[classesServerJS.length + appendToClassesServerJS.length]; + System.arraycopy(appendToClassesServerJS, 0, concatClassesServerJS, 0, appendToClassesServerJS.length); + System.arraycopy(classesServerJS, 0, concatClassesServerJS, appendToClassesServerJS.length, classesServerJS.length); + TeaVMBlobURLHandle classesServerJSURL = TeaVMBlobURLManager.registerNewURLByte(concatClassesServerJS, "text/javascript"); + toCleanOnException.add(classesServerJSURL); + launchOpts.put("serverWorkerURI", classesServerJSURL); + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); + TeaVMBlobURLHandle assetsEPKURL = loadingCache.get(new UUIDStringPair(clientData.epkFiles.get(0).dataUUID, "application/octet-stream")); + if(assetsEPKURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(assetsEPKURL); + launchOpts.put("assetsURI", assetsEPKURL.toExternalForm()); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + launchOpts.put("container", BootMenuMain.createRootElementForClient()); + final String launchOptsStr = RelayRandomizeHelper.replaceRelayMacroWithConstant(launchOpts.toString()); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMain("eaglercraftOpts", launchOptsStr, "main", blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + + private static void bootClientEagler15Old(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + final String b64Opts = translateNBTOpts(launchConf.launchOpts); + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + final TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0) + ")"); + final TeaVMBlobURLHandle assetsEPKURL = loadingCache.get(new UUIDStringPair(clientData.epkFiles.get(0).dataUUID, "application/octet-stream")); + if(assetsEPKURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(assetsEPKURL); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + final String container = BootMenuMain.createRootElementForClient(); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMainOld15("minecraftOpts", container, assetsEPKURL.toExternalForm(), b64Opts, launchConf.joinServer, "main", blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + + static String translateNBTOpts(String opts) { + opts = opts.trim(); + if(!opts.startsWith("[NBT]") || !opts.endsWith("[/NBT]")) { + throw new IllegalArgumentException("Eaglercraft opts are not in NBT format!"); + } + NBTTagCompound readTag; + try { + readTag = JsonToNBT.getTagFromJson(opts.substring(5, opts.length() - 6).trim()); + } catch (NBTException e) { + throw new IllegalArgumentException("Eaglercraft opts are invalid: " + e.getMessage()); + } + EaglerOutputStream bao = new EaglerOutputStream(256); + try { + CompressedStreamTools.write(readTag, new DataOutputStream(bao)); + } catch (IOException e) { + throw new RuntimeException("Could not write NBT tag compound!"); + } + return Base64.encodeBase64String(bao.toByteArray()); + } + + private static void bootClientEaglerB13(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + final TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0) + ")"); + final TeaVMBlobURLHandle assetsEPKURL = loadingCache.get(new UUIDStringPair(clientData.epkFiles.get(0).dataUUID, "application/octet-stream")); + if(assetsEPKURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(assetsEPKURL); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + final String container = BootMenuMain.createRootElementForClient(); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMainOldB13("minecraftOpts", container, assetsEPKURL.toExternalForm(), launchConf.joinServer, "main", blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + + private static void bootClientPeytonAlphaBeta(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + JSONObject launchOpts = new JSONObject(launchConf.launchOpts); + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + final TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0) + ")"); + final TeaVMBlobURLHandle assetsEPKURL = loadingCache.get(new UUIDStringPair(clientData.epkFiles.get(0).dataUUID, "application/octet-stream")); + if(assetsEPKURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(assetsEPKURL); + launchOpts.put("assetsLocation", assetsEPKURL.toExternalForm()); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + launchOpts.put("gameContainer", BootMenuMain.createRootElementForClient()); + final String launchOptsStr = launchOpts.toString(); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMain("config", launchOptsStr, "main", blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + + private static void bootClientPeytonIndev(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + final TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0) + ")"); + final TeaVMBlobURLHandle assetsEPKURL = loadingCache.get(new UUIDStringPair(clientData.epkFiles.get(0).dataUUID, "application/octet-stream")); + if(assetsEPKURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(assetsEPKURL); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + final String container = BootMenuMain.createRootElementForClient(); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMainOldB13("classicConfig", container, assetsEPKURL.toExternalForm(), "", "main", blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + + private static void bootClientStandardOffline(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + boolean blockUnsigned = IBootMenuConfigAdapter.instance.isBootMenuBlocksUnsignedClients(); + if(blockUnsigned) { + throw new UnsignedBootException(); + } + JSONObject launchOpts = new JSONObject(launchConf.launchOpts); + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + TeaVMBlobURLHandle classesJSURL = loadingCache.get(new UUIDStringPair(clientData.mainPayload, "text/javascript")); + List toCleanOnException = new ArrayList<>(); + if(classesJSURL == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(classesJSURL); + JSONArray epkFiles = new JSONArray(); + for(EPKDataEntry etr : clientData.epkFiles) { + doUpdateMessage(cb, "Resolving assets.epk (" + etr.dataUUID + ", path: /" + etr.extractTo + ")"); + TeaVMBlobURLHandle epkURL = loadingCache.get(new UUIDStringPair(etr.dataUUID, "application/octet-stream")); + if(epkURL == null) { + for(TeaVMBlobURLHandle url : toCleanOnException) { + try { + TeaVMBlobURLManager.releaseURL(url); + }catch(Throwable t) { + } + } + String msg = "Could not resolve assets.epk! (" + etr.dataUUID + ", path: /" + etr.extractTo + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + toCleanOnException.add(epkURL); + JSONObject epkEntry = new JSONObject(); + epkEntry.put("url", epkURL.toExternalForm()); + epkEntry.put("path", etr.extractTo); + epkFiles.put(epkEntry); + } + launchOpts.put(launchConf.launchOptsAssetsURIVar, epkFiles); + doUpdateMessage(cb, "Launching client..."); + BootMenuMain.runLaterMS(() -> { + clearMain("main"); + HTMLDocument docTemp = BootMenuMain.doc; + launchOpts.put(launchConf.launchOptsContainerVar, BootMenuMain.createRootElementForClient()); + final String launchOptsStr = RelayRandomizeHelper.replaceRelayMacroWithConstant(launchOpts.toString()); + HTMLScriptElement scriptElement = (HTMLScriptElement)docTemp.createElement("script"); + scriptElement.addEventListener("load", (evt) -> { + BootMenuMain.runLater(() -> { + while(!isMainReady("main")) { + logger.error("main function is not available yet! waiting 250ms..."); + EagUtils.sleep(250l); + } + BootMenuMain.stopEventLoop(); + callMain(launchConf.launchOptsVar, launchOptsStr, launchConf.mainFunction, blockUnsigned); + }); + }); + scriptElement.setType("text/javascript"); + scriptElement.setSrc(classesJSURL.toExternalForm()); + docTemp.getHead().appendChild(scriptElement); + }, 250); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientDataEntry.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientDataEntry.java new file mode 100755 index 0000000..6c24a38 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientDataEntry.java @@ -0,0 +1,152 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; + +import com.google.common.collect.Collections2; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClientDataEntry { + + public final EnumClientFormatType type; + public final EaglercraftUUID uuid; + public final EaglercraftUUID mainPayload; + public final EaglercraftUUID integratedServer; + public final EaglercraftUUID clientSignature; + public final List epkFiles; + + public ClientDataEntry(EnumClientFormatType type, EaglercraftUUID uuid, EaglercraftUUID mainPayload, + EaglercraftUUID integratedServer, EaglercraftUUID clientSignature, List epkFiles) { + this.type = type; + this.uuid = uuid; + this.mainPayload = mainPayload; + this.integratedServer = integratedServer; + this.clientSignature = clientSignature; + this.epkFiles = epkFiles; + } + + public ClientDataEntry(EaglercraftUUID uuid, JSONObject jsonObject) { + this.uuid = uuid; + EaglercraftUUID sanityUUID = EaglercraftUUID.fromString(jsonObject.getString("uuid")); + if(!sanityUUID.equals(uuid)) { + throw new IllegalArgumentException("The file's name UUID does not equal the UUID string found in the file!"); + } + int typeId = jsonObject.getInt("type"); + type = EnumClientFormatType.getById(typeId); + if(type == null) { + throw new IllegalArgumentException("Unknown client data type " + typeId + "!"); + } + switch(type) { + default: + case EAGLER_STANDARD_OFFLINE: + mainPayload = EaglercraftUUID.fromString(jsonObject.getString("mainPayload")); + integratedServer = null; + clientSignature = null; + epkFiles = loadEPKFiles(jsonObject.getJSONArray("epkFiles")); + break; + case EAGLER_STANDARD_1_5_OFFLINE: + mainPayload = EaglercraftUUID.fromString(jsonObject.getString("mainPayload")); + integratedServer = EaglercraftUUID.fromString(jsonObject.getString("integratedServer")); + clientSignature = null; + epkFiles = loadEPKFiles(jsonObject.getJSONArray("epkFiles")); + break; + case EAGLER_SIGNED_OFFLINE: + mainPayload = EaglercraftUUID.fromString(jsonObject.getString("mainPayload")); + integratedServer = null; + clientSignature = EaglercraftUUID.fromString(jsonObject.getString("clientSignature")); + epkFiles = null; + break; + } + } + + public void writeJSON(JSONObject jsonObject) { + jsonObject.put("uuid", uuid.toString()); + jsonObject.put("type", type.id); + switch(type) { + case EAGLER_STANDARD_OFFLINE: + default: + jsonObject.put("mainPayload", mainPayload.toString()); + jsonObject.put("epkFiles", storeEPKFiles(epkFiles)); + break; + case EAGLER_STANDARD_1_5_OFFLINE: + jsonObject.put("mainPayload", mainPayload.toString()); + jsonObject.put("integratedServer", integratedServer.toString()); + jsonObject.put("epkFiles", storeEPKFiles(epkFiles)); + break; + case EAGLER_SIGNED_OFFLINE: + jsonObject.put("mainPayload", mainPayload.toString()); + jsonObject.put("clientSignature", clientSignature.toString()); + break; + } + } + + protected static List loadEPKFiles(JSONArray arr) { + int cnt = arr.length(); + List ret = new ArrayList<>(cnt); + for(int i = 0; i < cnt; ++i) { + JSONObject obj = arr.getJSONObject(i); + ret.add(new EPKDataEntry(obj.optString("path", ""), EaglercraftUUID.fromString(obj.getString("uuid")))); + } + return ret; + } + + protected static JSONArray storeEPKFiles(List arr) { + int cnt = arr.size(); + JSONArray ret = new JSONArray(cnt); + for(int i = 0; i < cnt; ++i) { + EPKDataEntry etr = arr.get(i); + JSONObject obj = (new JSONObject()).put("uuid", etr.dataUUID.toString()); + if(etr.extractTo.length() > 0) { + obj.put("path", etr.extractTo); + } + ret.put(obj); + } + return ret; + } + + public Collection getReferencedBlobs() { + List toRet = new ArrayList<>(4); + switch(type) { + case EAGLER_STANDARD_OFFLINE: + default: + toRet.add(mainPayload); + toRet.addAll(Collections2.transform(epkFiles, (e) -> e.dataUUID)); + break; + case EAGLER_STANDARD_1_5_OFFLINE: + toRet.add(mainPayload); + toRet.add(integratedServer); + toRet.addAll(Collections2.transform(epkFiles, (e) -> e.dataUUID)); + break; + case EAGLER_SIGNED_OFFLINE: + toRet.add(mainPayload); + toRet.add(clientSignature); + break; + } + return toRet; + } + + public ClientDataEntry rotateUUID(EaglercraftUUID rotatedClientUUID) { + return new ClientDataEntry(type, rotatedClientUUID, mainPayload, integratedServer, clientSignature, epkFiles); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ConfirmationPopupController.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ConfirmationPopupController.java new file mode 100755 index 0000000..8dd6013 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ConfirmationPopupController.java @@ -0,0 +1,191 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.List; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.escape.Escaper; +import com.google.common.html.HtmlEscapers; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class ConfirmationPopupController { + + public static interface SelectionOption { + + String getName(); + + default boolean getEnabled() { + return true; + } + + } + + public static class SelectionOptionEnum implements SelectionOption { + + protected final String displayName; + protected final boolean enabled; + protected final E itemEnum; + + public SelectionOptionEnum(String displayName, boolean enabled, E itemEnum) { + this.displayName = displayName; + this.enabled = enabled; + this.itemEnum = itemEnum; + } + + public SelectionOptionEnum(String displayName, E itemEnum) { + this.displayName = displayName; + this.enabled = true; + this.itemEnum = itemEnum; + } + + @Override + public String getName() { + return displayName; + } + + @Override + public boolean getEnabled() { + return enabled; + } + + public E getEnum() { + return itemEnum; + } + + } + + protected static class ConfirmationOptionInstance { + + protected final E listItem; + protected final HTMLElement element; + + protected ConfirmationOptionInstance(E listItem, HTMLElement element) { + this.listItem = listItem; + this.element = element; + } + + } + + protected final HTMLElement parent; + protected final List optionList; + protected final List> optionEnableList; + protected int currentSelected = -1; + + public ConfirmationPopupController(HTMLElement parent, List optionList) { + this.parent = parent; + this.optionList = optionList; + this.optionEnableList = new ArrayList<>(optionList.size()); + } + + public void setup() { + optionEnableList.clear(); + parent.setInnerHTML(""); + StringBuilder htmlBuilder = new StringBuilder(); + Escaper escaper = HtmlEscapers.htmlEscaper(); + currentSelected = -1; + for(int i = 0, l = optionList.size(); i < l; ++i) { + T itm = optionList.get(i); + if(i > 0) { + htmlBuilder.append("   "); + } + htmlBuilder.append( + " < "); + htmlBuilder.append(escaper.escape(itm.getName())); + htmlBuilder.append(" > "); + } + parent.setInnerHTML(htmlBuilder.toString()); + for(int i = 0, l = optionList.size(); i < l; ++i) { + T itm = optionList.get(i); + HTMLElement el = parent.querySelector("._eaglercraftX_boot_menu_popup_confirm_opt" + i); + if(el == null) { + throw new RuntimeException("Failed to select element from page: ._eaglercraftX_boot_menu_popup_confirm_opt" + i); + } + if(itm.getEnabled()) { + if(currentSelected == -1) { + currentSelected = 0; + el.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "popup_confirm_opt_selected"); + } + final ConfirmationOptionInstance newInstance = new ConfirmationOptionInstance(itm, el); + final int ii = optionEnableList.size(); + el.addEventListener("mouseover", (evt) -> { + BootMenuMain.runLater(() -> { + setSelected(ii); + }); + }); + el.addEventListener("click", (evt) -> { + BootMenuMain.runLater(() -> { + optionSelected(newInstance.listItem); + }); + }); + optionEnableList.add(newInstance); + }else { + el.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "popup_confirm_opt_disabled"); + } + } + } + + public void destroy() { + parent.setInnerHTML(""); + currentSelected = -1; + optionEnableList.clear(); + } + + public void setSelected(int idx) { + int listLen = optionEnableList.size(); + if(listLen == 0) { + idx = -1; + }else if(idx >= listLen) { + idx = listLen - 1; + }else if(idx < 0) { + idx = 0; + } + if(idx == currentSelected) { + return; + } + if(currentSelected >= 0 && currentSelected < optionEnableList.size()) { + optionEnableList.get(currentSelected).element.getClassList().remove(BootMenuConstants.cssClassPrefixBootMenu + "popup_confirm_opt_selected"); + } + currentSelected = idx; + if(idx != -1) { + optionEnableList.get(idx).element.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "popup_confirm_opt_selected"); + } + } + + public void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ARROW_LEFT) { + setSelected(currentSelected - 1); + }else if(keyCode == KeyCodes.DOM_KEY_ARROW_RIGHT) { + setSelected(currentSelected + 1); + }else if(keyCode == KeyCodes.DOM_KEY_ENTER) { + if(currentSelected >= 0 && currentSelected < optionEnableList.size()) { + optionSelected(optionEnableList.get(currentSelected).listItem); + } + } + } + + public void handleKeyRepeat(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ARROW_LEFT) { + setSelected(currentSelected - 1); + }else if(keyCode == KeyCodes.DOM_KEY_ARROW_RIGHT) { + setSelected(currentSelected + 1); + } + } + + protected abstract void optionSelected(T item); +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKClientFactory.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKClientFactory.java new file mode 100755 index 0000000..7325710 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKClientFactory.java @@ -0,0 +1,100 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Supplier; + +import org.json.JSONArray; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.server.export.EPKCompiler; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EPKClientFactory { + + private static final Logger logger = LogManager.getLogger("EPKClientFactory"); + + private static void doUpdateMessage(IProgressMsgCallback cb, String str) { + logger.info(str); + cb.updateMessage(str); + } + + public static void downloadEPKClient(LaunchConfigEntry launchConf, ClientDataEntry clientData, + Map> loaders, IProgressMsgCallback cb) { + doUpdateMessage(cb, "Generating manifest..."); + EaglercraftUUID randomLaunchUUID = EaglercraftUUID.randomUUID(); + EaglercraftUUID randomClientUUID = EaglercraftUUID.randomUUID(); + launchConf = launchConf.rotateUUIDs(randomLaunchUUID, randomClientUUID); + clientData = clientData.rotateUUID(randomClientUUID); + JSONArray launchDatas = new JSONArray(); + JSONObject launchObj = new JSONObject(); + launchConf.writeJSON(launchObj); + launchDatas.put(launchObj); + JSONArray clientDatas = new JSONArray(); + JSONObject clientObj = new JSONObject(); + clientData.writeJSON(clientObj); + clientDatas.put(clientObj); + JSONObject manifestObj = new JSONObject(); + manifestObj.put("launchData", launchDatas); + manifestObj.put("clientData", clientDatas); + byte[] manifestBytes = manifestObj.toString().getBytes(StandardCharsets.UTF_8); + Map blobs = new HashMap<>(); + for(EaglercraftUUID uuid : clientData.getReferencedBlobs()) { + String name = uuid.toString(); + doUpdateMessage(cb, "Resolving blobs (" + name + ")"); + if(!blobs.containsKey(name)) { + Supplier loader = loaders.get(uuid); + byte[] dat = null; + if(loader != null) { + dat = loader.get(); + } + if(dat == null) { + String msg = "Could not resolve blob! (" + name + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + blobs.put(name, dat); + } + } + doUpdateMessage(cb, "Compressing EPK file..."); + String fileName = launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_"); + if(fileName.length() > 251) { + fileName = fileName.substring(0, 251); + } + fileName = fileName + ".epk"; + EPKCompiler epkComp = new EPKCompiler(fileName, "unknown", "epk/client-archive-v1", true, false, + "\n\n # Eagler EPK v2.0 Client Archive v1\n # Clients: \"" + launchConf.displayName + "\"\n\n"); + byte[] epkData; + try { + epkComp.append("manifest.json", manifestBytes); + for(Entry blob : blobs.entrySet()) { + epkComp.append(blob.getKey(), blob.getValue()); + } + }finally { + epkData = epkComp.complete(); + } + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(fileName, epkData); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKClientParser.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKClientParser.java new file mode 100755 index 0000000..bd0af0b --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKClientParser.java @@ -0,0 +1,136 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.OfflineDownloadParser.ParsedOfflineAdapter; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.server.export.EPKDecompiler; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EPKClientParser { + + private static final Logger logger = LogManager.getLogger("EPKClientParser"); + + public static List parseEPKClient(byte[] epkData) throws IOException { + EPKDecompiler epkDecompiler = new EPKDecompiler(epkData); + EPKDecompiler.FileEntry fetr = epkDecompiler.readFile(); + if(fetr == null || !fetr.type.equals("HEAD") || !fetr.name.equals("file-type")) { + epkDecompiler.close(); + throw new IOException("File is incomplete!"); + } + if(!Arrays.equals(fetr.data, "epk/client-archive-v1".getBytes(StandardCharsets.UTF_8))) { + throw new IOException("File is not a client archive!"); + } + Map files = new HashMap<>(); + while((fetr = epkDecompiler.readFile()) != null) { + if(fetr.type.equals("FILE")) { + files.put(fetr.name, fetr.data); + }else { + logger.error("Skipping non-FILE entry: {} {}", fetr.type, fetr.name); + } + } + byte[] manifestData = files.get("manifest.json"); + if(manifestData == null) { + throw new IOException("File is incomplete!"); + } + List lst = new ArrayList<>(); + Map clientDatas; + List launchDatas; + try { + JSONObject mainfestJSON = new JSONObject(new String(manifestData, StandardCharsets.UTF_8)); + JSONArray launches = mainfestJSON.getJSONArray("launchData"); + JSONArray clients = mainfestJSON.getJSONArray("clientData"); + clientDatas = new HashMap<>(clients.length()); + launchDatas = new ArrayList<>(launches.length()); + for(int i = 0, l = clients.length(); i < l; ++i) { + JSONObject obj = clients.getJSONObject(i); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(obj.getString("uuid")); + if(!theUUID.equals(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN)) { + clientDatas.put(theUUID, new ClientDataEntry(theUUID, obj)); + } + } + for(int i = 0, l = launches.length(); i < l; ++i) { + JSONObject obj = launches.getJSONObject(i); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(obj.getString("uuid")); + if(!theUUID.equals(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN)) { + LaunchConfigEntry theEtr = new LaunchConfigEntry(theUUID, obj); + if(!BootMenuConstants.UUID_CLIENT_DATA_ORIGIN.equals(theEtr.clientDataUUID)) { + if(clientDatas.containsKey(theEtr.clientDataUUID)) { + launchDatas.add(theEtr); + }else { + logger.warn("Skipping launch config {} because the client data {} is missing!", theUUID, theEtr.clientDataUUID); + } + } + } + } + }catch(JSONException ex) { + throw new IOException("File manifest is corrupt!", ex); + } + Map blobs = new HashMap<>(); + Iterator itr = clientDatas.values().iterator(); + loadClientDatas: while(itr.hasNext()) { + ClientDataEntry etr = itr.next(); + for(EaglercraftUUID uuid : etr.getReferencedBlobs()) { + if(!blobs.containsKey(uuid)) { + byte[] blobBytes = files.get(uuid.toString()); + if(blobBytes == null) { + logger.error("Blob UUID {} for client data {} is missing!", uuid, etr.uuid); + itr.remove(); + continue loadClientDatas; + } + if(!EaglercraftUUID.nameUUIDFromBytes(blobBytes).equals(uuid)) { + logger.error("Blob UUID {} for client data {} has an invalid checksum!", uuid, etr.uuid); + itr.remove(); + continue loadClientDatas; + } + blobs.put(uuid, blobBytes); + } + } + } + List list = new ArrayList<>(launchDatas.size()); + for(LaunchConfigEntry etr : launchDatas) { + ClientDataEntry clientData = clientDatas.get(etr.clientDataUUID); + if(clientData == null) { + logger.error("Client data UUID {} for launch data {} is missing!", etr.clientDataUUID, etr.uuid); + continue; + } + Map entryBlob = new HashMap<>(); + for(EaglercraftUUID uuid : clientData.getReferencedBlobs()) { + entryBlob.put(uuid, blobs.get(uuid)); + } + list.add(new ParsedOfflineAdapter(etr, clientData, entryBlob)); + } + logger.info("Loaded {} blobs from fat offline", blobs.size()); + logger.info("Loaded {} client configurations from EPK file", clientDatas.size()); + logger.info("Loaded {} launch configurations from EPK file", launchDatas.size()); + return list; + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKDataEntry.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKDataEntry.java new file mode 100755 index 0000000..830635a --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EPKDataEntry.java @@ -0,0 +1,30 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EPKDataEntry { + + public final String extractTo; + public final EaglercraftUUID dataUUID; + + public EPKDataEntry(String extractTo, EaglercraftUUID dataUUID) { + this.extractTo = extractTo; + this.dataUUID = dataUUID; + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumClientFormatType.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumClientFormatType.java new file mode 100755 index 0000000..2f917d9 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumClientFormatType.java @@ -0,0 +1,67 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.Set; + +import com.google.common.collect.Sets; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumClientFormatType { + /** + * Eagler 1.8, b1.3, or pre-singleplayer 1.5 offline + */ + EAGLER_STANDARD_OFFLINE(0, "Standard Offline", Sets.newHashSet(EnumClientLaunchType.EAGLERX_V1, EnumClientLaunchType.EAGLER_1_5_V1, + EnumClientLaunchType.EAGLER_BETA_V1, EnumClientLaunchType.PEYTON_V2, EnumClientLaunchType.PEYTON_V1, + EnumClientLaunchType.STANDARD_OFFLINE_V1)), + + /** + * Eagler 1.5 offline with integrated server + */ + EAGLER_STANDARD_1_5_OFFLINE(1, "Standard 1.5 Offline", Sets.newHashSet(EnumClientLaunchType.EAGLER_1_5_V2)), + + /** + * Eagler 1.8 with certificate + */ + EAGLER_SIGNED_OFFLINE(2, "Signed Offline", Sets.newHashSet(EnumClientLaunchType.EAGLERX_SIGNED_V1)); + + public final int id; + public final String displayName; + public final Set launchTypes; + + private EnumClientFormatType(int id, String displayName, Set launchTypes) { + this.id = id; + this.displayName = displayName; + this.launchTypes = launchTypes; + } + + private static final EnumClientFormatType[] lookup = new EnumClientFormatType[3]; + + public static EnumClientFormatType getById(int id) { + if(id >= 0 && id < lookup.length) { + return lookup[id]; + }else { + return null; + } + } + + static { + EnumClientFormatType[] _values = values(); + for(int i = 0; i < _values.length; ++i) { + lookup[_values[i].id] = _values[i]; + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumClientLaunchType.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumClientLaunchType.java new file mode 100755 index 0000000..72f3d76 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumClientLaunchType.java @@ -0,0 +1,86 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumClientLaunchType { + /** + * Configuable Eagler 1.8-like offline + */ + STANDARD_OFFLINE_V1(0), + + /** + * Eagler 1.8 standard + */ + EAGLERX_V1(1), + + /** + * Eagler 1.8 with certificate + */ + EAGLERX_SIGNED_V1(2), + + /** + * Eagler 1.5 array "window.minecraftOpts" of element id, epk file, servers, server to join + */ + EAGLER_1_5_V1(3), + + /** + * Eagler 1.5 array "window.eaglercraftOpts" with "container" and "assetsURI" and "serverWorkerURI" + */ + EAGLER_1_5_V2(4), + + /** + * Eagler beta array "window.minecraftOpts" of element id, epk file, server to join + */ + EAGLER_BETA_V1(5), + + /** + * Peyton format with "window.classicConfig" array of root element id and epk file + */ + PEYTON_V1(6), + + /** + * Peyton format with "window.config" JSON object with "gameContainer" and "assetsLocation" + */ + PEYTON_V2(7); + + public final int id; + + private EnumClientLaunchType(int id) { + this.id = id; + } + + private static final EnumClientLaunchType[] lookup = new EnumClientLaunchType[8]; + + public static EnumClientLaunchType getById(int id) { + if(id >= 0 && id < lookup.length) { + return lookup[id]; + }else { + return null; + } + } + + public static EnumClientLaunchType[] _values() { + return lookup; + } + + static { + EnumClientLaunchType[] _values = values(); + for(int i = 0; i < _values.length; ++i) { + lookup[_values[i].id] = _values[i]; + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumOfflineParseType.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumOfflineParseType.java new file mode 100755 index 0000000..c6c5206 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/EnumOfflineParseType.java @@ -0,0 +1,53 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumOfflineParseType { + EAGLERCRAFTX_1_8_OFFLINE, + EAGLERCRAFTX_1_8_SIGNED, + EAGLERCRAFTX_1_8_FAT_OFFLINE, + EAGLERCRAFTX_1_8_FAT_SIGNED, + EAGLERCRAFT_1_5_NEW_OFFLINE, + EAGLERCRAFT_1_5_OLD_OFFLINE, + EAGLERCRAFT_BETA_B1_3_OFFLINE, + PEYTONPLAYZ585_ALPHA_BETA, + PEYTONPLAYZ585_INDEV, + EXPORTED_STANDARD_OFFLINE, + EAGLERCRAFT_EPK_FILE; + + public static EnumOfflineParseType inferFromClientFormat(EnumClientLaunchType etr) { + switch(etr) { + case STANDARD_OFFLINE_V1: + return EXPORTED_STANDARD_OFFLINE; + case EAGLERX_V1: + return EAGLERCRAFTX_1_8_OFFLINE; + case EAGLERX_SIGNED_V1: + return EAGLERCRAFTX_1_8_SIGNED; + case EAGLER_1_5_V1: + return EAGLERCRAFT_1_5_OLD_OFFLINE; + case EAGLER_1_5_V2: + return EAGLERCRAFT_1_5_NEW_OFFLINE; + case EAGLER_BETA_V1: + return EAGLERCRAFT_BETA_B1_3_OFFLINE; + case PEYTON_V1: + return PEYTONPLAYZ585_INDEV; + case PEYTON_V2: + return PEYTONPLAYZ585_ALPHA_BETA; + default: + throw new IllegalArgumentException(); + } + } +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/FatOfflineDownloadFactory.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/FatOfflineDownloadFactory.java new file mode 100755 index 0000000..497a17f --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/FatOfflineDownloadFactory.java @@ -0,0 +1,207 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint.UnsignedClientEPKLoader; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class FatOfflineDownloadFactory { + + private static final Logger logger = LogManager.getLogger("FatOfflineDownloadFactory"); + + public static void downloadOffline(List lst, IProgressMsgCallback cb) { + Map loadedBlobs = new HashMap<>(); + JSONArray manifestClientDatas = new JSONArray(); + Set manifestClientDatasSet = new HashSet<>(); + JSONArray manifestLaunchDatas = new JSONArray(); + Set manifestLaunchDatasSet = new HashSet<>(); + for(BootableClientEntry etr : lst) { + ClientDataEntry clientData = etr.bootAdapter.getClientDataEntry(); + LaunchConfigEntry launchConf = etr.bootAdapter.getLaunchConfigEntry(); + if(launchConf.uuid.equals(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN)) { + logger.warn("Cannot export origin launch configuration to fat offline!"); + continue; + } + if(manifestLaunchDatasSet.add(launchConf.uuid)) { + JSONObject obj = new JSONObject(); + launchConf.writeJSON(obj); + manifestLaunchDatas.put(obj); + if(!clientData.uuid.equals(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN) && manifestClientDatasSet.add(clientData.uuid)) { + obj = new JSONObject(); + clientData.writeJSON(obj); + manifestClientDatas.put(obj); + Map> loaders = etr.bootAdapter.getBlobLoaders(); + for(EaglercraftUUID uuid : clientData.getReferencedBlobs()) { + doUpdateMessage(cb, "Resolving data for \"" + launchConf.displayName + "\" (" + uuid + ")"); + if(!cacheLoad(loadedBlobs, loaders, uuid)) { + throw new NullPointerException("Blob for configuration \"" + launchConf.displayName + "\" is missing: " + uuid); + } + } + } + }else { + logger.warn("Skipping duplicate launch config uuid: {}", launchConf.uuid); + } + } + JSONObject manifest = new JSONObject(); + manifest.put("clientData", manifestClientDatas); + manifest.put("launchData", manifestLaunchDatas); + String manifestStr = manifest.toString().replace(StringUtils.reverse(">elyts/<"), "<_style>"); + boolean isSigned = BootMenuEntryPoint.isSignedClient(); + String template; + if(isSigned) { + doUpdateMessage(cb, "Loading offline_template_eaglercraftX_1_8_fat_signed.html"); + template = OfflineDownloadFactory.loadTemplate("offline_template_eaglercraftX_1_8_fat_signed.html"); + }else { + doUpdateMessage(cb, "Loading offline_template_eaglercraftX_1_8_fat_offline.html"); + template = OfflineDownloadFactory.loadTemplate("offline_template_eaglercraftX_1_8_fat_offline.html"); + } + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + template = template.replace("${num_clients}", Integer.toString(manifestLaunchDatas.length() + 1)); + JSONObject optsDump = BootMenuEntryPoint.getOriginLaunchOptsJSON(); + optsDump.put("bootMenuBlocksUnsignedClients", false); + RelayRandomizeHelper.makeOptsJSONHaveMacroHack(optsDump); + String optsStr = optsDump.toString(); + JSONObject launchConfJSON = new JSONObject(); + (new LaunchConfigEntry(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN, BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, + BootMenuConstants.client_projectForkName + " " + BootMenuConstants.client_projectOriginRevision + " " + + BootMenuConstants.client_projectOriginVersion, + isSigned ? EnumClientLaunchType.EAGLERX_SIGNED_V1 : EnumClientLaunchType.EAGLERX_V1, null, null, null, + null, null, optsStr, false)).writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + int relayIdCount = RelayRandomizeHelper.countRelayMacro(optsStr); + if(relayIdCount > 0) { + optsStr = RelayRandomizeHelper.replaceRelayMacroWithEqRelayId(optsStr); + } + template = template.replace("${relayId_max}", Integer.toString(relayIdCount)); + template = template.replace("${launch_opts}", optsStr); + if(isSigned) { + doUpdateMessage(cb, "Retrieving origin client signature and payload"); + template = template.replace("${client_signature}", Base64.encodeBase64String(BootMenuEntryPoint.getSignedClientSignature())); + template = template.replace("${client_bundle}", Base64.encodeBase64String(BootMenuEntryPoint.getSignedClientBundle())); + }else { + doUpdateMessage(cb, "Retrieving origin client classes.js"); + byte[] classesJS = BootMenuEntryPoint.getUnsignedClientClassesJS(); + if(classesJS == null) { + throw new NullPointerException("Could not load classes.js!"); + } + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(OfflineDownloadFactory.removeUseStrict(classesJS), StandardCharsets.UTF_8)); + UnsignedClientEPKLoader epkLoader = BootMenuEntryPoint.getUnsignedClientAssetsEPK(); + String assetsEPKVal; + int epkNum = epkLoader.list.size(); + if(epkNum > 1 || !StringUtils.isEmpty(epkLoader.list.get(0).extractTo)) { + StringBuilder assetsEPKBuilder = new StringBuilder("[ "); + for(int i = 0; i < epkNum; ++i) { + EPKDataEntry epk = epkLoader.list.get(i); + doUpdateMessage(cb, "Resolving assets.epk (" + epk.dataUUID + ", path: /" + epk.extractTo + ")"); + Supplier epkDataGetter = epkLoader.loaders.get(epk.dataUUID); + byte[] epkData = null; + if(epkDataGetter != null) { + epkData = epkDataGetter.get(); + } + if(epkData == null) { + String msg = "Could not resolve assets.epk! (" + epk.dataUUID + ", path: /" + epk.extractTo + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + if(i > 0) { + assetsEPKBuilder.append(", "); + } + assetsEPKBuilder.append("{ url: \"data:application/octet-stream;base64,"); + assetsEPKBuilder.append(Base64.encodeBase64String(epkData)); + assetsEPKBuilder.append("\", path: \""); + assetsEPKBuilder.append(OfflineDownloadFactory.stupidJSONEscape(epk.extractTo)); + assetsEPKBuilder.append("\" }"); + } + assetsEPKBuilder.append(" ]"); + assetsEPKVal = assetsEPKBuilder.toString(); + }else { + EPKDataEntry epk = epkLoader.list.get(0); + doUpdateMessage(cb, "Resolving assets.epk (" + epk.dataUUID + ", path: /)"); + Supplier epkDataGetter = epkLoader.loaders.get(epk.dataUUID); + byte[] epkData = null; + if(epkDataGetter != null) { + epkData = epkDataGetter.get(); + } + if(epkData == null) { + String msg = "Could not resolve assets.epk! (" + epk.dataUUID + ", path: /)"; + logger.error(msg); + throw new NullPointerException(msg); + } + assetsEPKVal = "\"data:application/octet-stream;base64," + Base64.encodeBase64String(epkData) + "\""; + } + + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), assetsEPKVal); + } + doUpdateMessage(cb, "Embedding additional clients as base64"); + StringBuilder fatOfflineDataBuilder = new StringBuilder(); + fatOfflineDataBuilder.append(StringUtils.reverse(">\"1v_tsefinam_enilffOtaFrelgae_\"=di \"tfarcrelgae\"=epyt elyts<")); + fatOfflineDataBuilder.append(manifestStr); + fatOfflineDataBuilder.append(StringUtils.reverse("\n>elyts/<")); + for(Entry etr : loadedBlobs.entrySet()) { + fatOfflineDataBuilder.append(StringUtils.reverse("_enilffOtaFrelgae_\"=di \"tfarcrelgae\"=epyt elyts<") + etr.getKey().toString() + "\">"); + fatOfflineDataBuilder.append(Base64.encodeBase64String(etr.getValue())); + fatOfflineDataBuilder.append(StringUtils.reverse("\n>elyts/<")); + } + template = template.replace(StringUtils.reverse("}atad_enilffo_taf{$"), fatOfflineDataBuilder.toString()); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName("EaglercraftX_1.8_Fat_Offline_Download.html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void doUpdateMessage(IProgressMsgCallback cb, String str) { + logger.info(str); + cb.updateMessage(str); + } + + private static boolean cacheLoad(Map loadedBlobs, + Map> loaders, EaglercraftUUID uuid) { + if(!loadedBlobs.containsKey(uuid)) { + Supplier getter = loaders.get(uuid); + if(getter != null) { + byte[] dat = getter.get(); + if(dat != null) { + loadedBlobs.put(uuid, dat); + return true; + }else { + return false; + } + }else { + return false; + } + }else { + return true; + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/IBootMenuConfigAdapter.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/IBootMenuConfigAdapter.java new file mode 100755 index 0000000..c614b0e --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/IBootMenuConfigAdapter.java @@ -0,0 +1,30 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IBootMenuConfigAdapter { + + public static final IBootMenuConfigAdapter instance = (IBootMenuConfigAdapter)TeaVMClientConfigAdapter.instance; + + boolean isAllowBootMenu(); + + boolean isShowBootMenuOnLaunch(); + + boolean isBootMenuBlocksUnsignedClients(); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/IProgressMsgCallback.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/IProgressMsgCallback.java new file mode 100755 index 0000000..274e2be --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/IProgressMsgCallback.java @@ -0,0 +1,22 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IProgressMsgCallback { + + void updateMessage(String msg); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/InputPopupController.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/InputPopupController.java new file mode 100755 index 0000000..4ecd3cb --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/InputPopupController.java @@ -0,0 +1,111 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLInputElement; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class InputPopupController { + + public final HTMLInputElement inputField; + public final boolean intValue; + public final HTMLElement cancelButton; + public final HTMLElement doneButton; + protected boolean cancelSelected = true; + + public InputPopupController(HTMLInputElement inputField, boolean intValue, HTMLElement cancelButton, HTMLElement doneButton) { + this.inputField = inputField; + this.intValue = intValue; + this.cancelButton = cancelButton; + this.doneButton = doneButton; + } + + public void setup(String initialValue) { + cancelSelected = true; + inputField.setValue(initialValue); + cancelButton.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "popup_input_opt_selected"); + doneButton.getClassList().remove(BootMenuConstants.cssClassPrefixBootMenu + "popup_input_opt_selected"); + } + + public void handleOnChanged(HTMLElement htmlElement) { + if(inputField == htmlElement) { + if(intValue) { + int i; + try { + i = Integer.parseInt(inputField.getValue().trim()); + }catch(NumberFormatException ex) { + inputField.setValue("0"); + return; + } + if(i < 0) { + inputField.setValue("0"); + } + }else { + inputField.setValue(inputField.getValue().trim()); + } + } + } + + public void handleOnClick(HTMLElement htmlElement) { + if(doneButton == htmlElement) { + onSave(inputField); + }else if(cancelButton == htmlElement) { + onCancel(); + } + } + + public void handleOnMouseOver(HTMLElement htmlElement) { + if(doneButton == htmlElement) { + setCancelSelected(false); + }else if(cancelButton == htmlElement) { + setCancelSelected(true); + } + } + + public void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ARROW_RIGHT) { + setCancelSelected(false); + }else if(keyCode == KeyCodes.DOM_KEY_ARROW_LEFT) { + setCancelSelected(true); + }else if(keyCode == KeyCodes.DOM_KEY_ENTER) { + handleOnChanged(inputField); + onSave(inputField); + }else if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + onCancel(); + } + } + + public void setCancelSelected(boolean sel) { + if(sel) { + if(!cancelSelected) { + cancelSelected = true; + cancelButton.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "popup_input_opt_selected"); + doneButton.getClassList().remove(BootMenuConstants.cssClassPrefixBootMenu + "popup_input_opt_selected"); + } + }else { + if(cancelSelected) { + cancelSelected = false; + doneButton.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "popup_input_opt_selected"); + cancelButton.getClassList().remove(BootMenuConstants.cssClassPrefixBootMenu + "popup_input_opt_selected"); + } + } + } + + protected abstract void onSave(HTMLInputElement inputField); + + protected abstract void onCancel(); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/KeyCodes.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/KeyCodes.java new file mode 100755 index 0000000..74f42af --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/KeyCodes.java @@ -0,0 +1,34 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class KeyCodes { + + public static final int DOM_KEY_SHIFT = 16; + public static final int DOM_KEY_CONTROL = 17; + public static final int DOM_KEY_ALT = 18; + public static final int DOM_KEY_ENTER = 13; + public static final int DOM_KEY_ESCAPE = 27; + + public static final int DOM_KEY_E = 69; + public static final int DOM_KEY_SPACE = 32; + + public static final int DOM_KEY_ARROW_LEFT = 37; + public static final int DOM_KEY_ARROW_UP = 38; + public static final int DOM_KEY_ARROW_RIGHT = 39; + public static final int DOM_KEY_ARROW_DOWN = 40; + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/LaunchConfigEntry.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/LaunchConfigEntry.java new file mode 100755 index 0000000..daed772 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/LaunchConfigEntry.java @@ -0,0 +1,143 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class LaunchConfigEntry { + + public final EaglercraftUUID uuid; + public final EaglercraftUUID clientDataUUID; + public String displayName; + public EnumClientLaunchType type; + public String joinServer; + public String launchOptsVar; + public String launchOptsAssetsURIVar; + public String launchOptsContainerVar; + public String mainFunction; + public String launchOpts; + public boolean clearCookiesBeforeLaunch; + + public LaunchConfigEntry(EaglercraftUUID uuid, EaglercraftUUID clientDataUUID) { + this.uuid = uuid; + this.clientDataUUID = clientDataUUID; + } + + public LaunchConfigEntry(EaglercraftUUID uuid, EaglercraftUUID clientDataUUID, String displayName, + EnumClientLaunchType type, String joinServer, String launchOptsVar, String assetsURIVar, + String containerVar, String mainFunction, String launchOpts, boolean clearCookiesBeforeLaunch) { + this.uuid = uuid; + this.clientDataUUID = clientDataUUID; + this.displayName = displayName; + this.type = type; + this.joinServer = joinServer; + this.launchOptsVar = launchOptsVar; + this.launchOptsAssetsURIVar = assetsURIVar; + this.launchOptsContainerVar = containerVar; + this.mainFunction = mainFunction; + this.launchOpts = launchOpts; + this.clearCookiesBeforeLaunch = clearCookiesBeforeLaunch; + } + + public LaunchConfigEntry(EaglercraftUUID uuid, JSONObject jsonObject) { + this.uuid = uuid; + EaglercraftUUID sanityUUID = EaglercraftUUID.fromString(jsonObject.getString("uuid")); + if(!sanityUUID.equals(uuid)) { + throw new IllegalArgumentException("The file's name UUID does not equal the UUID string found in the file!"); + } + int typeId = jsonObject.getInt("type"); + type = EnumClientLaunchType.getById(typeId); + if(type == null) { + throw new IllegalArgumentException("Unknown launch configuration type " + typeId + "!"); + } + clientDataUUID = EaglercraftUUID.fromString(jsonObject.getString("dataUUID")); + displayName = jsonObject.getString("displayName"); + clearCookiesBeforeLaunch = jsonObject.getBoolean("clearCookies"); + switch(type) { + case STANDARD_OFFLINE_V1: + launchOpts = jsonObject.getString("launchOpts"); + launchOptsVar = jsonObject.getString("launchOptsVar"); + launchOptsAssetsURIVar = jsonObject.getString("assetsURIVar"); + launchOptsContainerVar = jsonObject.getString("containerVar"); + mainFunction = jsonObject.getString("entryPoint"); + break; + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + launchOpts = jsonObject.getString("launchOpts"); + break; + case EAGLER_1_5_V1: + launchOpts = jsonObject.getString("launchOpts"); + joinServer = jsonObject.getString("joinServer"); + break; + case EAGLER_BETA_V1: + joinServer = jsonObject.getString("joinServer"); + break; + case PEYTON_V1: + break; + default: //? + break; + } + } + + public void writeJSON(JSONObject jsonObject) { + jsonObject.put("uuid", uuid.toString()); + jsonObject.put("type", type.id); + jsonObject.put("dataUUID", clientDataUUID.toString()); + jsonObject.put("displayName", displayName); + jsonObject.put("clearCookies", clearCookiesBeforeLaunch); + switch(type) { + case STANDARD_OFFLINE_V1: + jsonObject.put("launchOpts", launchOpts != null ? launchOpts : "{ }"); + jsonObject.put("launchOptsVar", launchOptsVar != null ? launchOptsVar : "eaglercraftXOpts"); + jsonObject.put("assetsURIVar", launchOptsAssetsURIVar != null ? launchOptsAssetsURIVar : "assetsURI"); + jsonObject.put("containerVar", launchOptsContainerVar != null ? launchOptsContainerVar : "container"); + jsonObject.put("entryPoint", mainFunction != null ? mainFunction : "main"); + break; + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + jsonObject.put("launchOpts", launchOpts != null ? launchOpts : "{ }"); + break; + case EAGLER_1_5_V1: + jsonObject.put("launchOpts", launchOpts != null ? launchOpts : "[NBT]{ }[/NBT]"); + jsonObject.put("joinServer", joinServer != null ? joinServer : ""); + break; + case EAGLER_BETA_V1: + jsonObject.put("joinServer", joinServer != null ? joinServer : ""); + break; + case PEYTON_V1: + break; + default: //? + break; + } + } + + public LaunchConfigEntry rotateUUIDs(EaglercraftUUID rotatedLaunchUUID, EaglercraftUUID rotatedClientUUID) { + return new LaunchConfigEntry(rotatedLaunchUUID, rotatedClientUUID, displayName, type, joinServer, launchOptsVar, + launchOptsAssetsURIVar, launchOptsContainerVar, mainFunction, launchOpts, clearCookiesBeforeLaunch); + } + + public LaunchConfigEntry fork() { + return new LaunchConfigEntry(uuid, clientDataUUID, displayName, type, joinServer, launchOptsVar, + launchOptsAssetsURIVar, launchOptsContainerVar, mainFunction, launchOpts, clearCookiesBeforeLaunch); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateConfirmation.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateConfirmation.java new file mode 100755 index 0000000..ab6c363 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateConfirmation.java @@ -0,0 +1,141 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.List; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Collections2; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuPopupStateConfirmation extends MenuState { + + public static enum EnumYesNoHelper { + YES("Yes"), + NO("No"); + + private final String str; + + private EnumYesNoHelper(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + } + + protected class SelectionItem implements ConfirmationPopupController.SelectionOption { + + protected E enumValue; + + protected SelectionItem(E enumValue) { + this.enumValue = enumValue; + } + + @Override + public String getName() { + return enumValue.toString(); + } + + } + + protected final String title; + protected final List options; + protected final ConfirmationPopupController popupController; + + public MenuPopupStateConfirmation(String title, List options) { + this.title = title; + this.options = options; + this.popupController = new ConfirmationPopupController( + BootMenuMain.bootMenuDOM.popup_confirm_opts, + new ArrayList(Collections2.transform(options, SelectionItem::new))) { + @Override + protected void optionSelected(SelectionItem item) { + MenuPopupStateConfirmation.this.selectCallback(item.enumValue); + } + }; + } + + @Override + protected void enterState() { + popupController.setup(); + BootMenuMain.bootMenuDOM.popup_confirm_title.setInnerText(title); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup_view_confirm); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void exitState() { + popupController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup_view_confirm); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void enterPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void exitPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + BootMenuMain.currentState.changePopupState(null); + return; + } + popupController.handleKeyDown(keyCode); + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + popupController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + + protected abstract void selectCallback(E enumValue); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateEditInteger.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateEditInteger.java new file mode 100755 index 0000000..1aa60ee --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateEditInteger.java @@ -0,0 +1,114 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLInputElement; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuPopupStateEditInteger extends MenuState { + + protected final InputPopupController inputPopupController; + protected final String menuTitle; + protected final int defaultValue; + + public MenuPopupStateEditInteger(String menuTitle, int defaultValue) { + this.inputPopupController = new InputPopupController((HTMLInputElement) BootMenuMain.bootMenuDOM.popup_input_val, + true, BootMenuMain.bootMenuDOM.popup_input_opt_cancel, BootMenuMain.bootMenuDOM.popup_input_opt_done) { + + @Override + protected void onSave(HTMLInputElement inputField) { + int i = 0; + try { + i = Integer.parseInt(inputField.getValue().trim()); + }catch(NumberFormatException ex) { + } + MenuPopupStateEditInteger.this.onSave(i); + } + + @Override + protected void onCancel() { + MenuPopupStateEditInteger.this.onCancel(); + } + + }; + this.menuTitle = menuTitle; + this.defaultValue = defaultValue; + } + + @Override + protected void enterState() { + BootMenuMain.bootMenuDOM.popup_input_title.setInnerText(menuTitle); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup_view_input); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup); + inputPopupController.setup(Integer.toString(defaultValue)); + } + + @Override + protected void exitState() { + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup_view_input); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void enterPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void exitPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void handleKeyDown(int keyCode) { + inputPopupController.handleKeyDown(keyCode); + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + inputPopupController.handleOnChanged(htmlElement); + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + inputPopupController.handleOnClick(htmlElement); + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + inputPopupController.handleOnMouseOver(htmlElement); + } + + @Override + protected void update() { + + } + + protected abstract void onSave(int i); + + protected abstract void onCancel(); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateEditString.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateEditString.java new file mode 100755 index 0000000..5610545 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateEditString.java @@ -0,0 +1,109 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLInputElement; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuPopupStateEditString extends MenuState { + + protected final InputPopupController inputPopupController; + protected final String menuTitle; + protected final String defaultValue; + + public MenuPopupStateEditString(String menuTitle, String defaultValue) { + this.inputPopupController = new InputPopupController((HTMLInputElement) BootMenuMain.bootMenuDOM.popup_input_val, + false, BootMenuMain.bootMenuDOM.popup_input_opt_cancel, BootMenuMain.bootMenuDOM.popup_input_opt_done) { + + @Override + protected void onSave(HTMLInputElement inputField) { + MenuPopupStateEditString.this.onSave(inputField.getValue().trim()); + } + + @Override + protected void onCancel() { + MenuPopupStateEditString.this.onCancel(); + } + + }; + this.menuTitle = menuTitle; + this.defaultValue = defaultValue; + } + + @Override + protected void enterState() { + BootMenuMain.bootMenuDOM.popup_input_title.setInnerText(menuTitle); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup_view_input); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup); + inputPopupController.setup(defaultValue); + } + + @Override + protected void exitState() { + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup_view_input); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void enterPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void exitPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void handleKeyDown(int keyCode) { + inputPopupController.handleKeyDown(keyCode); + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + inputPopupController.handleOnChanged(htmlElement); + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + inputPopupController.handleOnClick(htmlElement); + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + inputPopupController.handleOnMouseOver(htmlElement); + } + + @Override + protected void update() { + + } + + protected abstract void onSave(String str); + + protected abstract void onCancel(); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateFileChooser.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateFileChooser.java new file mode 100755 index 0000000..dcc3293 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateFileChooser.java @@ -0,0 +1,106 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuPopupStateFileChooser extends MenuState { + + protected final String text; + protected final String mime; + protected final String ext; + protected String msg; + private boolean waitingForFile = false; + + public MenuPopupStateFileChooser(String text, String mime, String ext) { + this.text = text; + this.mime = mime; + this.ext = ext; + } + + @Override + protected void enterState() { + BootMenuMain.bootMenuDOM.popup_confirm_opts.setInnerHTML(""); + BootMenuMain.bootMenuDOM.popup_confirm_title.setInnerText(text + "\n\nPress ESC to cancel"); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup_view_confirm); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup); + EagRuntime.displayFileChooser(mime, ext); + waitingForFile = true; + } + + @Override + protected void exitState() { + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup_view_confirm); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void enterPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void exitPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + waitingForFile = false; + onResult(null); + } + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + if(waitingForFile && EagRuntime.fileChooserHasResult()) { + waitingForFile = false; + onResult(EagRuntime.getFileChooserResult()); + } + } + + protected abstract void onResult(FileChooserResult res); + +} \ No newline at end of file diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateLoading.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateLoading.java new file mode 100755 index 0000000..2a64512 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateLoading.java @@ -0,0 +1,98 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.apache.commons.lang3.StringUtils; +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class MenuPopupStateLoading extends MenuState implements IProgressMsgCallback { + + protected final String text; + protected String msg; + + public MenuPopupStateLoading(String text) { + this.text = text; + } + + @Override + protected void enterState() { + BootMenuMain.bootMenuDOM.popup_confirm_opts.setInnerHTML(""); + BootMenuMain.bootMenuDOM.popup_confirm_title.setInnerText(!StringUtils.isAllEmpty(msg) ? (text + "\n\n" + msg) : text); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup_view_confirm); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void exitState() { + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup_view_confirm); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void enterPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void exitPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void handleKeyDown(int keyCode) { + + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + public void updateMessage(String msg) { + this.msg = msg; + BootMenuMain.bootMenuDOM.popup_confirm_title.setInnerText(!StringUtils.isAllEmpty(msg) ? (text + "\n\n" + msg) : text); + EagUtils.sleep(50l); + } + + @Override + protected void update() { + + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateSelection.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateSelection.java new file mode 100755 index 0000000..86ee032 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuPopupStateSelection.java @@ -0,0 +1,141 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Collections2; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuPopupStateSelection extends MenuState { + + public static class SelectionItem implements SelectionListController.ListItem { + + protected final String name; + protected final E enumValue; + + public SelectionItem(E enumValue, String name) { + this.name = name; + this.enumValue = enumValue; + } + + public SelectionItem(E enumValue) { + this.name = enumValue.toString(); + this.enumValue = enumValue; + } + + @Override + public String getName() { + return name; + } + + } + + protected final String title; + protected final List> items; + protected SelectionListController> selectionController; + + public MenuPopupStateSelection(String title, List> items) { + this.title = title; + this.items = items; + this.selectionController = new SelectionListController>(BootMenuMain.bootMenuDOM.popup_selection, items) { + @Override + protected void itemSelected(SelectionItem item) { + MenuPopupStateSelection.this.itemSelected(item.enumValue); + } + }; + } + + public static MenuPopupStateSelection createHelper(String title, List items, Consumer selectCallback) { + return new MenuPopupStateSelection(title, + new ArrayList>(Collections2.transform(items, SelectionItem::new))) { + @Override + protected void itemSelected(T item) { + selectCallback.accept(item); + } + }; + } + + @Override + protected void enterState() { + selectionController.setup(); + BootMenuMain.bootMenuDOM.popup_selection_title.setInnerText(title); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup_view_selection); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup_view_selection); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.popup); + } + + @Override + protected void enterPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void exitPopupBlockingState() { + throw new IllegalStateException(); + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + BootMenuMain.currentState.changePopupState(null); + return; + } + selectionController.handleKeyDown(keyCode); + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + selectionController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + + protected abstract void itemSelected(T item); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuState.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuState.java new file mode 100755 index 0000000..7ffb7f1 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuState.java @@ -0,0 +1,135 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.teavm.jso.dom.html.HTMLElement; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuState { + + protected MenuState currentPopup = null; + + public void changePopupState(MenuState popup) { + if(popup == null) { + if(currentPopup != null) { + currentPopup.exitState(); + exitPopupBlockingState(); + currentPopup = null; + } + }else { + if(currentPopup != null) { + currentPopup.exitState(); + } + currentPopup = popup; + enterPopupBlockingState(); + popup.enterState(); + } + } + + public void doEnterState() { + enterState(); + if(currentPopup != null) { + enterPopupBlockingState(); + currentPopup.enterState(); + } + } + + public void doExitState() { + if(currentPopup != null) { + currentPopup.exitState(); + exitPopupBlockingState(); + } + exitState(); + } + + protected abstract void enterState(); + + protected abstract void exitState(); + + protected abstract void enterPopupBlockingState(); + + protected abstract void exitPopupBlockingState(); + + public void doHandleKeyDown(int keyCode) { + if(currentPopup != null) { + currentPopup.doHandleKeyDown(keyCode); + }else { + handleKeyDown(keyCode); + } + } + + public void doHandleKeyUp(int keyCode) { + if(currentPopup != null) { + currentPopup.doHandleKeyUp(keyCode); + }else { + handleKeyUp(keyCode); + } + } + + public void doHandleKeyRepeat(int keyCode) { + if(currentPopup != null) { + currentPopup.doHandleKeyRepeat(keyCode); + }else { + handleKeyRepeat(keyCode); + } + } + + public void doHandleOnChange(HTMLElement element) { + if(currentPopup != null) { + currentPopup.doHandleOnChange(element); + }else { + handleOnChanged(element); + } + } + + public void doHandleOnClick(HTMLElement element) { + if(currentPopup != null) { + currentPopup.doHandleOnClick(element); + }else { + handleOnClick(element); + } + } + + public void doHandleOnMouseOver(HTMLElement element) { + if(currentPopup != null) { + currentPopup.doHandleOnMouseOver(element); + }else { + handleOnMouseOver(element); + } + } + + public void doUpdate() { + if(currentPopup != null) { + currentPopup.doUpdate(); + }else { + update(); + } + } + + protected abstract void handleKeyDown(int keyCode); + + protected abstract void handleKeyUp(int keyCode); + + protected abstract void handleKeyRepeat(int keyCode); + + protected abstract void handleOnChanged(HTMLElement htmlElement); + + protected abstract void handleOnClick(HTMLElement htmlElement); + + protected abstract void handleOnMouseOver(HTMLElement htmlElement); + + protected abstract void update(); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateBoot.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateBoot.java new file mode 100755 index 0000000..6de8a25 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateBoot.java @@ -0,0 +1,583 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuMetadata.DefaultLaunchTemplate; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.OfflineDownloadParser.ParsedOfflineAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class MenuStateBoot extends MenuState { + + private static final Logger logger = LogManager.getLogger("MenuStateBoot"); + + private static class BootItem implements SelectionListController.ListItem, Runnable { + + private final String name; + private final Consumer runnable; + private final Consumer onEdit; + + private BootItem(String name, Consumer runnable, Consumer onEdit) { + this.name = name; + this.runnable = runnable; + this.onEdit = onEdit; + } + + @Override + public String getName() { + return name; + } + + @Override + public void run() { + runnable.accept(this); + } + + } + + protected SelectionListController selectionController; + + private static enum EnumImportExportMenu { + IMPORT_CLIENT("Import Client"), + EXPORT_CLIENT("Export Client"), + EXPORT_OFFLINE_BUNDLE("Export Offline Bundle"), + CANCEL("Cancel"); + + private final String str; + + private EnumImportExportMenu(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + + } + + private static final List OPTIONS_NO_BUNDLE = Arrays.asList( + EnumImportExportMenu.IMPORT_CLIENT, EnumImportExportMenu.EXPORT_CLIENT, EnumImportExportMenu.CANCEL); + + private static final List OPTIONS_BUNDLE = Arrays.asList(EnumImportExportMenu.IMPORT_CLIENT, + EnumImportExportMenu.EXPORT_CLIENT, EnumImportExportMenu.EXPORT_OFFLINE_BUNDLE, + EnumImportExportMenu.CANCEL); + + private static enum EnumImportModeMenu { + AUTO_DETECT("Auto Detect"), + EAGLERCRAFT_EPK_FILE(".EPK Client Archive"), + EAGLERCRAFTX_1_8_OFFLINE("EaglercraftX 1.8 Offline .HTML"), + EAGLERCRAFTX_1_8_SIGNED("EaglercraftX 1.8 Signed .HTML"), + EAGLERCRAFTX_1_8_FAT_OFFLINE("EaglercraftX 1.8 Fat Offline .HTML"), + EAGLERCRAFTX_1_8_FAT_SIGNED("EaglercraftX 1.8 Fat Signed .HTML"), + EAGLERCRAFT_1_5_NEW_OFFLINE("Eaglercraft 1.5.2 Offline .HTML (post-22w34a)"), + EAGLERCRAFT_1_5_OLD_OFFLINE("Eaglercraft 1.5.2 Offline .HTML (pre-22w34a)"), + EAGLERCRAFT_BETA_B1_3_OFFLINE("Eaglercraft Beta 1.3 Offline .HTML"), + PEYTONPLAYZ585_BETA_1_7_3("PeytonPlayz585 Beta 1.7.3 Offline .HTML"), + PEYTONPLAYZ585_ALPHA_1_2_6("PeytonPlayz585 Alpha 1.2.6 Offline .HTML"), + PEYTONPLAYZ585_INDEV("PeytonPlayz585 Indev Offline .HTML"), + CANCEL("Cancel"); + + private final String str; + + private EnumImportModeMenu(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + + } + + private static enum EnumUnsignedClientAction { + DOWNLOAD_OFFLINE("Download Client"), + DOWNLOAD_EAGLERX_OFFLINE("Download EaglercraftX"), + CANCEL("Cancel"); + + private final String str; + + private EnumUnsignedClientAction(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + } + + public static void displayUnsignedError(final MenuState parentState, final Consumer onDownloadOffline, final Runnable onDone) { + parentState.changePopupState(new MenuPopupStateConfirmation( + "Error: This client does not have a valid signature!\n\nUnsigned clients are disabled on this website", + Arrays.asList(EnumUnsignedClientAction.values())) { + @Override + protected void selectCallback(EnumUnsignedClientAction enumValue) { + switch(enumValue) { + case DOWNLOAD_OFFLINE: + MenuPopupStateLoading loadingScreen = new MenuPopupStateLoading("Downloading client..."); + parentState.changePopupState(loadingScreen); + try { + onDownloadOffline.accept(loadingScreen); + }catch(Throwable t) { + logger.error("Failed to invoke download offline function!"); + logger.error(t); + parentState.changePopupState(new MenuPopupStateConfirmation( + "Error: Failed to download!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + onDone.run(); + } + }); + return; + } + onDone.run(); + break; + case DOWNLOAD_EAGLERX_OFFLINE: + loadingScreen = new MenuPopupStateLoading("Downloading client..."); + parentState.changePopupState(loadingScreen); + try { + BootableClientEntry.getOriginClient().bootAdapter.downloadOffline(loadingScreen); + }catch(Throwable t) { + logger.error("Failed to invoke download offline function!"); + logger.error(t); + parentState.changePopupState(new MenuPopupStateConfirmation( + "Error: Failed to download!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + onDone.run(); + } + }); + return; + } + onDone.run(); + break; + case CANCEL: + default: + onDone.run(); + break; + } + } + }); + } + + protected int bootCountDown = 0; + protected int bootCountDownCur = 0; + protected long bootCountDownStart = 0l; + protected boolean doCountDown; + + public MenuStateBoot(boolean doCountDown) { + this.doCountDown = doCountDown; + List list = new ArrayList<>(); + final List bootableClients = BootableClientEntry.enumerateBootableClients(); + if(BootableClientEntry.applyClientOrdering(BootMenuMain.bootMenuDataManager.launchOrderList, bootableClients)) { + BootMenuMain.bootMenuDataManager.writeManifest(); + } + for(int i = 0, l = bootableClients.size(); i < l; ++i) { + final BootableClientEntry etr = bootableClients.get(i); + list.add(new BootItem(etr.bootAdapter.getDisplayName(), + (itm) -> { + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Booting: '" + itm.name + "'..."); + MenuStateBoot.this.changePopupState(popupState); + BootMenuMain.runLaterMS(() -> { + try { + etr.bootAdapter.bootClient(popupState); + }catch(UnsignedBootException ex) { + displayUnsignedError(MenuStateBoot.this, etr.bootAdapter::downloadOffline, () -> { + MenuStateBoot.this.changePopupState(null); + }); + return; + }catch(Throwable t) { + logger.error("Failed to boot client!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation("Error: Failed to boot client!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateBoot.this.changePopupState(null); + } + }); + return; + } + }, 250); + }, (itm) -> { + ClientDataEntry clientData = etr.bootAdapter.getClientDataEntry(); + LaunchConfigEntry launchConf = etr.bootAdapter.getLaunchConfigEntry(); + if(clientData != null && launchConf != null) { + BootMenuMain.changeState(new MenuStateEditingLaunch(MenuStateBoot.this, launchConf.fork(), + clientData, false, etr.bootAdapter.getBlobLoaders())); + } + })); + } + list.add(new BootItem("Import/Export", + (itm) -> { + MenuStateBoot.this.changePopupState(MenuPopupStateSelection.createHelper("What do you wanna do?", + bootableClients.size() > 1 ? OPTIONS_BUNDLE : OPTIONS_NO_BUNDLE, (enumValue) -> { + switch(enumValue) { + case IMPORT_CLIENT: + MenuStateBoot.this.changePopupState(MenuPopupStateSelection.createHelper("Select the format of the client:", + Arrays.asList(EnumImportModeMenu.values()), (enumValue2) -> { + EnumOfflineParseType parseType = null; + if(enumValue2 == EnumImportModeMenu.CANCEL) { + MenuStateBoot.this.changePopupState(null); + return; + }else if(enumValue2 != EnumImportModeMenu.AUTO_DETECT) { + switch(enumValue2) { + case EAGLERCRAFTX_1_8_OFFLINE: parseType = EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE; break; + case EAGLERCRAFTX_1_8_SIGNED: parseType = EnumOfflineParseType.EAGLERCRAFTX_1_8_SIGNED; break; + case EAGLERCRAFTX_1_8_FAT_OFFLINE: parseType = EnumOfflineParseType.EAGLERCRAFTX_1_8_FAT_OFFLINE; break; + case EAGLERCRAFTX_1_8_FAT_SIGNED: parseType = EnumOfflineParseType.EAGLERCRAFTX_1_8_FAT_SIGNED; break; + case EAGLERCRAFT_1_5_NEW_OFFLINE: parseType = EnumOfflineParseType.EAGLERCRAFT_1_5_NEW_OFFLINE; break; + case EAGLERCRAFT_1_5_OLD_OFFLINE: parseType = EnumOfflineParseType.EAGLERCRAFT_1_5_OLD_OFFLINE; break; + case EAGLERCRAFT_BETA_B1_3_OFFLINE: parseType = EnumOfflineParseType.EAGLERCRAFT_BETA_B1_3_OFFLINE; break; + case PEYTONPLAYZ585_BETA_1_7_3: parseType = EnumOfflineParseType.PEYTONPLAYZ585_ALPHA_BETA; break; + case PEYTONPLAYZ585_ALPHA_1_2_6: parseType = EnumOfflineParseType.PEYTONPLAYZ585_ALPHA_BETA; break; + case PEYTONPLAYZ585_INDEV: parseType = EnumOfflineParseType.PEYTONPLAYZ585_INDEV; break; + case EAGLERCRAFT_EPK_FILE: parseType = EnumOfflineParseType.EAGLERCRAFT_EPK_FILE; break; + default: + MenuStateBoot.this.changePopupState(null); + return; + } + } + String mime = parseType == EnumOfflineParseType.EAGLERCRAFT_EPK_FILE ? null : "text/html"; + String ext = parseType == EnumOfflineParseType.EAGLERCRAFT_EPK_FILE ? "epk" : "html"; + final EnumOfflineParseType parseTypeF = parseType; + MenuStateBoot.this.changePopupState(new MenuPopupStateFileChooser("Importing client...", mime, ext) { + @Override + protected void onResult(FileChooserResult res) { + if(res != null) { + MenuPopupStateLoading loadingScreen = new MenuPopupStateLoading("Importing client..."); + MenuStateBoot.this.changePopupState(loadingScreen); + EagUtils.sleep(50l); + String offlineData = new String(res.fileData, StandardCharsets.UTF_8).replace("\r\n", "\n"); + EnumOfflineParseType parseType2 = parseTypeF; + if(parseType2 == null) { + loadingScreen.updateMessage("Detecting type..."); + try { + parseType2 = OfflineDownloadParser.detectOfflineType(offlineData); + if(parseType2 == null) { + throw new IllegalStateException("Failed to detect offline download type!"); + } + }catch(Throwable t) { + MenuStateBoot.this.changePopupState(new MenuPopupStateConfirmation( + t.toString(), Arrays.asList(EnumImportModeMenu.CANCEL)) { + @Override + protected void selectCallback(EnumImportModeMenu enumValue) { + MenuStateBoot.this.changePopupState(null); + return; + } + }); + return; + } + } + loadingScreen.updateMessage("Parsing file..."); + List parsedOfflines; + try { + if(parseType2 == EnumOfflineParseType.EAGLERCRAFT_EPK_FILE) { + parsedOfflines = EPKClientParser.parseEPKClient(res.fileData); + }else { + parsedOfflines = OfflineDownloadParser.parseOffline(parseType2, offlineData); + } + if(parsedOfflines == null || parsedOfflines.size() == 0) { + throw new IllegalStateException("Failed to parse the file as \"" + parseType2 + "\"!"); + } + }catch(Throwable t) { + MenuStateBoot.this.changePopupState(new MenuPopupStateConfirmation( + t.toString(), Arrays.asList(EnumImportModeMenu.CANCEL)) { + @Override + protected void selectCallback(EnumImportModeMenu enumValue) { + MenuStateBoot.this.changePopupState(null); + } + }); + return; + } + if(parsedOfflines.size() == 1) { + ParsedOfflineAdapter pp = parsedOfflines.get(0); + if(pp.launchData == null) { + List templates = BootMenuMain.bootMenuMetadata.getTemplatesForParseType(pp.parseType); + if(templates.size() == 0) { + throw new IllegalStateException("Missing default launch type for parse type: " + pp.parseType); + } + if(templates.size() == 1) { + EaglercraftUUID rotatedLaunchUUID = EaglercraftUUID.randomUUID(); + EaglercraftUUID rotatedClientUUID = EaglercraftUUID.randomUUID(); + LaunchConfigEntry launchConfig = templates.get(0).createLaunchConfig(rotatedLaunchUUID, rotatedClientUUID); + ClientDataEntry clientData = pp.clientData.rotateUUID(rotatedClientUUID); + MenuStateBoot.this.changePopupState(null); + BootMenuMain.changeState(MenuStateEditingLaunch.createHelper(MenuStateBoot.this, launchConfig, clientData, true, pp.blobs)); + }else { + MenuStateBoot.this.changePopupState( + MenuPopupStateSelection.createHelper( + "Please select the template launch configuration to use:", + templates, (template) -> { + if(template != null) { + EaglercraftUUID rotatedLaunchUUID = EaglercraftUUID.randomUUID(); + EaglercraftUUID rotatedClientUUID = EaglercraftUUID.randomUUID(); + LaunchConfigEntry launchConfig = template.createLaunchConfig(rotatedLaunchUUID, rotatedClientUUID); + ClientDataEntry clientData = pp.clientData.rotateUUID(rotatedClientUUID); + MenuStateBoot.this.changePopupState(null); + BootMenuMain.changeState(MenuStateEditingLaunch.createHelper(MenuStateBoot.this, launchConfig, clientData, true, pp.blobs)); + }else { + MenuStateBoot.this.changePopupState(null); + } + })); + } + }else { + EaglercraftUUID rotatedLaunchUUID = EaglercraftUUID.randomUUID(); + EaglercraftUUID rotatedClientUUID = EaglercraftUUID.randomUUID(); + LaunchConfigEntry launchConfig = pp.launchData.rotateUUIDs(rotatedLaunchUUID, rotatedClientUUID); + ClientDataEntry clientData = pp.clientData.rotateUUID(rotatedClientUUID); + MenuStateBoot.this.changePopupState(null); + BootMenuMain.changeState(MenuStateEditingLaunch.createHelper(MenuStateBoot.this, launchConfig, clientData, true, pp.blobs)); + } + }else { + MenuStateBoot.this.changePopupState(null); + BootMenuMain.changeState(new MenuStateImportMultiSelect(MenuStateBoot.this, parsedOfflines)); + } + }else { + MenuStateBoot.this.changePopupState(null); + return; + } + } + }); + + })); + break; + case EXPORT_CLIENT: + final MenuState[] formatSelState = new MenuState[1]; + formatSelState[0] = MenuPopupStateSelection.createHelper("Select the format of the client:", + Arrays.asList(EnumImportModeMenu.AUTO_DETECT, EnumImportModeMenu.EAGLERCRAFT_EPK_FILE, + EnumImportModeMenu.EAGLERCRAFTX_1_8_OFFLINE, + EnumImportModeMenu.EAGLERCRAFTX_1_8_SIGNED, + EnumImportModeMenu.EAGLERCRAFT_1_5_NEW_OFFLINE, + EnumImportModeMenu.EAGLERCRAFT_1_5_OLD_OFFLINE, + EnumImportModeMenu.EAGLERCRAFT_BETA_B1_3_OFFLINE, + EnumImportModeMenu.PEYTONPLAYZ585_BETA_1_7_3, + EnumImportModeMenu.PEYTONPLAYZ585_ALPHA_1_2_6, + EnumImportModeMenu.PEYTONPLAYZ585_INDEV, + EnumImportModeMenu.CANCEL), (enumValue2) -> { + List filteredList; + if(enumValue2 == EnumImportModeMenu.CANCEL) { + MenuStateBoot.this.changePopupState(null); + return; + }else if(enumValue2 == EnumImportModeMenu.AUTO_DETECT || enumValue2 == EnumImportModeMenu.EAGLERCRAFT_EPK_FILE) { + filteredList = bootableClients; + }else { + filteredList = Lists.newArrayList(Collections2.filter(bootableClients, (etr) -> { + switch(enumValue2) { + case EAGLERCRAFTX_1_8_OFFLINE: + case EAGLERCRAFT_1_5_OLD_OFFLINE: + case EAGLERCRAFT_BETA_B1_3_OFFLINE: + case PEYTONPLAYZ585_BETA_1_7_3: + case PEYTONPLAYZ585_ALPHA_1_2_6: + case PEYTONPLAYZ585_INDEV: + return etr.bootAdapter.getClientDataEntry().type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE; + case EAGLERCRAFTX_1_8_SIGNED: + return etr.bootAdapter.getClientDataEntry().type == EnumClientFormatType.EAGLER_SIGNED_OFFLINE; + case EAGLERCRAFT_1_5_NEW_OFFLINE: + return etr.bootAdapter.getClientDataEntry().type == EnumClientFormatType.EAGLER_STANDARD_1_5_OFFLINE; + default: + return false; + } + })); + } + if(filteredList.size() > 0) { + MenuStateBoot.this.changePopupState(null); + BootMenuMain.changeState(new MenuStateSelectExportClients(enumValue2 == EnumImportModeMenu.EAGLERCRAFT_EPK_FILE, MenuStateBoot.this, filteredList)); + }else { + changePopupState(new MenuPopupStateConfirmation("Error: No clients available to export!", + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateBoot.this.changePopupState(formatSelState[0]); + } + }); + } + }); + MenuStateBoot.this.changePopupState(formatSelState[0]); + break; + case EXPORT_OFFLINE_BUNDLE: + MenuStateBoot.this.changePopupState(null); + BootMenuMain.changeState(new MenuStateClientMultiSelect(MenuStateBoot.this, bootableClients) { + @Override + protected void onDone(List entries) { + if(entries.size() > 0) { + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Exporting Fat Offline..."); + this.changePopupState(popupState); + try { + FatOfflineDownloadFactory.downloadOffline(entries, popupState); + }catch(Throwable t) { + logger.error("Failed to export fat offline!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation( + "Error: Failed to export!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + BootMenuMain.changeState(MenuStateBoot.this); + } + }); + return; + } + } + BootMenuMain.changeState(MenuStateBoot.this); + } + }); + break; + case CANCEL: + MenuStateBoot.this.changePopupState(null); + break; + default: + break; + } + })); + }, null)); + list.add(new BootItem("Enter Setup", + (itm) -> { + BootMenuMain.changeState(new MenuStateEnterSetup(bootableClients)); + }, null)); + selectionController = new SelectionListController(BootMenuMain.bootMenuDOM.content_selection, list) { + @Override + protected void itemSelected(BootItem item) { + item.run(); + } + }; + } + + @Override + protected void enterState() { + if(doCountDown) { + bootCountDownStart = EagRuntime.steadyTimeMillis(); + bootCountDown = BootMenuMain.bootMenuDataManager.confBootTimeout; + bootCountDownCur = bootCountDown; + } + selectionController.setup(); + if(bootCountDown > 0) { + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_boot_select_count); + BootMenuMain.bootMenuDOM.footer_text_boot_countdown.setInnerText(Integer.toString(bootCountDown)); + }else { + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_boot_select); + } + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_selection); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_boot_select); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_boot_select_count); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_selection); + } + + @Override + protected void enterPopupBlockingState() { + selectionController.setCursorEventsSuspended(true); + } + + @Override + protected void exitPopupBlockingState() { + selectionController.setCursorEventsSuspended(false); + } + + @Override + protected void handleKeyDown(int keyCode) { + boolean cancelEsc = bootCountDown > 0; + cancelCountdown(); + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + if(!cancelEsc) { + BootItem def = selectionController.selectionEnableList.get(0).listItem; + def.runnable.accept(def); + } + }else if(keyCode == KeyCodes.DOM_KEY_E) { + BootItem itm = selectionController.getSelected(); + if(itm.onEdit != null) { + itm.onEdit.accept(itm); + } + }else { + selectionController.handleKeyDown(keyCode); + } + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + cancelCountdown(); + selectionController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + if(bootCountDown > 0) { + int countDownCur = bootCountDown - (int)((EagRuntime.steadyTimeMillis() - bootCountDownStart) / 1000l); + if(countDownCur < 0) { + BootItem def = selectionController.selectionEnableList.get(0).listItem; + def.runnable.accept(def); + bootCountDown = 0; + return; + } + if(bootCountDownCur != countDownCur) { + bootCountDownCur = countDownCur; + BootMenuMain.bootMenuDOM.footer_text_boot_countdown.setInnerText(Integer.toString(countDownCur)); + } + } + } + + public void cancelCountdown() { + if(bootCountDown > 0) { + bootCountDown = 0; + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_boot_select_count); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_boot_select); + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateClientMultiSelect.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateClientMultiSelect.java new file mode 100755 index 0000000..d704131 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateClientMultiSelect.java @@ -0,0 +1,137 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.List; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuStateClientMultiSelect extends MenuState { + + protected static class BootItem implements SelectionListController.ListItem { + + protected final BootableClientEntry bootableClient; + protected final boolean alwaysSelected; + + public BootItem(BootableClientEntry bootableClient) { + this.bootableClient = bootableClient; + this.alwaysSelected = BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN + .equals(bootableClient.bootAdapter.getLaunchConfigEntry().uuid); + } + + @Override + public String getName() { + return bootableClient.bootAdapter.getDisplayName(); + } + + @Override + public boolean getAlwaysSelected() { + return alwaysSelected; + } + + } + + protected MenuState parentState; + protected CheckboxListController selectionController; + + public MenuStateClientMultiSelect(MenuState parentState, List bootableClients) { + this.parentState = parentState; + List list = new ArrayList<>(Collections2.transform(bootableClients, BootItem::new)); + selectionController = new CheckboxListController(BootMenuMain.bootMenuDOM.content_selection, list) { + + @Override + protected void cancelSelected() { + BootMenuMain.changeState(MenuStateClientMultiSelect.this.parentState); + } + + @Override + protected void doneSelected(List selectedItems) { + MenuStateClientMultiSelect.this.onDone(Lists.newArrayList(Collections2.transform(selectedItems, (itm) -> itm.bootableClient))); + } + + }; + } + + @Override + protected void enterState() { + selectionController.setup(); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void enterPopupBlockingState() { + selectionController.setCursorEventsSuspended(true); + } + + @Override + protected void exitPopupBlockingState() { + selectionController.setCursorEventsSuspended(false); + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + BootMenuMain.changeState(MenuStateClientMultiSelect.this.parentState); + }else { + selectionController.handleKeyDown(keyCode); + } + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + selectionController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + + protected abstract void onDone(List entries); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEditBootOrder.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEditBootOrder.java new file mode 100755 index 0000000..d5f60f3 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEditBootOrder.java @@ -0,0 +1,268 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MenuStateEditBootOrder extends MenuState { + + private static final EaglercraftUUID MAGIC_UUID_CANCEL = new EaglercraftUUID(0xD13983F5B764B3DL, 0xBF2C5157DEFDB5F9L); + private static final EaglercraftUUID MAGIC_UUID_DONE = new EaglercraftUUID(0x1F2BBFD548DD4818L, 0xB00B79D143BBF607L); + + private static final BootOrderItem CANCEL_ITEM = new BootOrderItem("Cancel", MAGIC_UUID_CANCEL); + private static final BootOrderItem DONE_ITEM = new BootOrderItem("Done", MAGIC_UUID_DONE); + + public static class BootOrderItem implements SelectionListController.ListItem { + + public final String displayName; + public final EaglercraftUUID uuid; + + public BootOrderItem(String displayName, EaglercraftUUID uuid) { + this.displayName = displayName; + this.uuid = uuid; + } + + @Override + public String getName() { + return displayName; + } + + } + + public static class BootOrderItemContainer { + + public final EaglercraftUUID uuid; + public final BootOrderItem exists; + + public BootOrderItemContainer(EaglercraftUUID uuid, BootOrderItem exists) { + this.uuid = uuid; + this.exists = exists; + } + + } + + protected final MenuState parent; + protected final SelectionListController selectionController; + protected final List existingBootItems; + protected final List allBootItems; + protected boolean controlDown = false; + + public MenuStateEditBootOrder(MenuState parent) { + this(parent, helper()); + } + + private static List helper() { + List enumBootItems = BootableClientEntry.enumerateBootableClients(); + if(BootableClientEntry.applyClientOrdering(BootMenuMain.bootMenuDataManager.launchOrderList, enumBootItems)) { + BootMenuMain.bootMenuDataManager.writeManifest(); + } + return enumBootItems; + } + + public MenuStateEditBootOrder(MenuState parent, List enumBootItems) { + this.parent = parent; + Map enumBootItemsMap = new HashMap<>(enumBootItems.size()); + for(int i = 0, l = enumBootItems.size(); i < l; ++i) { + BootableClientEntry etr = enumBootItems.get(i); + enumBootItemsMap.put(etr.bootAdapter.getLaunchConfigEntry().uuid, etr); + } + List lst = new ArrayList<>(); + List lst2 = new ArrayList<>(); + List lstSrc = BootMenuMain.bootMenuDataManager.launchOrderList; + for(int i = 0, l = lstSrc.size(); i < l; ++i) { + EaglercraftUUID uuid = lstSrc.get(i); + BootableClientEntry etr = enumBootItemsMap.get(uuid); + if(etr != null) { + BootOrderItem itm = new BootOrderItem(etr.bootAdapter.getDisplayName(), uuid); + lst.add(itm); + lst2.add(new BootOrderItemContainer(uuid, itm)); + }else { + lst2.add(new BootOrderItemContainer(uuid, null)); + } + } + existingBootItems = lst; + allBootItems = lst2; + List lst3 = new ArrayList<>(lst.size() + 2); + lst3.addAll(lst); + lst3.add(CANCEL_ITEM); + lst3.add(DONE_ITEM); + selectionController = new SelectionListController(BootMenuMain.bootMenuDOM.content_selection, lst3) { + @Override + protected void itemSelected(BootOrderItem item) { + if(item == DONE_ITEM) { + MenuStateEditBootOrder.this.fireHandleSave(); + }else if(item == CANCEL_ITEM) { + MenuStateEditBootOrder.this.handleCancel(); + } + } + }; + selectionController.setCursorEventsSuspended(true); // mouse over events might make it hard to reorder + } + + protected void fireHandleSave() { + List retList = new ArrayList<>(allBootItems.size()); + for(int i = 0, l = allBootItems.size(); i < l; ++i) { + retList.add(allBootItems.get(i).uuid); + } + handleSave(retList); + } + + @Override + protected void enterState() { + selectionController.setup(); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_boot_order); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_selection); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_boot_order); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_selection); + } + + @Override + protected void enterPopupBlockingState() { + + } + + @Override + protected void exitPopupBlockingState() { + + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + handleCancel(); + }else if(keyCode == KeyCodes.DOM_KEY_CONTROL) { + controlDown = true; + }else if(controlDown && keyCode == KeyCodes.DOM_KEY_ARROW_UP) { + moveEntryUp(); + }else if(controlDown && keyCode == KeyCodes.DOM_KEY_ARROW_DOWN) { + moveEntryDown(); + }else { + selectionController.handleKeyDown(keyCode); + } + } + + protected void moveEntryUp() { + BootOrderItem itm = selectionController.getSelected(); + if(itm == null || itm == DONE_ITEM || itm == CANCEL_ITEM) { + return; + } + int index = selectionController.currentSelected; + if(index <= 0) { + return; + } + EaglercraftUUID currentUUID = itm.uuid; + int index2 = -1; + for(int i = 0, l = allBootItems.size(); i < l; ++i) { + BootOrderItemContainer itm2 = allBootItems.get(i); + if(itm2.uuid.equals(currentUUID)) { + index2 = i; + break; + } + } + if(index2 == -1) { + throw new IllegalStateException(); + } + int newIndex = index - 1; + int newIndex2 = index2 - 1; + while(newIndex2 > 0 && allBootItems.get(newIndex2).exists == null) { + --newIndex2; + } + Collections.swap(existingBootItems, index, newIndex); + Collections.swap(allBootItems, index2, newIndex2); + selectionController.moveEntryUp(index); + } + + protected void moveEntryDown() { + BootOrderItem itm = selectionController.getSelected(); + if(itm == null || itm == DONE_ITEM || itm == CANCEL_ITEM) { + return; + } + int index = selectionController.currentSelected; + if(index >= existingBootItems.size() - 1) { + return; + } + EaglercraftUUID currentUUID = itm.uuid; + int index2 = -1; + for(int i = 0, l = allBootItems.size(); i < l; ++i) { + BootOrderItemContainer itm2 = allBootItems.get(i); + if(itm2.uuid.equals(currentUUID)) { + index2 = i; + break; + } + } + if(index2 == -1) { + throw new IllegalStateException(); + } + int newIndex = index + 1; + int newIndex2 = index2 + 1; + while(newIndex2 < allBootItems.size() - 1 && allBootItems.get(newIndex2).exists == null) { + ++newIndex2; + } + Collections.swap(existingBootItems, index, newIndex); + Collections.swap(allBootItems, index2, newIndex2); + selectionController.moveEntryDown(index); + } + + @Override + protected void handleKeyUp(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_CONTROL) { + controlDown = false; + } + } + + @Override + protected void handleKeyRepeat(int keyCode) { + selectionController.handleKeyDown(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + + protected abstract void handleSave(List reorderedList); + + protected abstract void handleCancel(); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEditingLaunch.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEditingLaunch.java new file mode 100755 index 0000000..a5db272 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEditingLaunch.java @@ -0,0 +1,674 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import org.json.JSONException; +import org.json.JSONObject; +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuMetadata.DefaultLaunchTemplate; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuMetadata.LaunchTemplate; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTException; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class MenuStateEditingLaunch extends MenuState { + + private static final Logger logger = LogManager.getLogger("MenuStateEditingLaunch"); + + protected final MenuState previousMenu; + protected final LaunchConfigEntry launchConf; + protected final ClientDataEntry clientData; + protected final boolean isImporting; + protected final Map> importClientBlobs; + protected boolean controlDown = false; + + public static MenuStateEditingLaunch createHelper(MenuState previousMenu, LaunchConfigEntry launchConf, + ClientDataEntry clientData, boolean isImporting, Map importClientBlobs) { + return new MenuStateEditingLaunch(previousMenu, launchConf, clientData, isImporting, + Maps.transformValues(importClientBlobs, (blob) -> { + return () -> blob; + })); + } + + public MenuStateEditingLaunch(MenuState previousMenu, LaunchConfigEntry launchConf, ClientDataEntry clientData, + boolean isImporting, Map> importClientBlobs) { + this.previousMenu = previousMenu; + this.launchConf = launchConf; + this.clientData = clientData; + this.isImporting = isImporting; + this.importClientBlobs = importClientBlobs; + } + + private void setVisibleControlSet() { + EnumClientLaunchType launchType = launchConf.type; + switch(launchType) { + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + case PEYTON_V1: + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_join_server); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_opts_name); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_assetsURI); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_container); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_main_func); + break; + case EAGLER_1_5_V1: + case EAGLER_BETA_V1: + BootMenuDOM.show(BootMenuMain.bootMenuDOM.launch_conf_join_server); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_opts_name); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_assetsURI); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_container); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_main_func); + break; + case STANDARD_OFFLINE_V1: + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_join_server); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.launch_conf_opts_name); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.launch_conf_assetsURI); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.launch_conf_container); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.launch_conf_main_func); + break; + } + } + + private void setControlSetValues() { + BootMenuMain.bootMenuDOM.launch_conf_val_data_format.setInnerText(clientData.type.displayName); + EnumClientLaunchType launchType = launchConf.type; + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_launch_type, launchType.toString()); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_profile_name, launchConf.displayName); + BootMenuDOM.setChecked(BootMenuMain.bootMenuDOM.launch_conf_val_clear_cookies, launchConf.clearCookiesBeforeLaunch); + switch(launchType) { + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_opt_editor, tryJSONFormat(launchConf.launchOpts)); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_container, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, ""); + break; + case EAGLER_1_5_V1: + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_opt_editor, launchConf.launchOpts); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, launchConf.joinServer); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_container, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, ""); + break; + case EAGLER_BETA_V1: + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_opt_editor, "JSON opts are not supported for Eaglercraft b1.3!"); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, launchConf.joinServer); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_container, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, ""); + break; + case PEYTON_V1: + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_opt_editor, "JSON opts are not supported for Indev!"); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_container, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, ""); + break; + case STANDARD_OFFLINE_V1: + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_opt_editor, tryJSONFormat(launchConf.launchOpts)); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, ""); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, launchConf.launchOptsVar); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, launchConf.launchOptsAssetsURIVar); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_container, launchConf.launchOptsContainerVar); + BootMenuDOM.setValue(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, launchConf.mainFunction); + break; + } + } + + private void setEnabledControlSet() { + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_launch_type, false); + setEnabledLaunchTypes(); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_profile_name, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_clear_cookies, false); + EnumClientLaunchType launchType = launchConf.type; + switch(launchType) { + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_opt_editor, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_container, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, true); + break; + case EAGLER_1_5_V1: + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_opt_editor, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_container, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, true); + break; + case EAGLER_BETA_V1: + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_opt_editor, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_container, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, true); + break; + case PEYTON_V1: + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_opt_editor, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_container, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, true); + break; + case STANDARD_OFFLINE_V1: + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_opt_editor, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_container, false); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, false); + break; + } + } + + private void readAllValues() { + EnumClientLaunchType newLaunchType = EnumClientLaunchType.valueOf(BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_launch_type)); + if(!clientData.type.launchTypes.contains(newLaunchType)) { + logger.error("nope!"); + throw new IllegalStateException("nope!"); + } + EnumClientLaunchType launchType = launchConf.type; + launchConf.type = newLaunchType; + launchConf.displayName = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_profile_name).trim(); + launchConf.clearCookiesBeforeLaunch = BootMenuDOM.getChecked(BootMenuMain.bootMenuDOM.launch_conf_val_clear_cookies); + switch(launchType) { + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + launchConf.launchOpts = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_opt_editor).trim(); + break; + case EAGLER_1_5_V1: + launchConf.launchOpts = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_opt_editor).trim(); + launchConf.joinServer = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server).trim(); + break; + case EAGLER_BETA_V1: + launchConf.joinServer = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_join_server).trim(); + break; + case PEYTON_V1: + break; + case STANDARD_OFFLINE_V1: + launchConf.launchOpts = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_opt_editor).trim(); + launchConf.launchOptsVar = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name).trim(); + launchConf.launchOptsAssetsURIVar = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI).trim(); + launchConf.launchOptsContainerVar = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_container).trim(); + launchConf.mainFunction = BootMenuDOM.getValue(BootMenuMain.bootMenuDOM.launch_conf_val_main_func).trim(); + break; + } + } + + private void setEnabledLaunchTypes() { + EnumClientFormatType clientType = clientData.type; + Set launchTypes = clientType.launchTypes; + EnumClientLaunchType[] itr = EnumClientLaunchType._values(); + for(int i = 0; i < itr.length; ++i) { + EnumClientLaunchType enumType = itr[i]; + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_launch_type_opts.get(enumType), + !launchTypes.contains(enumType)); + } + } + + private static String tryJSONFormat(String input) { + try { + return (new JSONObject(input)).toString(4); + }catch(JSONException ex) { + logger.warn("This client's JSON is corrupt! Failed to format"); + logger.warn(ex); + return input; + } + } + + @Override + protected void enterState() { + if(isImporting) { + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_opts_editor_alt); + }else { + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_opts_editor); + } + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_editor); + setEnabledControlSet(); + setControlSetValues(); + setVisibleControlSet(); + } + + @Override + protected void exitState() { + if(isImporting) { + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_opts_editor_alt); + }else { + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_opts_editor); + } + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_editor); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_join_server); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_opts_name); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_assetsURI); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_container); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.launch_conf_main_func); + } + + @Override + protected void enterPopupBlockingState() { + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_opt_editor, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_launch_type, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_profile_name, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_join_server, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_opts_name, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_assetsURI, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_container, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_main_func, true); + BootMenuDOM.setDisabled(BootMenuMain.bootMenuDOM.launch_conf_val_clear_cookies, true); + } + + @Override + protected void exitPopupBlockingState() { + setEnabledControlSet(); + } + + public static enum EnumEditorMenu { + BOOT("Boot configuration"), + SAVE("Save configuration"), + SAVE_COPY("Save a copy"), + LOAD_DEFAULTS("Load defaults"), + LOAD_TEMPLATE("Load template"), + EXPORT_OFFLINE("Export Offline"), + DELETE("Delete configuration"), + RETURN("Return to menu"), + CANCEL("Cancel"); + + private final String str; + + private EnumEditorMenu(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + } + + protected static final List EDITOR_MENU = Arrays.asList(EnumEditorMenu.BOOT, EnumEditorMenu.SAVE, + EnumEditorMenu.SAVE_COPY, EnumEditorMenu.LOAD_DEFAULTS, EnumEditorMenu.LOAD_TEMPLATE, + EnumEditorMenu.EXPORT_OFFLINE, EnumEditorMenu.DELETE, EnumEditorMenu.RETURN, EnumEditorMenu.CANCEL); + + protected static final List IMPORTER_MENU = Arrays.asList(EnumEditorMenu.SAVE, + EnumEditorMenu.LOAD_DEFAULTS, EnumEditorMenu.LOAD_TEMPLATE, EnumEditorMenu.EXPORT_OFFLINE, + EnumEditorMenu.RETURN, EnumEditorMenu.CANCEL); + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + returnToMenu(); + }else if(keyCode == KeyCodes.DOM_KEY_CONTROL) { + controlDown = true; + }else if(keyCode == KeyCodes.DOM_KEY_SHIFT) { + if(controlDown) { + changePopupState(MenuPopupStateSelection.createHelper("What do you wanna do?", isImporting ? IMPORTER_MENU : EDITOR_MENU, (opt) -> { + switch(opt) { + case BOOT: + bootCurrentConfig(); + break; + case SAVE: + saveAndExit(); + break; + case SAVE_COPY: + saveCopy(); + break; + case LOAD_DEFAULTS: + loadDefaults(); + break; + case LOAD_TEMPLATE: + loadTemplate(); + break; + case EXPORT_OFFLINE: + exportOffline(); + break; + case DELETE: + deleteConfig(); + break; + case RETURN: + returnToMenu(); + break; + case CANCEL: + default: + changePopupState(null); + break; + } + })); + } + }else if(keyCode == KeyCodes.DOM_KEY_ENTER) { + if(controlDown) { + if(isImporting) { + saveAndExit(); + }else { + bootCurrentConfig(); + } + } + } + } + + private String validateLaunchOpts() { + EnumClientLaunchType launchType = launchConf.type; + switch(launchType) { + case EAGLERX_V1: + case EAGLERX_SIGNED_V1: + case EAGLER_1_5_V2: + case PEYTON_V2: + case STANDARD_OFFLINE_V1: + try { + new JSONObject(launchConf.launchOpts); + return null; + }catch(JSONException ex) { + return ex.toString(); + } + case EAGLER_1_5_V1: + if(!launchConf.launchOpts.startsWith("[NBT]") || !launchConf.launchOpts.endsWith("[/NBT]")) { + return "Content does not begin/end with \"[NBT]\" and \"[/NBT]\""; + } + try { + String str = launchConf.launchOpts; + JsonToNBT.getTagFromJson(str.substring(5, str.length() - 6).trim()); + return null; + }catch(NBTException ex) { + return ex.toString(); + } + case EAGLER_BETA_V1: + case PEYTON_V1: + default: + return null; + } + } + + private void bootCurrentConfig() { + readAllValues(); + String err = validateLaunchOpts(); + if(err != null) { + changePopupState(new MenuPopupStateConfirmation("Error: Invalid syntax in launch opts!\n\n" + err, + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Booting: '" + launchConf.displayName + "'..."); + changePopupState(popupState); + BootMenuMain.runLaterMS(() -> { + try { + ClientBootFactory.bootClient(launchConf, clientData, importClientBlobs, popupState); + }catch(UnsignedBootException ex) { + MenuStateBoot.displayUnsignedError(MenuStateEditingLaunch.this, (cb) -> { + MenuStateEditingLaunch.this.exportOffline(); + }, () -> { + MenuStateEditingLaunch.this.changePopupState(null); + }); + return; + }catch(Throwable t) { + logger.error("Failed to boot client!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation("Error: Failed to boot client!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + try { + changePopupState(null); + }catch(Throwable t) { + } + }, 250); + } + + private void saveAndExit() { + readAllValues(); + String err = validateLaunchOpts(); + if(err != null) { + changePopupState(new MenuPopupStateConfirmation("Error: Invalid syntax in launch opts!\n\n" + err, + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Saving: '" + launchConf.displayName + "'..."); + changePopupState(popupState); + BootMenuMain.runLaterMS(() -> { + try { + BootMenuMain.bootMenuDataManager.installNewLaunchConfig(launchConf, clientData, + Maps.transformValues(importClientBlobs, (e) -> e.get()), false); + }catch(Throwable t) { + logger.error("Error: could not save launch config!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation("Error: Could not save launch config!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + BootMenuMain.changeState(new MenuStateBoot(false)); + }, 250); + } + + private void saveCopy() { + readAllValues(); + String err = validateLaunchOpts(); + if(err != null) { + changePopupState(new MenuPopupStateConfirmation("Error: Invalid syntax in launch opts!\n\n" + err, + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + final LaunchConfigEntry launchConfCopy = launchConf.rotateUUIDs(EaglercraftUUID.randomUUID(), launchConf.clientDataUUID); + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Saving: '" + launchConfCopy.displayName + "'..."); + changePopupState(popupState); + BootMenuMain.runLaterMS(() -> { + try { + BootMenuMain.bootMenuDataManager.installNewLaunchConfig(launchConfCopy, clientData, + Maps.transformValues(importClientBlobs, (e) -> e.get()), false); + }catch(Throwable t) { + logger.error("Error: could not save launch config!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation("Error: Could not save launch config!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + BootMenuMain.changeState(new MenuStateBoot(false)); + }, 250); + } + + private void loadDefaults() { + LaunchTemplate def = BootMenuMain.bootMenuMetadata.formatDefaultOptsMap.get(launchConf.type); + if(def != null) { + def.configureLaunchConfig(launchConf); + setControlSetValues(); + changePopupState(null); + }else { + changePopupState(new MenuPopupStateConfirmation("Error: Could not find default config!", + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + } + } + + private void loadTemplate() { + List templates = BootMenuMain.bootMenuMetadata.getTemplatesForParseType(EnumOfflineParseType.inferFromClientFormat(launchConf.type)); + if(templates != null && !templates.isEmpty()) { + List listWithCancel = Lists.newArrayList(templates); + listWithCancel.add("Cancel"); + changePopupState(MenuPopupStateSelection.createHelper("Select the template you would like to load:", listWithCancel, (template) -> { + if(template != null) { + if(!"Cancel".equals(template)) { + ((DefaultLaunchTemplate)template).templateState.configureLaunchConfig(launchConf); + setControlSetValues(); + changePopupState(null); + }else { + changePopupState(null); + } + } + })); + }else { + changePopupState(new MenuPopupStateConfirmation("Error: Could not find any templates!", + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + } + } + + private void exportOffline() { + readAllValues(); + String err = validateLaunchOpts(); + if(err != null) { + changePopupState(new MenuPopupStateConfirmation("Error: Invalid syntax in launch opts!\n\n" + err, + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Exporting: '" + launchConf.displayName + "'..."); + changePopupState(popupState); + BootMenuMain.runLaterMS(() -> { + try { + OfflineDownloadFactory.downloadOffline(launchConf, clientData, importClientBlobs, popupState); + }catch(Throwable t) { + logger.error("Failed to export offline!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation("Error: Failed to export offline!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEditingLaunch.this.changePopupState(null); + } + }); + return; + } + MenuStateEditingLaunch.this.changePopupState(null); + }, 250); + + } + + private void deleteConfig() { + changePopupState(new MenuPopupStateLoading("Deleting: '" + launchConf.displayName + "'...")); + BootMenuMain.runLaterMS(() -> { + try { + BootMenuMain.bootMenuDataManager.deleteLaunchConfig(launchConf.uuid); + }finally { + BootMenuMain.changeState(new MenuStateBoot(false)); + } + }, 250); + } + + private void returnToMenu() { + if(previousMenu != null) { + BootMenuMain.changeState(previousMenu); + } + } + + @Override + protected void handleKeyUp(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_CONTROL) { + controlDown = false; + } + } + + @Override + protected void handleKeyRepeat(int keyCode) { + + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + if(BootMenuMain.bootMenuDOM.launch_conf_val_launch_type == htmlElement) { + EnumClientLaunchType launchType = launchConf.type; + readAllValues(); + if(launchConf.type != launchType) { + setEnabledControlSet(); + setControlSetValues(); + setVisibleControlSet(); + } + } + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEnterSetup.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEnterSetup.java new file mode 100755 index 0000000..4a8e5e6 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateEnterSetup.java @@ -0,0 +1,261 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class MenuStateEnterSetup extends MenuState { + + private static enum EnumListMultiSelectType { + CHECKBOX_ALWAYS_SHOW, SUBMENU_BOOT_ORDER, SUBMENU_DELETE_ITEM, AUTOMATIC_BOOT_TIMEOUT, SUBMENU_CHANGE_TITLE, DONE; + } + + private static class MenuItem implements SelectionListController.ListItem { + + private final EnumListMultiSelectType type; + private final String displayName; + + private MenuItem(EnumListMultiSelectType type, String displayName) { + this.type = type; + this.displayName = displayName; + } + + @Override + public String getName() { + return displayName; + } + + } + + protected SelectionListController selectionController; + + public MenuStateEnterSetup(List bootableClients) { + int bootFlagsRet = BootMenuDataManager.getBootMenuFlags(BootMenuMain.win); + final int bootFlags = bootFlagsRet == -1 ? 0 : bootFlagsRet; + final boolean[] alwaysShowState = new boolean[] { (bootFlags & 1) != 0 }; + int timeout = BootMenuMain.bootMenuDataManager.confBootTimeout; + selectionController = new SelectionListController(BootMenuMain.bootMenuDOM.content_selection, Arrays.asList( + new MenuItem(EnumListMultiSelectType.CHECKBOX_ALWAYS_SHOW, (alwaysShowState[0] ? "[X]" : "[ ]") + " Always show boot menu on launch"), + new MenuItem(EnumListMultiSelectType.SUBMENU_BOOT_ORDER, "Change Boot Order"), + new MenuItem(EnumListMultiSelectType.SUBMENU_DELETE_ITEM, "Delete Boot Item"), + new MenuItem(EnumListMultiSelectType.AUTOMATIC_BOOT_TIMEOUT, "Automatic Boot Timeout: " + (timeout == 0 ? "Disabled" : timeout)), + new MenuItem(EnumListMultiSelectType.SUBMENU_CHANGE_TITLE, "Change Menu Title"), + new MenuItem(EnumListMultiSelectType.DONE, "Done") + )) { + + @Override + public void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_SPACE) { + if(currentSelected == 0) { // the checkbox + fireSelect(); + } + }else { + super.handleKeyDown(keyCode); + } + } + + @Override + protected void itemSelectedLow(ListItemInstance item) { + switch(item.listItem.type) { + case CHECKBOX_ALWAYS_SHOW: + alwaysShowState[0] = !alwaysShowState[0]; + BootMenuDataManager.setBootMenuFlags(BootMenuMain.win, (bootFlags & ~1) | (alwaysShowState[0] ? 1 : 0)); + item.element.setInnerText((alwaysShowState[0] ? "[X]" : "[ ]") + " Always show boot menu on launch"); + break; + case SUBMENU_BOOT_ORDER: + BootMenuMain.changeState(new MenuStateEditBootOrder(MenuStateEnterSetup.this, bootableClients) { + + @Override + protected void handleSave(List reorderedList) { + BootMenuMain.bootMenuDataManager.launchOrderList.clear(); + BootMenuMain.bootMenuDataManager.launchOrderList.addAll(reorderedList); + BootableClientEntry.applyClientOrdering(BootMenuMain.bootMenuDataManager.launchOrderList, bootableClients); + BootMenuMain.bootMenuDataManager.writeManifest(); + BootMenuMain.changeState(MenuStateEnterSetup.this); + } + + @Override + protected void handleCancel() { + BootMenuMain.changeState(MenuStateEnterSetup.this); + } + + }); + break; + case SUBMENU_DELETE_ITEM: + List deletableClients = new ArrayList<>(bootableClients.size() - 1); + for(int i = 0, l = bootableClients.size(); i < l; ++i) { + BootableClientEntry etr = bootableClients.get(i); + if(etr.dataType == BootableClientEntry.EnumDataType.LOCAL_STORAGE) { + deletableClients.add(etr); + } + } + if(!deletableClients.isEmpty()) { + BootMenuMain.changeState(new MenuStateClientMultiSelect(MenuStateEnterSetup.this, deletableClients) { + @Override + protected void onDone(List entries) { + if(entries != null && !entries.isEmpty()) { + for(int i = 0, l = entries.size(); i < l; ++i) { + EaglercraftUUID toDelete = entries.get(i).bootAdapter.getLaunchConfigEntry().uuid; + BootMenuMain.bootMenuDataManager.deleteLaunchConfig(toDelete); + } + List newEnum = BootableClientEntry.enumerateBootableClients(); + if(BootableClientEntry.applyClientOrdering(BootMenuMain.bootMenuDataManager.launchOrderList, newEnum)) { + BootMenuMain.bootMenuDataManager.writeManifest(); + } + BootMenuMain.changeState(new MenuStateEnterSetup(newEnum)); + }else { + BootMenuMain.changeState(MenuStateEnterSetup.this); + } + } + }); + }else { + changePopupState(new MenuPopupStateConfirmation( + "Error: No deletable clients!", + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + MenuStateEnterSetup.this.changePopupState(null); + } + }); + } + break; + case AUTOMATIC_BOOT_TIMEOUT: + MenuStateEnterSetup.this.changePopupState(new MenuPopupStateEditInteger("Enter the number of seconds, or 0 to disable:", BootMenuMain.bootMenuDataManager.confBootTimeout) { + + @Override + protected void onSave(int i) { + if(i < 0) i = 0; + if(i != BootMenuMain.bootMenuDataManager.confBootTimeout) { + BootMenuMain.bootMenuDataManager.confBootTimeout = i; + BootMenuMain.bootMenuDataManager.saveAdditionalConf(); + item.element.setInnerText("Automatic Boot Timeout: " + (i == 0 ? "Disabled" : i)); + } + MenuStateEnterSetup.this.changePopupState(null); + } + + @Override + protected void onCancel() { + MenuStateEnterSetup.this.changePopupState(null); + } + + }); + break; + case SUBMENU_CHANGE_TITLE: + MenuStateEnterSetup.this.changePopupState(new MenuPopupStateEditString( + "Enter the title to display on the menu:", BootMenuMain.bootMenuDataManager.confMenuTitle) { + + @Override + protected void onSave(String str) { + str = str.trim(); + if(!StringUtils.isEmpty(str) && !str.equals(BootMenuMain.bootMenuDataManager.confMenuTitle)) { + BootMenuMain.bootMenuDataManager.confMenuTitle = str; + BootMenuMain.bootMenuDataManager.saveAdditionalConf(); + BootMenuMain.bootMenuDOM.header_title.setInnerText(str); + } + MenuStateEnterSetup.this.changePopupState(null); + } + + @Override + protected void onCancel() { + MenuStateEnterSetup.this.changePopupState(null); + } + + }); + break; + case DONE: + BootMenuMain.changeState(new MenuStateBoot(false)); + break; + default: + break; + } + } + @Override + protected void itemSelected(MenuItem item) { + } + }; + } + + @Override + protected void enterState() { + selectionController.setup(); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void enterPopupBlockingState() { + selectionController.setCursorEventsSuspended(true); + } + + @Override + protected void exitPopupBlockingState() { + selectionController.setCursorEventsSuspended(false); + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + BootMenuMain.changeState(new MenuStateBoot(false)); + }else { + selectionController.handleKeyDown(keyCode); + } + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + selectionController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateImportMultiSelect.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateImportMultiSelect.java new file mode 100755 index 0000000..56e8837 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateImportMultiSelect.java @@ -0,0 +1,144 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.List; + +import org.teavm.jso.dom.html.HTMLElement; + +import com.google.common.collect.Collections2; + +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.OfflineDownloadParser.ParsedOfflineAdapter; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class MenuStateImportMultiSelect extends MenuState { + + protected static class BootItem implements SelectionListController.ListItem { + + protected final ParsedOfflineAdapter parsedClient; + + public BootItem(ParsedOfflineAdapter parsedClient) { + this.parsedClient = parsedClient; + } + + @Override + public String getName() { + return parsedClient.launchData.displayName; + } + + @Override + public boolean getAlwaysSelected() { + return false; + } + + } + + protected MenuState parentState; + protected CheckboxListController selectionController; + + public MenuStateImportMultiSelect(MenuState parentState, List parsedClients) { + this.parentState = parentState; + List list = new ArrayList<>(Collections2.transform(parsedClients, BootItem::new)); + selectionController = new CheckboxListController(BootMenuMain.bootMenuDOM.content_selection, list) { + + @Override + protected void cancelSelected() { + BootMenuMain.changeState(MenuStateImportMultiSelect.this.parentState); + } + + @Override + protected void doneSelected(List selectedItems) { + if(selectedItems.isEmpty()) { + cancelSelected(); + return; + } + MenuPopupStateLoading loadingScreen = new MenuPopupStateLoading("Importing clients..."); + MenuStateImportMultiSelect.this.changePopupState(loadingScreen); + for(int i = 0, l = selectedItems.size(); i < l; ++i) { + loadingScreen.updateMessage("Importing (" + (i + 1) + " / " + l + ")..."); + ParsedOfflineAdapter cl = selectedItems.get(i).parsedClient; + BootMenuMain.bootMenuDataManager.installNewClientData(cl.launchData, cl.clientData, cl.blobs, true); + } + BootMenuMain.changeState(new MenuStateBoot(false)); + } + + }; + } + + @Override + protected void enterState() { + selectionController.setup(); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void enterPopupBlockingState() { + selectionController.setCursorEventsSuspended(true); + } + + @Override + protected void exitPopupBlockingState() { + selectionController.setCursorEventsSuspended(false); + } + + @Override + protected void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ESCAPE) { + BootMenuMain.changeState(MenuStateImportMultiSelect.this.parentState); + }else { + selectionController.handleKeyDown(keyCode); + } + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + selectionController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateSelectExportClients.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateSelectExportClients.java new file mode 100755 index 0000000..97f38a7 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/MenuStateSelectExportClients.java @@ -0,0 +1,151 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class MenuStateSelectExportClients extends MenuState { + + private static final Logger logger = LogManager.getLogger("MenuStateSelectExportClients"); + + private static class BootItem implements SelectionListController.ListItem { + + private final BootableClientEntry bootEntry; + private final Consumer onSelected; + + public BootItem(BootableClientEntry bootEntry, Consumer onSelected) { + this.bootEntry = bootEntry; + this.onSelected = onSelected; + } + + @Override + public String getName() { + return bootEntry.bootAdapter.getDisplayName(); + } + + } + + protected final boolean exportEPK; + protected final MenuState parentState; + protected SelectionListController selectionController; + + public MenuStateSelectExportClients(boolean exportEPK, MenuStateBoot parentState, List filteredList) { + this.exportEPK = exportEPK; + this.parentState = parentState; + List lst = new ArrayList<>(filteredList.size()); + for(int i = 0, l = filteredList.size(); i < l; ++i) { + lst.add(new BootItem(filteredList.get(i), (etr) -> { + MenuPopupStateLoading popupState = new MenuPopupStateLoading("Downloading: '" + etr.bootAdapter.getDisplayName() + "'..."); + MenuStateSelectExportClients.this.changePopupState(popupState); + BootMenuMain.runLaterMS(() -> { + try { + if(exportEPK) { + etr.bootAdapter.downloadEPK(popupState); + }else { + etr.bootAdapter.downloadOffline(popupState); + } + }catch(Throwable t) { + logger.error("Failed to download client!"); + logger.error(t); + changePopupState(new MenuPopupStateConfirmation("Error: Failed to download client!\n\n" + t.toString(), + Arrays.asList("OK")) { + @Override + protected void selectCallback(String enumValue) { + BootMenuMain.changeState(parentState); + } + }); + return; + } + BootMenuMain.changeState(parentState); + }, 250); + })); + } + selectionController = new SelectionListController(BootMenuMain.bootMenuDOM.content_selection, lst) { + @Override + protected void itemSelected(BootItem item) { + item.onSelected.accept(item.bootEntry); + } + }; + } + + @Override + protected void enterState() { + selectionController.setup(); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.show(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void exitState() { + selectionController.destroy(); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.content_view_selection); + BootMenuDOM.hide(BootMenuMain.bootMenuDOM.footer_text_menu_select); + } + + @Override + protected void enterPopupBlockingState() { + selectionController.setCursorEventsSuspended(true); + } + + @Override + protected void exitPopupBlockingState() { + selectionController.setCursorEventsSuspended(false); + } + + @Override + protected void handleKeyDown(int keyCode) { + selectionController.handleKeyDown(keyCode); + } + + @Override + protected void handleKeyUp(int keyCode) { + + } + + @Override + protected void handleKeyRepeat(int keyCode) { + selectionController.handleKeyRepeat(keyCode); + } + + @Override + protected void handleOnChanged(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnClick(HTMLElement htmlElement) { + + } + + @Override + protected void handleOnMouseOver(HTMLElement htmlElement) { + + } + + @Override + protected void update() { + + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/OfflineDownloadFactory.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/OfflineDownloadFactory.java new file mode 100755 index 0000000..32f8626 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/OfflineDownloadFactory.java @@ -0,0 +1,613 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.google.common.html.HtmlEscapers; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.cache.EaglerLoadingCache; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.update.CertificateInvalidException; +import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class OfflineDownloadFactory { + + private static final Logger logger = LogManager.getLogger("OfflineDownloadFactory"); + + public static void downloadOffline(LaunchConfigEntry launchConf, ClientDataEntry clientData, + Map> loaders, IProgressMsgCallback cb) { + EaglerLoadingCache loadingCache = new EaglerLoadingCache((uuid) -> { + Supplier sup = loaders.get(uuid); + return sup != null ? sup.get() : null; + }); + switch(launchConf.type) { + case STANDARD_OFFLINE_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + downloadClientStandardOffline(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for STANDARD_OFFLINE_V1!"); + } + break; + case EAGLERX_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + downloadClientEaglerX18(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLERX_V1!"); + } + break; + case EAGLERX_SIGNED_V1: + if(clientData.type == EnumClientFormatType.EAGLER_SIGNED_OFFLINE) { + downloadClientEaglerX18Signed(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLERX_SIGNED_V1!"); + } + break; + case EAGLER_1_5_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + downloadClientEagler15Old(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLER_1_5_V1!"); + } + break; + case EAGLER_1_5_V2: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_1_5_OFFLINE) { + downloadClientEagler15New(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLER_1_5_V2!"); + } + break; + case EAGLER_BETA_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + downloadClientEaglerB13(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for EAGLER_1_5_V2!"); + } + break; + case PEYTON_V1: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + downloadClientPeytonIndev(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for PEYTON_V1!"); + } + break; + case PEYTON_V2: + if(clientData.type == EnumClientFormatType.EAGLER_STANDARD_OFFLINE) { + downloadClientPeytonAlphaBeta(launchConf, clientData, loadingCache, cb); + }else { + throw new UnsupportedOperationException("Wrong client data type " + clientData.type + " for PEYTON_V2!"); + } + break; + } + } + + static String loadTemplate(String name) { + name = "/assets/eagler/boot_menu/" + name; + String template = BootMenuAssets.loadResourceString(name); + if(template == null) { + throw new NullPointerException("Could not locate offline download template: " + name); + } + return template; + } + + private static void doUpdateMessage(IProgressMsgCallback cb, String str) { + logger.info(str); + cb.updateMessage(str); + } + + private static void downloadClientEaglerX18(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + String assetsEPKVal; + int epkNum = clientData.epkFiles.size(); + if(epkNum > 1 || !StringUtils.isEmpty(clientData.epkFiles.get(0).extractTo)) { + StringBuilder assetsEPKBuilder = new StringBuilder("[ "); + for(int i = 0; i < epkNum; ++i) { + EPKDataEntry epk = clientData.epkFiles.get(i); + doUpdateMessage(cb, "Resolving assets.epk (" + epk.dataUUID + ", path: /" + epk.extractTo + ")"); + byte[] epkData = loadingCache.get(epk.dataUUID); + if(epkData == null) { + String msg = "Could not resolve assets.epk! (" + epk.dataUUID + ", path: /" + epk.extractTo + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + if(i > 0) { + assetsEPKBuilder.append(", "); + } + assetsEPKBuilder.append("{ url: \"data:application/octet-stream;base64,"); + assetsEPKBuilder.append(Base64.encodeBase64String(epkData)); + assetsEPKBuilder.append("\", path: \""); + assetsEPKBuilder.append(stupidJSONEscape(epk.extractTo)); + assetsEPKBuilder.append("\" }"); + } + assetsEPKBuilder.append(" ]"); + assetsEPKVal = assetsEPKBuilder.toString(); + }else { + EPKDataEntry epk = clientData.epkFiles.get(0); + doUpdateMessage(cb, "Resolving assets.epk (" + epk.dataUUID + ", path: /)"); + byte[] epkData = loadingCache.get(epk.dataUUID); + if(epkData == null) { + String msg = "Could not resolve assets.epk! (" + epk.dataUUID + ", path: /)"; + logger.error(msg); + throw new NullPointerException(msg); + } + assetsEPKVal = "\"data:application/octet-stream;base64," + Base64.encodeBase64String(epkData) + "\""; + } + doUpdateMessage(cb, "Loading offline_template_eaglercraftX_1_8.html"); + String template = loadTemplate("offline_template_eaglercraftX_1_8.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + int relayIdCount = RelayRandomizeHelper.countRelayMacro(launchConf.launchOpts); + template = template.replace("${relayId_max}", Integer.toString(relayIdCount)); + String launchOptsFormatted; + try { + launchOptsFormatted = (new JSONObject(launchConf.launchOpts)).toString(4); + }catch(JSONException ex) { + throw new IllegalArgumentException("Launch options JSON is invalid! " + ex.toString()); + } + if(relayIdCount > 0) { + launchOptsFormatted = RelayRandomizeHelper.replaceRelayMacroWithEqRelayId(launchOptsFormatted); + } + template = template.replace("${launch_opts}", launchOptsFormatted); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), assetsEPKVal); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void downloadClientEaglerX18Signed(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + doUpdateMessage(cb, "Resolving client signature (" + clientData.clientSignature + ")"); + byte[] clientSignature = loadingCache.get(clientData.clientSignature); + if(clientSignature == null) { + String msg = "Could not resolve client signature! (" + clientData.clientSignature + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving client payload (" + clientData.mainPayload + ")"); + byte[] clientPayload = loadingCache.get(clientData.mainPayload); + if(clientPayload == null) { + String msg = "Could not resolve client payload! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Checking signature validity"); + UpdateCertificate cert = null; + try { + cert = UpdateCertificate.parseAndVerifyCertificate(clientSignature); + }catch(CertificateInvalidException | IOException e) { + logger.error("Signature invalid or not recognized!"); + logger.error(e); + logger.info("The client will be exported anyway. RIP"); + } + if(cert != null) { + if(!cert.isBundleDataValid(clientPayload)) { + logger.error("Client payload checksum does not match the certificate!"); + cert = null; + } + }else { + logger.info("Signature is valid: {} - {}", cert.bundleDisplayName, cert.bundleDisplayVersion); + } + doUpdateMessage(cb, "Loading offline_template_eaglercraftX_1_8_signed.html"); + String template = loadTemplate("offline_template_eaglercraftX_1_8_signed.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(cert != null ? (cert.bundleDisplayName + " " + cert.bundleDisplayVersion) : launchConf.displayName)); + SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy"); + template = template.replace("${date}", df.format(new Date())); + int relayIdCount = RelayRandomizeHelper.countRelayMacro(launchConf.launchOpts); + template = template.replace("${relayId_max}", Integer.toString(relayIdCount)); + String launchOptsFormatted; + try { + launchOptsFormatted = (new JSONObject(launchConf.launchOpts)).toString(4); + }catch(JSONException ex) { + throw new IllegalArgumentException("Launch options JSON is invalid! " + ex.toString()); + } + if(relayIdCount > 0) { + launchOptsFormatted = RelayRandomizeHelper.replaceRelayMacroWithEqRelayId(launchOptsFormatted); + } + template = template.replace("${launch_opts}", launchOptsFormatted); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + String fileName; + if(cert != null) { + String d8 = df.format(new Date(cert.sigTimestamp)); + template = template.replace("${signature_details}", cert.bundleDisplayName + " " + cert.bundleDisplayVersion + + " (" + cert.bundleVersionInteger + ") " + d8 + ", Author: " + cert.bundleAuthorName); + template = template.replace("${client_name_or_origin_date}", "This file is from " + d8 + ""); + fileName = cert.bundleDisplayName + "_" + cert.bundleDisplayVersion + "_Offline_Signed"; + }else { + template = template.replace("${signature_details}", "INVALID! (Or just from a 3rd party client)"); + template = template.replace("${client_name_or_origin_date}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + fileName = launchConf.displayName; + } + template = template.replace("${client_signature}", Base64.encodeBase64String(clientSignature)); + template = template.replace("${client_bundle}", Base64.encodeBase64String(clientPayload)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(fileName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void downloadClientEagler15New(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving classes_server.js (" + clientData.integratedServer + ")"); + byte[] classesServerJSBytes = loadingCache.get(clientData.integratedServer); + if(classesServerJSBytes == null) { + String msg = "Could not resolve classes_server.js! (" + clientData.integratedServer + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); + byte[] assetsEPKBytes = loadingCache.get(clientData.epkFiles.get(0).dataUUID); + if(assetsEPKBytes == null) { + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Loading offline_template_eaglercraft_1_5.html"); + String template = loadTemplate("offline_template_eaglercraft_1_5.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + int relayIdCount = RelayRandomizeHelper.countRelayMacro(launchConf.launchOpts); + template = template.replace("${relayId_max}", Integer.toString(relayIdCount)); + String launchOptsFormatted; + try { + launchOptsFormatted = (new JSONObject(launchConf.launchOpts)).toString(4); + }catch(JSONException ex) { + throw new IllegalArgumentException("Launch options JSON is invalid! " + ex.toString()); + } + if(relayIdCount > 0) { + launchOptsFormatted = RelayRandomizeHelper.replaceRelayMacroWithEqRelayId(launchOptsFormatted); + } + template = template.replace("${launch_opts}", launchOptsFormatted); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), Base64.encodeBase64String(assetsEPKBytes)); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + template = template.replace("${classes_server_js}", new String(removeUseStrict(classesServerJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void downloadClientEagler15Old(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); + byte[] assetsEPKBytes = loadingCache.get(clientData.epkFiles.get(0).dataUUID); + if(assetsEPKBytes == null) { + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Loading offline_template_eaglercraft_1_5_legacy.html"); + String template = loadTemplate("offline_template_eaglercraft_1_5_legacy.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace("${launch_opts}", ClientBootFactory.translateNBTOpts(launchConf.launchOpts)); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), Base64.encodeBase64String(assetsEPKBytes)); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void downloadClientEaglerB13(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); + byte[] assetsEPKBytes = loadingCache.get(clientData.epkFiles.get(0).dataUUID); + if(assetsEPKBytes == null) { + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Loading offline_template_eaglercraft_b1_3.html"); + String template = loadTemplate("offline_template_eaglercraft_b1_3.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), Base64.encodeBase64String(assetsEPKBytes)); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void downloadClientPeytonAlphaBeta(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); + byte[] assetsEPKBytes = loadingCache.get(clientData.epkFiles.get(0).dataUUID); + if(assetsEPKBytes == null) { + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Loading offline_template_peytonplayz585_a_b.html"); + String template = loadTemplate("offline_template_peytonplayz585_a_b.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + String launchOptsFormatted; + try { + launchOptsFormatted = (new JSONObject(launchConf.launchOpts)).toString(4); + }catch(JSONException ex) { + throw new IllegalArgumentException("Launch options JSON is invalid! " + ex.toString()); + } + template = template.replace("${launch_opts}", launchOptsFormatted); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), Base64.encodeBase64String(assetsEPKBytes)); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + private static void downloadClientPeytonIndev(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + if(clientData.epkFiles.size() != 1) { + throw new UnsupportedOperationException("Wrong number of EPK files: " + clientData.epkFiles.size()); + } + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); + byte[] assetsEPKBytes = loadingCache.get(clientData.epkFiles.get(0).dataUUID); + if(assetsEPKBytes == null) { + String msg = "Could not resolve assets.epk! (" + clientData.epkFiles.get(0).dataUUID + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + doUpdateMessage(cb, "Loading offline_template_peytonplayz585_indev.html"); + String template = loadTemplate("offline_template_peytonplayz585_indev.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), Base64.encodeBase64String(assetsEPKBytes)); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + static String stupidJSONEscape(String str) { + str = (new JSONArray().put(str)).toString(); + return str.substring(2, str.length() - 2); + } + + private static void downloadClientStandardOffline(LaunchConfigEntry launchConf, ClientDataEntry clientData, + EaglerLoadingCache loadingCache, IProgressMsgCallback cb) { + doUpdateMessage(cb, "Resolving classes.js (" + clientData.mainPayload + ")"); + byte[] classesJSBytes = loadingCache.get(clientData.mainPayload); + if(classesJSBytes == null) { + String msg = "Could not resolve classes.js! (" + clientData.mainPayload + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + JSONArray assetsEPKs = new JSONArray(); + for(EPKDataEntry epk : clientData.epkFiles) { + doUpdateMessage(cb, "Resolving assets.epk (" + epk.dataUUID + ", path: /" + epk.extractTo + ")"); + byte[] epkData = loadingCache.get(epk.dataUUID); + if(epkData == null) { + String msg = "Could not resolve assets.epk! (" + epk.dataUUID + ", path: /" + epk.extractTo + ")"; + logger.error(msg); + throw new NullPointerException(msg); + } + JSONObject obj = new JSONObject(); + obj.put("url", "data:application/octet-stream;base64," + Base64.encodeBase64String(epkData)); + obj.put("path", epk.extractTo); + assetsEPKs.put(obj); + } + doUpdateMessage(cb, "Loading offline_template_eaglercraftX_1_8.html"); + String template = loadTemplate("offline_template_eaglercraftX_1_8.html"); + template = template.replace("${client_name}", HtmlEscapers.htmlEscaper().escape(launchConf.displayName)); + template = template.replace("${date}", (new SimpleDateFormat("MM/dd/yyyy")).format(new Date())); + template = template.replace("${launch_opts_var_name}", stupidJSONEscape(launchConf.launchOptsVar)); + template = template.replace("${launch_opts_var_container_name}", stupidJSONEscape(launchConf.launchOptsContainerVar)); + template = template.replace("${launch_opts_var_assetsURI_name}", stupidJSONEscape(launchConf.launchOptsAssetsURIVar)); + template = template.replace("${main_function_name}", stupidJSONEscape(launchConf.mainFunction)); + int relayIdCount = RelayRandomizeHelper.countRelayMacro(launchConf.launchOpts); + template = template.replace("${relayId_max}", Integer.toString(relayIdCount)); + String launchOptsFormatted; + try { + launchOptsFormatted = (new JSONObject(launchConf.launchOpts)).toString(4); + }catch(JSONException ex) { + throw new IllegalArgumentException("Launch options JSON is invalid! " + ex.toString()); + } + if(relayIdCount > 0) { + launchOptsFormatted = RelayRandomizeHelper.replaceRelayMacroWithEqRelayId(launchOptsFormatted); + } + template = template.replace("${launch_opts}", launchOptsFormatted); + JSONObject launchConfJSON = new JSONObject(); + launchConf.writeJSON(launchConfJSON); + template = template.replace("${launch_conf_json}", launchConfJSON.toString()); + template = template.replace(StringUtils.reverse("}kpe_stessa{$"), assetsEPKs.toString()); + template = template.replace(StringUtils.reverse("}sj_sessalc{$"), new String(removeUseStrict(classesJSBytes), StandardCharsets.UTF_8)); + doUpdateMessage(cb, "Downloading file..."); + EagRuntime.downloadFileWithName(launchConf.displayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + ".html", template.getBytes(StandardCharsets.UTF_8)); + } + + public static byte[] removeClientScriptElement(byte[] input, boolean addUseStrict) { + byte[] str = "\"use strict\";\r\nif(typeof window !== \"undefined\") window.eaglercraftXClientScriptElement = document.currentScript;".getBytes(StandardCharsets.UTF_8); + if(input.length < str.length + 2) { + return input; + } + if(Arrays.equals(str, 0, str.length, input, 0, str.length)) { + return doUseStrict(input, str.length, addUseStrict); + } + if(Arrays.equals(str, 0, str.length, input, 1, str.length + 1)) { + return doUseStrict(input, str.length + 1, addUseStrict); + } + if(Arrays.equals(str, 0, str.length, input, 2, str.length + 2)) { + return doUseStrict(input, str.length + 2, addUseStrict); + } + str = "\"use strict\";\nif(typeof window !== \"undefined\") window.eaglercraftXClientScriptElement = document.currentScript;".getBytes(StandardCharsets.UTF_8); + if(input.length < str.length) { + return input; + } + if(Arrays.equals(str, 0, str.length, input, 0, str.length)) { + return doUseStrict(input, str.length, addUseStrict); + } + if(Arrays.equals(str, 0, str.length, input, 1, str.length + 1)) { + return doUseStrict(input, str.length + 1, addUseStrict); + } + if(Arrays.equals(str, 0, str.length, input, 2, str.length + 2)) { + return doUseStrict(input, str.length + 2, addUseStrict); + } + if(addUseStrict) { + str = new byte[] {(byte)34, (byte)117, (byte)115, (byte)101, (byte)32, (byte)115, (byte)116, (byte)114, (byte)105, (byte)99, (byte)116, (byte)34, (byte)59}; + if(Arrays.equals(str, 0, str.length, input, 0, str.length)) { + return input; + } + if(Arrays.equals(str, 0, str.length, input, 1, str.length + 1)) { + return input; + } + if(Arrays.equals(str, 0, str.length, input, 2, str.length + 2)) { + return input; + } + return doUseStrict(input, 0, addUseStrict); + }else { + return input; + } + } + + private static byte[] doUseStrict(byte[] input, int removeLength, boolean addUseStrict) { + if(addUseStrict) { + byte[] useStrict = new byte[] {(byte)34, (byte)117, (byte)115, (byte)101, (byte)32, (byte)115, (byte)116, (byte)114, (byte)105, (byte)99, (byte)116, (byte)34, (byte)59, (byte)10}; + while(removeLength < input.length && (input[removeLength] == '\n' || input[removeLength] == '\r')) { + ++removeLength; + } + int endRemoveLength = input.length; + while(endRemoveLength > removeLength + 1 && (input[endRemoveLength - 1] == '\n' || input[endRemoveLength - 1] == '\r')) { + --endRemoveLength; + } + byte[] ret = new byte[endRemoveLength - removeLength + useStrict.length]; + System.arraycopy(useStrict, 0, ret, 0, useStrict.length); + System.arraycopy(input, removeLength, ret, useStrict.length, endRemoveLength - removeLength); + return ret; + }else { + int endRemoveLength = input.length; + while(endRemoveLength > removeLength + 1 && (input[endRemoveLength - 1] == '\n' || input[endRemoveLength - 1] == '\r')) { + --endRemoveLength; + } + if(removeLength > 0 || endRemoveLength != input.length) { + return Arrays.copyOfRange(input, removeLength, endRemoveLength); + }else { + return input; + } + } + } + + public static byte[] removeUseStrict(byte[] input) { + byte[] str = new byte[] {(byte)34, (byte)117, (byte)115, (byte)101, (byte)32, (byte)115, (byte)116, (byte)114, (byte)105, (byte)99, (byte)116, (byte)34, (byte)59}; + if(input.length < str.length + 2) { + return input; + } + int i = 0; + if (Arrays.equals(str, 0, str.length, input, 0, str.length) + || Arrays.equals(str, 0, str.length, input, ++i, str.length + i) + || Arrays.equals(str, 0, str.length, input, ++i, str.length + i)) { + int removeLength = str.length + i; + while(removeLength < input.length && (input[removeLength] == '\n' || input[removeLength] == '\r')) { + ++removeLength; + } + int endRemoveLength = input.length; + while(endRemoveLength > removeLength + 1 && (input[endRemoveLength - 1] == '\n' || input[endRemoveLength - 1] == '\r')) { + --endRemoveLength; + } + return Arrays.copyOfRange(input, removeLength, endRemoveLength); + }else { + int endRemoveLength = input.length; + while(endRemoveLength > 1 && (input[endRemoveLength - 1] == '\n' || input[endRemoveLength - 1] == '\r')) { + --endRemoveLength; + } + if(endRemoveLength != input.length) { + return Arrays.copyOfRange(input, 0, endRemoveLength); + }else { + return input; + } + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/OfflineDownloadParser.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/OfflineDownloadParser.java new file mode 100755 index 0000000..19c4efd --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/OfflineDownloadParser.java @@ -0,0 +1,990 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class OfflineDownloadParser { + + private static final Logger logger = LogManager.getLogger("OfflineDownloadParser"); + + public static EnumOfflineParseType detectOfflineType(String offlineDownloadData) { + int hintIndex = offlineDownloadData.indexOf(StringUtils.reverse(">\"tniHesraPenilffOtfarcrelgae\"=epyt elyts<")); + if(hintIndex != -1) { + hintIndex += 42; + int closeTagIndex = offlineDownloadData.indexOf(StringUtils.reverse(">elyts/<"), hintIndex); + if(closeTagIndex != -1) { + try { + JSONObject parseHint = new JSONObject(offlineDownloadData.substring(hintIndex, closeTagIndex)); + return EnumOfflineParseType.valueOf(parseHint.getString("type")); + }catch(JSONException | IllegalArgumentException ex) { + logger.error("This offline download has a parse hint section, but the JSON is corrupt!"); + logger.error(ex); + } + } + } + if(offlineDownloadData.startsWith("EAGPKG$$")) { + logger.info("Detected format: EAGLERCRAFT_EPK_FILE"); + return EnumOfflineParseType.EAGLERCRAFT_EPK_FILE; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad>\"erutangiStneilCXtfarcrelgae\"=di \"tfarcrelgae\"=epyt elyts<")), 32, 2048)) { + logger.info("Detected format: EAGLERCRAFTX_1_8_SIGNED (post u24)"); + return EnumOfflineParseType.EAGLERCRAFTX_1_8_SIGNED; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = erutangiStneilCXtfarcrelgae.wodniw")), 32, 2048)) { + logger.info("Detected format: EAGLERCRAFTX_1_8_SIGNED (pre u24)"); + return EnumOfflineParseType.EAGLERCRAFTX_1_8_SIGNED; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = IRUstessa.stpOXtfarcrelgae.wodniw")), 8388608, offlineDownloadData.length() - 1048576)) { + logger.info("Detected format: EAGLERCRAFTX_1_8_OFFLINE (en_US)"); + return EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" :lru { [ = IRUstessa.stpOXtfarcrelgae.wodniw")), 8388608, offlineDownloadData.length() - 1048576)) { + logger.info("Detected format: EAGLERCRAFTX_1_8_OFFLINE (International)"); + return EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse("{ = stpOtfarcrelgae.wodniw")), 32, 2048) && foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">\"rekrow_ps\"=di \"rekrowrelgae/txet\"=epyt tpircs<")), 4194304, offlineDownloadData.length() - 1048576)) { + logger.info("Detected format: EAGLERCRAFTX_1_5_NEW_OFFLINE"); + return EnumOfflineParseType.EAGLERCRAFT_1_5_NEW_OFFLINE; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",\"emarf_emag\"\t\t\n[ = stpOtfarcenim.wodniw")), 32, 2048) || foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",\"emarf_emag\" \n[ = stpOtfarcenim.wodniw")), 32, 2048)) { + if(foundWithin(offlineDownloadData.indexOf("\"eaglercraft.minecraft = \\\"b1.3\\\"\\n\""), 524288, offlineDownloadData.length() - 1048576)) { + logger.info("Detected format: EAGLERCRAFT_BETA_B1_3_OFFLINE"); + return EnumOfflineParseType.EAGLERCRAFT_BETA_B1_3_OFFLINE; + }else if(foundWithin(offlineDownloadData.indexOf("\"eaglercraft.minecraft = \\\"1.5.2\\\"\\n\""), 2097152, offlineDownloadData.length() - 2097152)) { + logger.info("Detected format: EAGLERCRAFTX_1_5_OLD_OFFLINE"); + return EnumOfflineParseType.EAGLERCRAFT_1_5_OLD_OFFLINE; + } + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse("{ = gifnoc.wodniw")), 32, 512)) { + logger.info("Detected format: PEYTONPLAYZ585_ALPHA_BETA"); + return EnumOfflineParseType.PEYTONPLAYZ585_ALPHA_BETA; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",\"emarf_emag\"\t\t\n[ = gifnoCcissalc.wodniw")), 32, 512) || foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(",\"emarf_emag\" \n[ = gifnoCcissalc.wodniw")), 32, 512)) { + logger.info("Detected format: PEYTONPLAYZ585_INDEV"); + return EnumOfflineParseType.PEYTONPLAYZ585_INDEV; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">eltit/eltit<\n>/ \"8-FTU\"=tesrahc atem<\n>daeh<")), 32, 2048)) { + logger.info("Detected format: EAGLERCRAFTX_1_5_NEW_OFFLINE (maybe)"); + return EnumOfflineParseType.EAGLERCRAFT_1_5_NEW_OFFLINE; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">eltit/eltit<\n>daeh<")), 32, 2048)) { + if(foundWithin(offlineDownloadData.indexOf("\"eaglercraft.minecraft = \\\"b1.3\\\"\\n\""), 524288, offlineDownloadData.length() - 1048576)) { + logger.info("Detected format: EAGLERCRAFT_BETA_B1_3_OFFLINE"); + return EnumOfflineParseType.EAGLERCRAFT_BETA_B1_3_OFFLINE; + }else if(foundWithin(offlineDownloadData.indexOf("\"eaglercraft.minecraft = \\\"1.5.2\\\"\\n\""), 2097152, offlineDownloadData.length() - 2097152)) { + logger.info("Detected format: EAGLERCRAFTX_1_5_OLD_OFFLINE"); + return EnumOfflineParseType.EAGLERCRAFT_1_5_OLD_OFFLINE; + } + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">eltit/<8.1 XtfarcrelgaE>eltit<\n>/ \"8.8.1 ,8.1 ,tfarcenim ,xtfarcrelgae ,tfarcrelgae\"=tnetnoc \"sdrowyek\"=eman atem<\n>/ \"enilffO 8.1 XtfarcrelgaE\"=tnetnoc \"noitpircsed\"=eman atem<")), 32, 2048)) { + logger.info("Detected format: EAGLERCRAFTX_1_8_OFFLINE (maybe)"); + return EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">eltit/<3.7.1 ateB>eltit<")), 8, 512)) { + logger.info("Detected format: PEYTONPLAYZ585_ALPHA_BETA (maybe)"); + return EnumOfflineParseType.PEYTONPLAYZ585_ALPHA_BETA; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">eltit/<6.2.1v ahplA>eltit<")), 8, 512)) { + logger.info("Detected format: PEYTONPLAYZ585_ALPHA_BETA (maybe)"); + return EnumOfflineParseType.PEYTONPLAYZ585_ALPHA_BETA; + } + if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">eltit/eltit<")), 8, 512)) { + logger.info("Detected format: PEYTONPLAYZ585_INDEV (maybe)"); + return EnumOfflineParseType.PEYTONPLAYZ585_INDEV; + } + return null; + } + + private static boolean foundWithin(int idx, int min, int max) { + return idx >= min && idx < max; + } + + public static class ParsedOfflineAdapter { + + public final EnumOfflineParseType parseType; + public final LaunchConfigEntry launchData; + public final ClientDataEntry clientData; + public final Map blobs; + + public ParsedOfflineAdapter(EnumOfflineParseType parseType, ClientDataEntry clientData, + Map blobs) { + this.parseType = parseType; + this.launchData = null; + this.clientData = clientData; + this.blobs = blobs; + } + + public ParsedOfflineAdapter(LaunchConfigEntry launchData, ClientDataEntry clientData, + Map blobs) { + this.parseType = EnumOfflineParseType.inferFromClientFormat(launchData.type); + this.launchData = launchData; + this.clientData = clientData; + this.blobs = blobs; + } + + } + + public static List parseOffline(EnumOfflineParseType parseType, String offlineDownloadData) { + if(parseType == null) { + parseType = detectOfflineType(offlineDownloadData); + if(parseType == null) { + throw new IllegalArgumentException("Could not automatically detect offline download type!"); + } + } + switch(parseType) { + case EAGLERCRAFTX_1_8_OFFLINE: + return parseOfflineEaglerX18(offlineDownloadData); + case EAGLERCRAFTX_1_8_SIGNED: + return parseOfflineEaglerX18Signed(offlineDownloadData); + case EAGLERCRAFTX_1_8_FAT_OFFLINE: + return parseOfflineEaglerX18Fat(offlineDownloadData); + case EAGLERCRAFTX_1_8_FAT_SIGNED: + return parseOfflineEaglerX18FatSigned(offlineDownloadData); + case EAGLERCRAFT_1_5_NEW_OFFLINE: + return parseOfflineEagler15New(offlineDownloadData); + case EAGLERCRAFT_1_5_OLD_OFFLINE: + return parseOfflineEagler15Old(offlineDownloadData); + case EAGLERCRAFT_BETA_B1_3_OFFLINE: + return parseOfflineEaglerB13(offlineDownloadData); + case PEYTONPLAYZ585_ALPHA_BETA: + return parseOfflinePeytonAlphaBeta(offlineDownloadData); + case PEYTONPLAYZ585_INDEV: + return parseOfflinePeytonIndev(offlineDownloadData); + case EXPORTED_STANDARD_OFFLINE: + return parseStandardOffline(offlineDownloadData); + default: + throw new UnsupportedOperationException(); + } + } + + public static class TagIsolator { + + protected final String openTag; + protected final String closeTag; + protected final String str; + protected int currentIndex = 0; + + public TagIsolator(String openTag, String closeTag, String str) { + this.openTag = openTag; + this.closeTag = closeTag; + this.str = str; + } + + public String nextScript() { + if(currentIndex != -1) { + currentIndex = str.indexOf(openTag, currentIndex); + if(currentIndex == -1) { + return null; + } + currentIndex += openTag.length(); + int i2 = str.indexOf(closeTag, currentIndex); + if(i2 == -1) { + currentIndex = -1; + return null; + } + String ret = str.substring(currentIndex, i2); + currentIndex = i2 + closeTag.length(); + return ret; + }else { + return null; + } + } + + public List getAllTags() { + List ret = new ArrayList<>(); + String str; + while((str = nextScript()) != null) { + ret.add(str); + } + return ret; + } + + } + + private static LaunchConfigEntry tryReadParseHint(EnumOfflineParseType expectedType, String offlineDownloadData) { + int hintIndex = offlineDownloadData.indexOf(StringUtils.reverse(">\"tniHesraPenilffOtfarcrelgae\"=epyt elyts<")); + if(hintIndex != -1) { + hintIndex += 42; + int closeTagIndex = offlineDownloadData.indexOf(StringUtils.reverse(">elyts/<"), hintIndex); + if(closeTagIndex != -1) { + try { + JSONObject parseHint = new JSONObject(offlineDownloadData.substring(hintIndex, closeTagIndex)); + EnumOfflineParseType realType = EnumOfflineParseType.valueOf(parseHint.getString("type")); + if(realType != expectedType) { + throw new IllegalStateException("The offline download type is not \"" + expectedType + "\", metadata says it is \"" + realType + "\"!"); + } + JSONObject launchConf = parseHint.getJSONObject("launchConf"); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(launchConf.getString("uuid")); + return new LaunchConfigEntry(theUUID, launchConf); + }catch(JSONException | IllegalArgumentException ex) { + logger.error("This offline download has a parse hint section, but the JSON is corrupt!"); + logger.error(ex); + } + } + } + return null; + } + + private static List parseOfflineEaglerX18(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFTX_1_8_OFFLINE"); + return parseOfflineEaglerX18(EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE, offlineDownloadData); + } + + private static List parseOfflineEaglerX18(EnumOfflineParseType expectedType, String offlineDownloadData) { + LaunchConfigEntry launchConf = null; + try { + launchConf = tryReadParseHint(expectedType, offlineDownloadData); + }catch(IllegalStateException ex) { + logger.error(ex.getMessage()); + return null; + } + List scripts = (new TagIsolator(StringUtils.reverse(">\"tpircsavaj/txet\"=epyt tpircs<"), StringUtils.reverse(">tpircs/<"), offlineDownloadData)).getAllTags(); + if(scripts.size() == 1) { + logger.info("Detected a single script tag, must be u19 or earlier"); + return parseOfflineEaglerX18PreU20(launchConf, scripts.get(0)); + } + byte[] classesJSSrc = null; + int classesTagIndex = -1; + for(int i = 0, l = scripts.size(); i < l; ++i) { + String script = scripts.get(i); + int j; + if(foundWithin(j = script.indexOf(StringUtils.reverse("\n;tpircStnerruc.tnemucod = tnemelEtpircStneilCXtfarcrelgae.wodniw )\"denifednu\" ==! wodniw foepyt(fi")), 0, 32)) { + classesJSSrc = ("\"use strict\";\n" + script.substring(j + 99).trim()).getBytes(StandardCharsets.UTF_8); + classesTagIndex = i; + break; + } + } + if(classesJSSrc == null) { + logger.error("Could not find script tag for classes.js!"); + return null; + } + EaglercraftUUID classesJSUUID = EaglercraftUUID.nameUUIDFromBytes(classesJSSrc); + Map blobs = new HashMap<>(); + List epks = new ArrayList<>(2); + blobs.put(classesJSUUID, classesJSSrc); + for(int i = 0, l = scripts.size(); i < l; ++i) { + if(i == classesTagIndex) { + continue; + } + String script = scripts.get(i); + int j; + if(foundWithin(j = script.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" :lru { [ = IRUstessa.stpOXtfarcrelgae.wodniw")), 0, 512)) { + int assetsEPKStart = j + 36; + int assetsEPKEnd = script.indexOf("];", assetsEPKStart); + if(assetsEPKEnd == -1) { + logger.error("Could not find where assets.epk ends!"); + return null; + } + assetsEPKEnd += 1; + String assetsEPKs = script.substring(assetsEPKStart, assetsEPKEnd); + assetsEPKs = assetsEPKs.replace("url:", "\"url\":"); + assetsEPKs = assetsEPKs.replace("path:", "\"path\":"); + try { + JSONArray epksJSON = new JSONArray(assetsEPKs); + for(int ii = 0, ll = epksJSON.length(); ii < ll; ++ii) { + JSONObject obj = epksJSON.getJSONObject(ii); + String path = obj.optString("path", ""); + String url = obj.getString("url"); + if(!url.startsWith("data:application/octet-stream;base64,")) { + logger.error("assetsURI is not base64!"); + return null; + } + byte[] binary = Base64.decodeBase64(url.substring(37)); + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(binary); + blobs.put(assetsEPKUUID, binary); + epks.add(new EPKDataEntry(path, assetsEPKUUID)); + } + }catch(JSONException | IllegalStateException | IllegalArgumentException ex) { + logger.error("assetsURI is not valid json!"); + return null; + } + }else if(foundWithin(j = script.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = IRUstessa.stpOXtfarcrelgae.wodniw")), 0, 512)) { + int assetsEPKStart = j + 74; + int assetsEPKEnd = script.indexOf("\";", assetsEPKStart); + if(assetsEPKEnd == -1) { + logger.error("Could not find where assets.epk ends!"); + return null; + } + byte[] assetsEPK; + try { + assetsEPK = Base64.decodeBase64(script.substring(assetsEPKStart, assetsEPKEnd)); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("assets.epk is not valid base64!"); + return null; + } + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(assetsEPK); + blobs.put(assetsEPKUUID, assetsEPK); + epks.add(new EPKDataEntry("", assetsEPKUUID)); + } + } + logger.info("Successfully loaded classes.js {} and assets.epk {}", classesJSUUID, String.join(", ", Lists.transform(epks, (e) -> e.dataUUID.toString()))); + if(launchConf == null) { + return Arrays.asList(new ParsedOfflineAdapter(EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, epks), blobs)); + }else { + return Arrays.asList(new ParsedOfflineAdapter(launchConf, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, epks), blobs)); + } + } + + private static List parseOfflineEaglerX18PreU20(LaunchConfigEntry launchConf, String script) { + int classesJSStart = script.indexOf(StringUtils.reverse(";dees_tr$=x rav{)(dItxen_tr$ noitcnuf;2424353642=dees_tr$ rav\n{)(noitcnuf(;niam rav")); + if(classesJSStart == -1 || classesJSStart > 32767) { + logger.error("Could not find where classes.js begins!"); + return null; + } + boolean isInternational = false; + int classesJSEnd = script.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = IRUstessa.stpOXtfarcrelgae.wodniw")); + if(classesJSEnd == -1) { + classesJSEnd = script.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" :lru { [ = IRUstessa.stpOXtfarcrelgae.wodniw")); + if(classesJSEnd == -1) { + logger.error("Could not find where classes.js ends!"); + return null; + }else { + isInternational = true; + } + } + byte[] classesJSSrc = script.substring(classesJSStart, classesJSEnd).trim().getBytes(StandardCharsets.UTF_8); + EaglercraftUUID classesJSUUID = EaglercraftUUID.nameUUIDFromBytes(classesJSSrc); + Map blobs = new HashMap<>(); + List epks = new ArrayList<>(2); + blobs.put(classesJSUUID, classesJSSrc); + if(isInternational) { + int assetsEPKStart = classesJSEnd + 36; + int assetsEPKEnd = script.indexOf("];", assetsEPKStart); + if(assetsEPKEnd == -1) { + logger.error("Could not find where assets.epk ends!"); + return null; + } + assetsEPKEnd += 1; + String assetsEPKs = script.substring(assetsEPKStart, assetsEPKEnd); + assetsEPKs = assetsEPKs.replace("url:", "\"url\":"); + assetsEPKs = assetsEPKs.replace("path:", "\"path\":"); + try { + JSONArray epksJSON = new JSONArray(assetsEPKs); + for(int i = 0, l = epksJSON.length(); i < l; ++i) { + JSONObject obj = epksJSON.getJSONObject(i); + String path = obj.optString("path", ""); + String url = obj.getString("url"); + if(!url.startsWith("data:application/octet-stream;base64,")) { + logger.error("assetsURI is not base64!"); + return null; + } + byte[] binary = Base64.decodeBase64(url.substring(37)); + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(binary); + blobs.put(assetsEPKUUID, binary); + epks.add(new EPKDataEntry(path, assetsEPKUUID)); + } + }catch(JSONException | IllegalStateException | IllegalArgumentException ex) { + logger.error("assetsURI is not valid json!"); + return null; + } + }else { + int assetsEPKStart = classesJSEnd + 74; + int assetsEPKEnd = script.indexOf("\";", assetsEPKStart); + if(assetsEPKEnd == -1) { + logger.error("Could not find where assets.epk ends!"); + return null; + } + byte[] assetsEPK; + try { + assetsEPK = Base64.decodeBase64(script.substring(assetsEPKStart, assetsEPKEnd)); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("assets.epk is not valid base64!"); + return null; + } + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(assetsEPK); + blobs.put(assetsEPKUUID, assetsEPK); + epks.add(new EPKDataEntry("", assetsEPKUUID)); + } + logger.info("Successfully loaded classes.js {} and assets.epk {}", classesJSUUID, String.join(", ", Lists.transform(epks, (e) -> e.dataUUID.toString()))); + if(launchConf == null) { + return Arrays.asList(new ParsedOfflineAdapter(EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, epks), blobs)); + }else { + return Arrays.asList(new ParsedOfflineAdapter(launchConf, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, epks), blobs)); + } + } + + private static List parseOfflineEaglerX18Signed(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFTX_1_8_SIGNED"); + return parseOfflineEaglerX18Signed(EnumOfflineParseType.EAGLERCRAFTX_1_8_SIGNED, offlineDownloadData); + } + + private static List parseOfflineEaglerX18Signed(EnumOfflineParseType expectType, String offlineDownloadData) { + LaunchConfigEntry launchConf = null; + try { + launchConf = tryReadParseHint(expectType, offlineDownloadData); + }catch(IllegalStateException ex) { + logger.error(ex.getMessage()); + return null; + } + int signatureStart = offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad>\"erutangiStneilCXtfarcrelgae\"=di \"tfarcrelgae\"=epyt elyts<")); + boolean isPreU24 = false; + if(signatureStart == -1) { + signatureStart = offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = erutangiStneilCXtfarcrelgae.wodniw")); + if(signatureStart == -1) { + logger.error("Could not find client signature!"); + return null; + } + isPreU24 = true; + signatureStart += 75; + logger.info("Client is a pre-u24 signed offline"); + }else { + signatureStart += 96; + } + Map blobs = new HashMap<>(); + EaglercraftUUID signatureUUID; + EaglercraftUUID payloadUUID; + if(!isPreU24) { + int signatureEnd = offlineDownloadData.indexOf(StringUtils.reverse(">elyts/<"), signatureStart); + if(signatureEnd == -1) { + logger.error("Could not find end of client signature!"); + return null; + } + byte[] signature; + try { + signature = Base64.decodeBase64(offlineDownloadData.substring(signatureStart, signatureEnd)); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("Client signature is not valid base64!"); + return null; + } + int bundleStart = offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad>\"eldnuBtneilCXtfarcrelgae\"=di \"tfarcrelgae\"=epyt elyts<"), signatureStart); + if(bundleStart == -1) { + logger.error("Could not find client payload!"); + return null; + } + bundleStart += 93; + int bundleEnd = offlineDownloadData.indexOf(StringUtils.reverse(">elyts/<"), bundleStart); + if(bundleEnd == -1) { + logger.error("Could not find end of client payload!"); + return null; + } + byte[] payload; + try { + payload = Base64.decodeBase64(offlineDownloadData.substring(bundleStart, bundleEnd)); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("Client payload is not valid base64!"); + return null; + } + signatureUUID = EaglercraftUUID.nameUUIDFromBytes(signature); + blobs.put(signatureUUID, signature); + payloadUUID = EaglercraftUUID.nameUUIDFromBytes(payload); + blobs.put(payloadUUID, payload); + }else { + int signatureEnd = offlineDownloadData.indexOf(StringUtils.reverse(";\""), signatureStart); + if(signatureEnd == -1) { + logger.error("Could not find end of client signature!"); + return null; + } + byte[] signature; + try { + signature = Base64.decodeBase64(offlineDownloadData.substring(signatureStart, signatureEnd)); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("Client signature is not valid base64!"); + return null; + } + int bundleStart = offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = eldnuBtneilCXtfarcrelgae.wodniw"), signatureStart); + if(bundleStart == -1) { + logger.error("Could not find client payload!"); + return null; + } + bundleStart += 72; + int bundleEnd = offlineDownloadData.indexOf(StringUtils.reverse(";\""), bundleStart); + if(bundleEnd == -1) { + logger.error("Could not find end of client payload!"); + return null; + } + byte[] payload; + try { + payload = Base64.decodeBase64(offlineDownloadData.substring(bundleStart, bundleEnd)); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("Client payload is not valid base64!"); + return null; + } + signatureUUID = EaglercraftUUID.nameUUIDFromBytes(signature); + blobs.put(signatureUUID, signature); + payloadUUID = EaglercraftUUID.nameUUIDFromBytes(payload); + blobs.put(payloadUUID, payload); + } + logger.info("Successfully loaded signature {} and payload {}", signatureUUID, payloadUUID); + if(launchConf == null) { + return Arrays.asList(new ParsedOfflineAdapter(EnumOfflineParseType.EAGLERCRAFTX_1_8_SIGNED, + new ClientDataEntry(EnumClientFormatType.EAGLER_SIGNED_OFFLINE, EaglercraftUUID.randomUUID(), + payloadUUID, null, signatureUUID, null), blobs)); + }else { + return Arrays.asList(new ParsedOfflineAdapter(launchConf, + new ClientDataEntry(EnumClientFormatType.EAGLER_SIGNED_OFFLINE, EaglercraftUUID.randomUUID(), + payloadUUID, null, signatureUUID, null), blobs)); + } + } + + private static List parseOfflineEaglerX18Fat(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFTX_1_8_FAT_OFFLINE"); + List lst = parseOfflineEaglerX18(EnumOfflineParseType.EAGLERCRAFTX_1_8_FAT_OFFLINE, offlineDownloadData); + if(lst == null || lst.size() != 1) { + logger.error("Fat client type is not EAGLERCRAFTX_1_8_FAT_OFFLINE"); + return null; + } + List lst2 = parseOfflineEaglerX18FatEmbeddedClients(lst.get(0), offlineDownloadData); + if(lst2 == null) { + logger.error("Could not parse embedded clients!"); + return null; + } + return lst2; + } + + private static List parseOfflineEaglerX18FatSigned(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFTX_1_8_FAT_SIGNED"); + List lst = parseOfflineEaglerX18Signed(EnumOfflineParseType.EAGLERCRAFTX_1_8_FAT_SIGNED, offlineDownloadData); + if(lst == null || lst.size() != 1) { + logger.error("Fat client type is not EAGLERCRAFTX_1_8_FAT_OFFLINE"); + return null; + } + List lst2 = parseOfflineEaglerX18FatEmbeddedClients(lst.get(0), offlineDownloadData); + if(lst2 == null) { + logger.error("Could not parse embedded clients!"); + return null; + } + return lst2; + } + + private static List parseOfflineEaglerX18FatEmbeddedClients(ParsedOfflineAdapter parentClient, String offlineDownloadData) { + logger.info("Attempting to parse embedded clients"); + String magicStart = StringUtils.reverse("_enilffOtaFrelgae_\"=di \"tfarcrelgae\"=epyt elyts<"); + String magicEnd = StringUtils.reverse("\n>elyts/<"); + Map fatClientData = new HashMap<>(); + int i = 0, j, k, l; + for(;;) { + if((i = offlineDownloadData.indexOf(magicStart, i)) == -1) { + break; + } + i += 48; + if((j = offlineDownloadData.indexOf("\">", i)) == -1 && j - i < 64) { + break; + } + String name = offlineDownloadData.substring(i, j); + if((k = offlineDownloadData.indexOf(magicEnd, j + 2)) == -1) { + break; + } + fatClientData.put(name, offlineDownloadData.substring(j + 2, k)); + } + String manifest = fatClientData.get("manifest_v1"); + if(manifest == null) { + logger.error("Could not find manifest tag!"); + } + Map clientDatas; + List launchDatas; + try { + JSONObject obj = new JSONObject(manifest); + JSONArray manifestClientDatas = obj.getJSONArray("clientData"); + int n = manifestClientDatas.length(); + clientDatas = new HashMap<>(n + 1); + clientDatas.put(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN, parentClient.clientData.rotateUUID(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN)); + for(l = 0; l < n; ++l) { + JSONObject obj2 = manifestClientDatas.getJSONObject(l); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(obj2.getString("uuid")); + clientDatas.put(theUUID, new ClientDataEntry(theUUID, obj2)); + } + JSONArray manifestLaunchDatas = obj.getJSONArray("launchData"); + n = manifestLaunchDatas.length(); + launchDatas = new ArrayList<>(n + 1); + launchDatas.add(parentClient.launchData.rotateUUIDs(BootMenuConstants.UUID_CLIENT_LAUNCH_ORIGIN, BootMenuConstants.UUID_CLIENT_DATA_ORIGIN)); + for(l = 0; l < n; ++l) { + JSONObject obj2 = manifestLaunchDatas.getJSONObject(l); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(obj2.getString("uuid")); + launchDatas.add(new LaunchConfigEntry(theUUID, obj2)); + } + + }catch(JSONException ex) { + logger.error("The manifest JSON is corrupt!"); + logger.error(ex); + return null; + } + Map blobs = new HashMap<>(); + Iterator itr = clientDatas.values().iterator(); + loadClientDatas: while(itr.hasNext()) { + ClientDataEntry etr = itr.next(); + for(EaglercraftUUID uuid : etr.getReferencedBlobs()) { + if(!blobs.containsKey(uuid)) { + String str = fatClientData.get(uuid.toString()); + if(str == null) { + logger.error("Blob UUID {} for client data {} is missing!", uuid, etr.uuid); + itr.remove(); + continue loadClientDatas; + } + byte[] blobBytes; + try { + blobBytes = Base64.decodeBase64(str); + }catch(IllegalArgumentException | IllegalStateException ex) { + logger.error("Blob UUID {} for client data {} is invalid base64!", uuid, etr.uuid); + itr.remove(); + continue loadClientDatas; + } + if(!EaglercraftUUID.nameUUIDFromBytes(blobBytes).equals(uuid)) { + logger.error("Blob UUID {} for client data {} has an invalid checksum!", uuid, etr.uuid); + itr.remove(); + continue loadClientDatas; + } + blobs.put(uuid, blobBytes); + } + } + } + List list = new ArrayList<>(launchDatas.size()); + for(LaunchConfigEntry etr : launchDatas) { + EaglercraftUUID clientDataUUID = etr.clientDataUUID; + if(clientDataUUID.equals(BootMenuConstants.UUID_CLIENT_DATA_ORIGIN)) { + list.add(new ParsedOfflineAdapter(etr, parentClient.clientData, parentClient.blobs)); + }else { + ClientDataEntry clientData = clientDatas.get(clientDataUUID); + if(clientData == null) { + logger.error("Client data UUID {} for launch data {} is missing!", clientDataUUID, etr.uuid); + continue; + } + Map entryBlob = new HashMap<>(); + for(EaglercraftUUID uuid : clientData.getReferencedBlobs()) { + entryBlob.put(uuid, blobs.get(uuid)); + } + list.add(new ParsedOfflineAdapter(etr, clientData, entryBlob)); + } + } + logger.info("Loaded {} blobs from fat offline", blobs.size()); + logger.info("Loaded {} client configurations from fat offline", clientDatas.size()); + logger.info("Loaded {} launch configurations from fat offline", launchDatas.size()); + return list; + } + + private static List parseOfflineEagler15New(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFTX_1_5_NEW_OFFLINE"); + LaunchConfigEntry launchConf = null; + try { + launchConf = tryReadParseHint(EnumOfflineParseType.EAGLERCRAFT_1_5_NEW_OFFLINE, offlineDownloadData); + }catch(IllegalStateException ex) { + logger.error(ex.getMessage()); + return null; + } + byte[] assetsEPK = null; + byte[] classesJS = null; + List scripts = (new TagIsolator(StringUtils.reverse(">\"tpircsavaj/txet\"=epyt tpircs<"), StringUtils.reverse(">tpircs/<"), offlineDownloadData)).getAllTags(); + for(String str : scripts) { + if(str.length() > 262144) { + int i = -1; + int j = -1; + i = str.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" nruter\t\n{ )(IRUstessAteg noitcnuf")); + if(i == -1 || i >= 1024) { + i = str.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" nruter\n{ )(IRUstessAteg noitcnuf")); + if(i != -1 && i < 1024) { + i += 71; + }else { + i = -1; + } + }else { + i += 72; + } + if(i != -1) { + j = str.indexOf("\";", i); + if(j != -1) { + if(assetsEPK == null) { + try { + assetsEPK = Base64.decodeBase64(str.substring(i, j)); + }catch(IllegalStateException | IllegalArgumentException ex) { + } + } + if(assetsEPK != null) { + continue; + } + }else { + j = -1; + } + } + i = -1; + j = -1; + if(foundWithin(str.indexOf(StringUtils.reverse("{ )le(IRUrekroWetaerc noitcnuf")), 0, 512)) { + continue; + } + if(foundWithin(str.indexOf(StringUtils.reverse(";)0001 ,} ;\")4 ni hcnual lliw emaG(\" = txeTrenni.c {)(noitcnuf(tuoemiTtes")), 0, 512)) { + continue; + } + if(foundWithin(str.indexOf(StringUtils.reverse("{ )(noitcnuf ,\"daol\"(renetsiLtnevEdda.wodniw")), 0, 512)) { + continue; + } + if(classesJS == null) { + classesJS = str.trim().getBytes(StandardCharsets.UTF_8); + } + if(assetsEPK != null && classesJS != null) { + break; + } + } + } + if(classesJS == null) { + logger.error("Could not find classes.js!"); + return null; + } + if(assetsEPK == null) { + logger.error("Could not find assets.epk!"); + return null; + } + int workerIdx = offlineDownloadData.indexOf(StringUtils.reverse(">\"rekrow_ps\"=di \"rekrowrelgae/txet\"=epyt tpircs<")); + if(workerIdx == -1) { + logger.error("Could not find start of integrated server!"); + return null; + }else { + workerIdx += 48; + } + int workerIdxEnd = offlineDownloadData.indexOf(StringUtils.reverse(">tpircs/<"), workerIdx); + if(workerIdxEnd == -1) { + logger.error("Could not find end of integrated server!"); + return null; + } + byte[] classesServerJS = offlineDownloadData.substring(workerIdx, workerIdxEnd).trim().getBytes(StandardCharsets.UTF_8); + Map blobs = new HashMap<>(); + EaglercraftUUID classesJSUUID = EaglercraftUUID.nameUUIDFromBytes(classesJS); + blobs.put(classesJSUUID, classesJS); + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(assetsEPK); + blobs.put(assetsEPKUUID, assetsEPK); + EaglercraftUUID classesServerJSUUID = EaglercraftUUID.nameUUIDFromBytes(classesServerJS); + blobs.put(classesServerJSUUID, classesServerJS); + logger.info("Successfully loaded classes.js {}, classes_server.js {}, and assets.epk {}", classesJSUUID, classesServerJS, assetsEPKUUID); + if(launchConf == null) { + return Arrays.asList(new ParsedOfflineAdapter(EnumOfflineParseType.EAGLERCRAFT_1_5_NEW_OFFLINE, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_1_5_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, classesServerJSUUID, null, Arrays.asList(new EPKDataEntry("", assetsEPKUUID))), blobs)); + }else { + return Arrays.asList(new ParsedOfflineAdapter(launchConf, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_1_5_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, classesServerJSUUID, null, Arrays.asList(new EPKDataEntry("", assetsEPKUUID))), blobs)); + } + } + + private static List parseOfflineEagler15Old(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFTX_1_5_OLD_OFFLINE"); + return parseOfflineEagler15OldImpl(EnumOfflineParseType.EAGLERCRAFT_1_5_OLD_OFFLINE, offlineDownloadData); + } + + private static List parseOfflineEagler15OldImpl(EnumOfflineParseType parseType, String offlineDownloadData) { + LaunchConfigEntry launchConf = null; + try { + launchConf = tryReadParseHint(parseType, offlineDownloadData); + }catch(IllegalStateException ex) { + logger.error(ex.getMessage()); + return null; + } + byte[] assetsEPK = null; + byte[] classesJS = null; + List scripts = (new TagIsolator(StringUtils.reverse(">\"tpircsavaj/txet\"=epyt tpircs<"), StringUtils.reverse(">tpircs/<"), offlineDownloadData)).getAllTags(); + for(String str : scripts) { + if(str.length() > 262144) { + if(foundWithin(str.indexOf(StringUtils.reverse("{ )le(IRUtessAetaerc noitcnuf")), 0, 512)) { + continue; + } + if(foundWithin(str.indexOf(StringUtils.reverse(";)0001 ,} ;\")4 ni hcnual lliw emaG(\" = txeTrenni.c {)(noitcnuf(tuoemiTtes")), 0, 512)) { + continue; + } + if(foundWithin(str.indexOf(StringUtils.reverse("{ )(noitcnuf ,\"daol\"(renetsiLtnevEdda.wodniw")), 0, 512)) { + continue; + } + if(assetsEPK == null) { + int i = str.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" nruter\t\n{ )(IRUstessAteg noitcnuf")); + if(i == -1 || i >= 1024) { + i = str.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" nruter\n{ )(IRUstessAteg noitcnuf")); + if(i != -1 && i < 1024) { + i += 71; + }else { + i = -1; + } + }else { + i += 72; + } + if(i != -1) { + int j = str.indexOf("\";", i); + if(j != -1) { + if(assetsEPK == null) { + try { + assetsEPK = Base64.decodeBase64(str.substring(i, j)); + }catch(IllegalStateException | IllegalArgumentException ex) { + } + } + if(assetsEPK != null) { + continue; + } + } + } + } + if(classesJS == null) { + classesJS = str.trim().getBytes(StandardCharsets.UTF_8); + } + if(assetsEPK != null && classesJS != null) { + break; + } + } + } + if(classesJS == null) { + logger.error("Could not find classes.js!"); + return null; + } + if(assetsEPK == null) { + int epkIdx = offlineDownloadData.indexOf(StringUtils.reverse(">\"stessa\"=di \";enon:yalpsid\"=elyts vid<")); + if(epkIdx == -1) { + logger.error("Could not find start of assets.epk!"); + return null; + }else { + epkIdx += 39; + } + int epkIdxEnd = offlineDownloadData.indexOf(StringUtils.reverse(">vid/<"), epkIdx); + if(epkIdxEnd == -1) { + logger.error("Could not find end of assets.epk!"); + return null; + } + try { + assetsEPK = Base64.decodeBase64(offlineDownloadData.substring(epkIdx, epkIdxEnd).trim()); + }catch(IllegalStateException | IllegalArgumentException ex) { + logger.error("Could not base64 decode assets.epk!"); + return null; + } + } + Map blobs = new HashMap<>(); + EaglercraftUUID classesJSUUID = EaglercraftUUID.nameUUIDFromBytes(classesJS); + blobs.put(classesJSUUID, classesJS); + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(assetsEPK); + blobs.put(assetsEPKUUID, assetsEPK); + logger.info("Successfully loaded classes.js {}, and assets.epk {}", classesJSUUID, assetsEPKUUID); + if(launchConf == null) { + return Arrays.asList(new ParsedOfflineAdapter(parseType, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, Arrays.asList(new EPKDataEntry("", assetsEPKUUID))), blobs)); + }else { + return Arrays.asList(new ParsedOfflineAdapter(launchConf, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, Arrays.asList(new EPKDataEntry("", assetsEPKUUID))), blobs)); + } + } + + private static List parseOfflineEaglerB13(String offlineDownloadData) { + logger.info("Attempting to parse as: EAGLERCRAFT_BETA_B1_3_OFFLINE"); + return parseOfflineEagler15OldImpl(EnumOfflineParseType.EAGLERCRAFT_BETA_B1_3_OFFLINE, offlineDownloadData); + } + + private static List parseOfflinePeytonAlphaBeta(String offlineDownloadData) { + logger.info("Attempting to parse as: PEYTONPLAYZ585_ALPHA_BETA"); + return parseOfflineEagler15OldImpl(EnumOfflineParseType.PEYTONPLAYZ585_ALPHA_BETA, offlineDownloadData); + } + + private static List parseOfflinePeytonIndev(String offlineDownloadData) { + logger.info("Attempting to parse as: PEYTONPLAYZ585_INDEV"); + return parseOfflineEagler15OldImpl(EnumOfflineParseType.PEYTONPLAYZ585_INDEV, offlineDownloadData); + } + + private static List parseStandardOffline(String offlineDownloadData) { + LaunchConfigEntry launchConf; + int hintIndex = offlineDownloadData.indexOf(StringUtils.reverse(">\"tniHesraPenilffOtfarcrelgae\"=epyt elyts<")); + if(hintIndex != -1) { + hintIndex += 42; + int closeTagIndex = offlineDownloadData.indexOf(StringUtils.reverse(">elyts/<"), hintIndex); + if(closeTagIndex != -1) { + try { + JSONObject parseHint = new JSONObject(offlineDownloadData.substring(hintIndex, closeTagIndex)); + EnumOfflineParseType typeEnum = EnumOfflineParseType.valueOf(parseHint.getString("type")); + if(typeEnum != EnumOfflineParseType.EXPORTED_STANDARD_OFFLINE) { + logger.error("This is not a \"EXPORTED_STANDARD_OFFLINE\" type offline!"); + return null; + } + JSONObject launchConfJSON = parseHint.getJSONObject("launchConf"); + EaglercraftUUID theUUID = EaglercraftUUID.fromString(launchConfJSON.getString("uuid")); + launchConf = new LaunchConfigEntry(theUUID, launchConfJSON); + }catch(JSONException | IllegalArgumentException ex) { + logger.error("This offline download has a parse hint section, but the JSON is corrupt!"); + logger.error(ex); + return null; + } + }else { + logger.error("Could not find parse hint section!"); + return null; + } + }else { + logger.error("Could not find parse hint section!"); + return null; + } + List scripts = (new TagIsolator(StringUtils.reverse(">\"tpircsavaj/txet\"=epyt tpircs<"), StringUtils.reverse(">tpircs/<"), offlineDownloadData)).getAllTags(); + if(scripts.size() != 3) { + logger.error("Wrong number of script tags!"); + return null; + } + byte[] classesJSSrc = OfflineDownloadFactory.removeClientScriptElement(scripts.get(1).getBytes(StandardCharsets.UTF_8), true); + EaglercraftUUID classesJSUUID = EaglercraftUUID.nameUUIDFromBytes(classesJSSrc); + Map blobs = new HashMap<>(); + List epks = new ArrayList<>(2); + blobs.put(classesJSUUID, classesJSSrc); + String script = scripts.get(2); + int j; + if(foundWithin(j = script.indexOf(StringUtils.reverse("/*}:KPE_STESSA_NIGEB:{*/")), 0, 512)) { + int assetsEPKStart = j + 24; + int assetsEPKEnd = script.indexOf(StringUtils.reverse("/*}:KPE_STESSA_DNE:{*/"), assetsEPKStart); + if(assetsEPKEnd == -1) { + logger.error("Could not find where assets.epk ends!"); + return null; + } + String assetsEPKs = script.substring(assetsEPKStart, assetsEPKEnd); + try { + JSONArray epksJSON = new JSONArray(assetsEPKs); + for(int ii = 0, ll = epksJSON.length(); ii < ll; ++ii) { + JSONObject obj = epksJSON.getJSONObject(ii); + String path = obj.optString("path", ""); + String url = obj.getString("url"); + if(!url.startsWith("data:application/octet-stream;base64,")) { + logger.error("assetsURI is not base64!"); + return null; + } + byte[] binary = Base64.decodeBase64(url.substring(37)); + EaglercraftUUID assetsEPKUUID = EaglercraftUUID.nameUUIDFromBytes(binary); + blobs.put(assetsEPKUUID, binary); + epks.add(new EPKDataEntry(path, assetsEPKUUID)); + } + }catch(JSONException | IllegalStateException | IllegalArgumentException ex) { + logger.error("assetsURI is not valid json!"); + return null; + } + } + logger.info("Successfully loaded classes.js {} and assets.epk {}", classesJSUUID, String.join(", ", Lists.transform(epks, (e) -> e.dataUUID.toString()))); + return Arrays.asList(new ParsedOfflineAdapter(launchConf, + new ClientDataEntry(EnumClientFormatType.EAGLER_STANDARD_OFFLINE, EaglercraftUUID.randomUUID(), + classesJSUUID, null, null, epks), blobs)); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/RelayRandomizeHelper.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/RelayRandomizeHelper.java new file mode 100755 index 0000000..e053026 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/RelayRandomizeHelper.java @@ -0,0 +1,71 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import org.json.JSONArray; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RelayRandomizeHelper { + + public static int countRelayMacro(String launchOpts) { + int i = 0; + while(launchOpts.contains("\"$random_relay_primary_" + i + "\"")) { + ++i; + } + return i; + } + + public static String replaceRelayMacroWithConstant(String launchOpts) { + int i = countRelayMacro(launchOpts); + if(i == 0) { + return launchOpts; + } + int randomRelay = ThreadLocalRandom.current().nextInt(i); + for(int j = 0; j < i; ++j) { + launchOpts = launchOpts.replace("\"$random_relay_primary_" + j + "\"", randomRelay == j ? "true" : "false"); + } + return launchOpts; + } + + public static String replaceRelayMacroWithEqRelayId(String launchOpts) { + int i = countRelayMacro(launchOpts); + if(i == 0) { + return launchOpts; + } + for(int j = 0; j < i; ++j) { + launchOpts = launchOpts.replace("\"$random_relay_primary_" + j + "\"", "relayId === " + j); + } + return launchOpts; + } + + public static void makeOptsJSONHaveMacroHack(JSONObject optsDump) { + int i = 0; + JSONArray arr = optsDump.optJSONArray("relays"); + if(arr != null) { + for(int j = 0, l = arr.length(); j < l; ++j) { + JSONObject relay = arr.optJSONObject(j); + if(relay != null) { + if(relay.has("primary")) { + relay.put("primary", "$random_relay_primary_" + i++); + } + } + } + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SelectionListController.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SelectionListController.java new file mode 100755 index 0000000..0c42f4f --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SelectionListController.java @@ -0,0 +1,240 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.teavm.jso.dom.html.HTMLElement; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class SelectionListController { + + public static interface ListItem { + + String getName(); + + default boolean getEnabled() { + return true; + } + + default boolean getAlwaysSelected() { + return true; + } + + } + + public static class ListItemEnum implements ListItem { + + protected final String displayName; + protected final boolean enabled; + protected final E itemEnum; + + public ListItemEnum(String displayName, boolean enabled, E itemEnum) { + this.displayName = displayName; + this.enabled = enabled; + this.itemEnum = itemEnum; + } + + public ListItemEnum(String displayName, E itemEnum) { + this.displayName = displayName; + this.enabled = true; + this.itemEnum = itemEnum; + } + + @Override + public String getName() { + return displayName; + } + + @Override + public boolean getEnabled() { + return enabled; + } + + public E getEnum() { + return itemEnum; + } + + } + + protected static class ListItemInstance { + + protected final E listItem; + protected final HTMLElement element; + protected int index; + protected boolean userVal = false; + + protected ListItemInstance(E listItem, HTMLElement element, int index) { + this.listItem = listItem; + this.element = element; + this.index = index; + } + + } + + protected final HTMLElement parent; + protected final List selectionList; + protected final List> selectionEnableList; + protected int currentSelected = -1; + protected boolean cursorEventsSuspended = false; + + public SelectionListController(HTMLElement parent, List selectionList) { + this.parent = parent; + this.selectionList = selectionList; + this.selectionEnableList = new ArrayList<>(selectionList.size()); + } + + public void setup() { + selectionEnableList.clear(); + parent.setInnerHTML(""); + currentSelected = -1; + for(int i = 0, l = selectionList.size(); i < l; ++i) { + T itm = selectionList.get(i); + HTMLElement el = BootMenuMain.doc.createElement("p"); + el.setInnerText(itm.getName()); + el.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "content_item"); + if(itm.getEnabled()) { + if(currentSelected == -1) { + currentSelected = 0; + el.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "content_item_selected"); + } + final int ii = selectionEnableList.size(); + final ListItemInstance newInstance = new ListItemInstance(itm, el, ii); + el.addEventListener("mouseover", (evt) -> { + BootMenuMain.runLater(() -> { + if(!cursorEventsSuspended) { + setSelected(newInstance.index); + } + }); + }); + el.addEventListener("click", (evt) -> { + BootMenuMain.runLater(() -> { + if(!cursorEventsSuspended) { + itemSelectedLow(newInstance); + } + }); + }); + selectionEnableList.add(newInstance); + }else { + el.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "content_item_disabled"); + } + parent.appendChild(el); + } + } + + public void destroy() { + parent.setInnerHTML(""); + currentSelected = -1; + selectionEnableList.clear(); + } + + public void setSelected(int idx) { + int listLen = selectionEnableList.size(); + if(listLen == 0) { + idx = -1; + }else if(idx >= listLen) { + idx = listLen - 1; + }else if(idx < 0) { + idx = 0; + } + if(idx == currentSelected) { + return; + } + if(currentSelected >= 0 && currentSelected < selectionEnableList.size()) { + selectionEnableList.get(currentSelected).element.getClassList().remove(BootMenuConstants.cssClassPrefixBootMenu + "content_item_selected"); + } + currentSelected = idx; + if(idx != -1) { + selectionEnableList.get(idx).element.getClassList().add(BootMenuConstants.cssClassPrefixBootMenu + "content_item_selected"); + } + } + + public T getSelected() { + if(currentSelected >= 0 && currentSelected < selectionEnableList.size()) { + return selectionEnableList.get(currentSelected).listItem; + }else { + return null; + } + } + + public void moveEntryUp(int index) { + if(index < 1 || index >= selectionEnableList.size()) return; + if(currentSelected == index) { + --currentSelected; + } + ListItemInstance etr = selectionEnableList.get(index); + ListItemInstance etr2 = selectionEnableList.get(index - 1); + Collections.swap(selectionEnableList, index, index - 1); + etr.index--; + etr2.index++; + parent.removeChild(etr.element); + parent.insertBefore(etr.element, etr2.element); + } + + public void moveEntryDown(int index) { + if(index < 0 || index >= selectionEnableList.size() - 1) return; + if(currentSelected == index) { + ++currentSelected; + } + ListItemInstance etr = selectionEnableList.get(index); + Collections.swap(selectionEnableList, index, index + 1); + etr.index++; + selectionEnableList.get(index + 1).index--; + parent.removeChild(etr.element); + if(index >= selectionEnableList.size() - 2) { + parent.appendChild(etr.element); + }else { + ListItemInstance etr2 = selectionEnableList.get(index + 2); + parent.insertBefore(etr.element, etr2.element); + } + } + + public void handleKeyDown(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ARROW_UP) { + setSelected(currentSelected - 1); + }else if(keyCode == KeyCodes.DOM_KEY_ARROW_DOWN) { + setSelected(currentSelected + 1); + }else if(keyCode == KeyCodes.DOM_KEY_ENTER) { + fireSelect(); + } + } + + protected void fireSelect() { + if(currentSelected >= 0 && currentSelected < selectionEnableList.size()) { + itemSelectedLow(selectionEnableList.get(currentSelected)); + } + } + + public void handleKeyRepeat(int keyCode) { + if(keyCode == KeyCodes.DOM_KEY_ARROW_UP) { + setSelected(currentSelected - 1); + }else if(keyCode == KeyCodes.DOM_KEY_ARROW_DOWN) { + setSelected(currentSelected + 1); + } + } + + public void setCursorEventsSuspended(boolean sus) { + cursorEventsSuspended = sus; + } + + protected void itemSelectedLow(ListItemInstance item) { + itemSelected(item.listItem); + } + + protected abstract void itemSelected(T item); + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SignatureCheckHelper.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SignatureCheckHelper.java new file mode 100755 index 0000000..0b09519 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SignatureCheckHelper.java @@ -0,0 +1,48 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.update.CertificateInvalidException; +import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SignatureCheckHelper { + + private static final Logger logger = LogManager.getLogger("SignatureCheckHelper"); + + public static boolean checkSignatureValid(byte[] signatureBytes, byte[] payloadBytes) { + UpdateCertificate cert; + try { + cert = UpdateCertificate.parseAndVerifyCertificate(signatureBytes); + } catch (CertificateInvalidException | IOException e) { + logger.error("The client's signature is invalid because the update certificate is bad"); + logger.error(e); + return false; + } + if(!cert.isBundleDataValid(payloadBytes)) { + logger.error("The client's signature is invalid because the payload checksum does not match the expected checksum in the update certificate"); + logger.error("(Update certificate client name and version: {} - {})", cert.bundleDisplayName, cert.bundleDisplayVersion); + return false; + }else { + logger.info("Signature is valid: {} - {}", cert.bundleDisplayName, cert.bundleDisplayVersion); + return true; + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SignedClientInstaller.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SignedClientInstaller.java new file mode 100755 index 0000000..3b16785 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/SignedClientInstaller.java @@ -0,0 +1,77 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; +import org.teavm.jso.browser.Window; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SignedClientInstaller { + + private static final Logger logger = LogManager.getLogger("SignedClientInstaller"); + + public static void installSignedClientAtRuntime(String displayName, Window win, byte[] clientCert, + byte[] clientPayload, boolean setDefault, boolean setTimeout) { + logger.info("Enabling boot menu..."); + int f = BootMenuDataManager.getBootMenuFlags(win); + if(f == -1) f = 0; + f |= 1; + BootMenuDataManager.setBootMenuFlags(win, f); + logger.info("Loading datastore..."); + BootMenuDatastore dstore = BootMenuDatastore.openDatastore(); + try { + logger.info("Loading manifest..."); + BootMenuDataManager dmgr = new BootMenuDataManager(dstore); + logger.info("Generating client data..."); + EaglercraftUUID certUUID = EaglercraftUUID.nameUUIDFromBytes(clientCert); + EaglercraftUUID payloadUUID = EaglercraftUUID.nameUUIDFromBytes(clientPayload); + Map blobs = new HashMap<>(2); + blobs.put(certUUID, clientCert); + blobs.put(payloadUUID, clientPayload); + ClientDataEntry clientData = new ClientDataEntry(EnumClientFormatType.EAGLER_SIGNED_OFFLINE, + EaglercraftUUID.randomUUID(), payloadUUID, null, certUUID, null); + JSONObject launchOptsJSON = BootMenuEntryPoint.getOriginLaunchOptsJSON(); + launchOptsJSON.put("bootMenuBlocksUnsignedClients", false); + RelayRandomizeHelper.makeOptsJSONHaveMacroHack(launchOptsJSON); + String launchOpts = launchOptsJSON.toString(4); + LaunchConfigEntry launchData = new LaunchConfigEntry(EaglercraftUUID.randomUUID(), clientData.uuid, displayName, + EnumClientLaunchType.EAGLERX_SIGNED_V1, null, null, null, null, null, launchOpts, false); + logger.info("Installing client data..."); + dmgr.installNewClientData(launchData, clientData, blobs, false); + if(setDefault) { + logger.info("Setting boot order..."); + while(dmgr.launchOrderList.remove(launchData.uuid)); + dmgr.launchOrderList.add(0, launchData.uuid); + dmgr.writeManifest(); + if(setTimeout) { + logger.info("Setting boot timeout..."); + dmgr.confBootTimeout = 5; + dmgr.saveAdditionalConf(); + } + } + }finally { + logger.info("Cleaning up..."); + dstore.closeDatastore(); + } + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/TemplateLoader.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/TemplateLoader.java new file mode 100755 index 0000000..1c806ce --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/TemplateLoader.java @@ -0,0 +1,77 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TemplateLoader { + + public static final Map baseGlobals; + + static { + baseGlobals = new HashMap<>(); + baseGlobals.put("client_name", BootMenuConstants.client_projectForkName); + baseGlobals.put("client_vendor", BootMenuConstants.client_projectForkVendor); + baseGlobals.put("client_version", BootMenuConstants.client_projectForkVersion); + baseGlobals.put("game_version", BootMenuConstants.client_projectOriginRevision); + baseGlobals.put("client_fork_name", BootMenuConstants.client_projectForkName); + baseGlobals.put("client_fork_vendor", BootMenuConstants.client_projectForkVendor); + baseGlobals.put("client_fork_version", BootMenuConstants.client_projectForkVersion); + baseGlobals.put("client_origin_name", BootMenuConstants.client_projectOriginName); + baseGlobals.put("client_origin_vendor", BootMenuConstants.client_projectOriginAuthor); + baseGlobals.put("client_origin_version", BootMenuConstants.client_projectOriginVersion); + baseGlobals.put("client_origin_revision", BootMenuConstants.client_projectOriginRevision); + EaglercraftRandom randomCharGenerator = new EaglercraftRandom(); + char[] vigg = new char[16]; + String charSel = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for(int i = 0; i < vigg.length; ++i) { + vigg[i] = charSel.charAt(randomCharGenerator.nextInt(charSel.length())); + } + baseGlobals.put("root_class_gen", BootMenuConstants.cssClassPrefix + (new String(vigg))); + } + + public static String loadTemplate(String path) throws IOException { + return loadTemplate(path, null); + } + + public static String loadTemplate(String path, Map globals) throws IOException { + String basePath; + int i = path.lastIndexOf('/'); + if(i != -1) { + basePath = path.substring(0, i); + }else { + basePath = ""; + } + if(globals != null) { + Map newGlobals = new HashMap<>(); + newGlobals.putAll(baseGlobals); + newGlobals.putAll(globals); + globals = newGlobals; + }else { + globals = baseGlobals; + } + String templateContent = BootMenuAssets.loadResourceString(path); + if(templateContent == null) { + throw new IOException("Could not load template: \"" + path + "\""); + } + return TemplateParser.loadTemplate(templateContent, basePath, true, globals); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/TemplateParser.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/TemplateParser.java new file mode 100755 index 0000000..5738f28 --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/TemplateParser.java @@ -0,0 +1,260 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +import java.io.IOException; +import java.util.Map; + +import org.apache.commons.lang3.text.StrTokenizer; +import org.json.JSONObject; + +import com.google.common.html.HtmlEscapers; + +import net.lax1dude.eaglercraft.v1_8.Base64; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TemplateParser { + + private static class State { + private boolean evalAllowed; + private String baseDir; + private Map globals; + private boolean htmlEscape; + private boolean strEscape; + private boolean disableMacros; + private boolean enableEval; + private State(String baseDir, boolean evalAllowed, Map globals) { + this.baseDir = baseDir; + this.evalAllowed = evalAllowed; + this.globals = globals; + } + private State push() { + return new State(baseDir, evalAllowed, globals); + } + } + + public static String loadTemplate(String content, String baseDir, boolean evalAllowed, Map globals) throws IOException { + return loadTemplate(content, new State(baseDir, evalAllowed, globals)); + } + + private static String loadTemplate(String content, State state) throws IOException { + StringBuilder ret = new StringBuilder(); + int i = 0, j = 0; + while((i = content.indexOf("{%", j)) != -1) { + ret.append(content, j, i); + j = i; + i = content.indexOf("%}", j + 2); + if(i != -1) { + ret.append(processMacro(content.substring(j + 2, i), state)); + j = i + 2; + }else { + break; + } + } + ret.append(content, j, content.length()); + return ret.toString(); + } + + public static class InvalidMacroException extends RuntimeException { + + public InvalidMacroException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidMacroException(String message) { + super(message); + } + + } + + private static String processMacro(String content, State state) throws IOException { + String trimmed = content.trim(); + try { + String[] strs = (new StrTokenizer(trimmed, ' ', '`')).getTokenArray(); + if(strs.length < 1) { + return "{%" + content + "%}"; + } + if(strs[0].equals("disablemacros") && strs.length == 2) { + switch(strs[1]) { + case "on": + if(state.disableMacros) { + return "{%" + content + "%}"; + }else { + state.disableMacros = true; + return ""; + } + case "off": + state.disableMacros = false; + return ""; + default: + if(state.disableMacros) { + return "{%" + content + "%}"; + }else { + throw new InvalidMacroException("Unknown disablemacros mode: " + strs[1] + " (Expected: on, off)"); + } + } + }else if(!state.disableMacros) { + switch(strs[0]) { + case "embed": + argCheck(3, strs.length); + switch(strs[1]) { + case "base64": + return Base64.encodeBase64String(loadResourceBytes(state.baseDir + "/" + strs[2])); + case "text": + return escapeMacroResult(loadResourceString(state.baseDir + "/" + strs[2]), state); + case "eval": + if(state.evalAllowed) { + return escapeMacroResult(loadTemplate(loadResourceString(state.baseDir + "/" + strs[2]), state.push()), state); + }else { + throw new InvalidMacroException("Template tried to eval file \"" + strs[2] + "\"! (eval is disabled)"); + } + default: + throw new InvalidMacroException("Unknown embed mode: " + strs[1] + " (Expected: base64, text, eval)"); + } + case "htmlescape": + argCheck(2, strs.length); + switch(strs[1]) { + case "on": + state.htmlEscape = true; + return ""; + case "off": + state.htmlEscape = false; + return ""; + default: + throw new InvalidMacroException("Unknown htmlescape mode: " + strs[1] + " (Expected: on, off)"); + } + case "strescape": + argCheck(2, strs.length); + switch(strs[1]) { + case "on": + state.strEscape = true; + return ""; + case "off": + state.strEscape = false; + return ""; + default: + throw new InvalidMacroException("Unknown strescape mode: " + strs[1] + " (Expected: on, off)"); + } + case "eval": + argCheck(2, strs.length); + switch(strs[1]) { + case "on": + if(!state.evalAllowed) { + throw new InvalidMacroException("Template tried to enable eval! (eval is disabled)"); + } + state.enableEval = true; + return ""; + case "off": + state.enableEval = false; + return ""; + default: + throw new InvalidMacroException("Unknown eval mode: " + strs[1] + " (Expected: on, off)"); + } + case "global": + argCheck(2, 3, strs.length); + String ret = state.globals.get(strs[1]); + if(ret == null) { + if(strs.length == 3) { + ret = strs[2]; + }else { + throw new InvalidMacroException("Unknown global \"" + strs[1] + "\"! (Available: " + String.join(", ", state.globals.keySet()) + ")"); + } + } + return escapeMacroResult(ret, state); + case "property": + argCheck(2, 3, strs.length); + ret = System.getProperty(strs[1]); + if(ret == null) { + if(strs.length == 3) { + ret = strs[2]; + }else { + throw new InvalidMacroException("Unknown system property \"" + strs[1] + "\"!"); + } + } + return escapeMacroResult(ret, state); + case "text": + argCheck(2, strs.length); + return escapeMacroResult(strs[1], state); +// case "translate": +// argCheckMin(2, strs.length); +// String[] additionalArgs = new String[strs.length - 2]; +// System.arraycopy(strs, 2, additionalArgs, 0, additionalArgs.length); +// return escapeMacroResult(BungeeCord.getInstance().getTranslation(strs[1], (Object[])additionalArgs), state); + default: + return "{%" + content + "%}"; + } + }else { + return "{%" + content + "%}"; + } + }catch(InvalidMacroException ex) { + throw new IOException("Invalid macro: {% " + trimmed + " %}, message: " + ex.getMessage(), ex); + }catch(Throwable th) { + throw new IOException("Error processing: {% " + trimmed + " %}, raised: " + th.toString(), th); + } + } + + private static String escapeMacroResult(String str, State state) throws IOException { + if(str.length() > 0) { + if(state.evalAllowed && state.enableEval) { + str = loadTemplate(str, state.push()); + } + if(state.strEscape) { + str = (new JSONObject()).put("e", str).toString(); //rip + if(str.length() >= 8) { + str = str.substring(6, str.length() - 2); + } + } + if(state.htmlEscape) { + str = HtmlEscapers.htmlEscaper().escape(str); + } + } + return str; + } + + private static void argCheck(int expect, int actual) { + if(expect != actual) { + throw new InvalidMacroException("Wrong number of arguments (" + actual + ", expected " + expect + ")"); + } + } + + private static void argCheck(int expectMin, int expectMax, int actual) { + if(expectMin > actual || expectMax < actual) { + throw new InvalidMacroException("Wrong number of arguments (" + actual + ", expected " + expectMin + " to " + expectMax + ")"); + } + } + + private static void argCheckMin(int expectMin, int actual) { + if(expectMin > actual) { + throw new InvalidMacroException("Wrong number of arguments (expected " + expectMin + " or more, got " + actual + ")"); + } + } + + private static byte[] loadResourceBytes(String path) { + byte[] res = BootMenuAssets.loadResourceBytes(path); + if(res == null) { + throw new InvalidMacroException("Unknown file: " + path); + } + return res; + } + + private static String loadResourceString(String path) { + String res = BootMenuAssets.loadResourceString(path); + if(res == null) { + throw new InvalidMacroException("Unknown file: " + path); + } + return res; + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/UnsignedBootException.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/UnsignedBootException.java new file mode 100755 index 0000000..1c39eac --- /dev/null +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/UnsignedBootException.java @@ -0,0 +1,23 @@ +package net.lax1dude.eaglercraft.v1_8.boot_menu.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class UnsignedBootException extends RuntimeException { + + public UnsignedBootException() { + } + +} diff --git a/src/teavm/java/com/jcraft/jogg/Buffer.java b/src/teavm/java/com/jcraft/jogg/Buffer.java new file mode 100755 index 0000000..dde9158 --- /dev/null +++ b/src/teavm/java/com/jcraft/jogg/Buffer.java @@ -0,0 +1,293 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jogg; + +public class Buffer { + private static final int BUFFER_INCREMENT = 256; + + private static final int[] mask = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, + 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, + 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff }; + + int ptr = 0; + byte[] buffer = null; + int endbit = 0; + int endbyte = 0; + int storage = 0; + + public void writeinit() { + buffer = new byte[BUFFER_INCREMENT]; + ptr = 0; + buffer[0] = (byte) '\0'; + storage = BUFFER_INCREMENT; + } + + public void write(byte[] s) { + for (int i = 0; i < s.length; i++) { + if (s[i] == 0) + break; + write(s[i], 8); + } + } + + public void read(byte[] s, int bytes) { + int i = 0; + while (bytes-- != 0) { + s[i++] = (byte) (read(8)); + } + } + + void reset() { + ptr = 0; + buffer[0] = (byte) '\0'; + endbit = endbyte = 0; + } + + public void writeclear() { + buffer = null; + } + + public void readinit(byte[] buf, int bytes) { + readinit(buf, 0, bytes); + } + + public void readinit(byte[] buf, int start, int bytes) { + ptr = start; + buffer = buf; + endbit = endbyte = 0; + storage = bytes; + } + + public void write(int value, int bits) { + if (endbyte + 4 >= storage) { + byte[] foo = new byte[storage + BUFFER_INCREMENT]; + System.arraycopy(buffer, 0, foo, 0, storage); + buffer = foo; + storage += BUFFER_INCREMENT; + } + + value &= mask[bits]; + bits += endbit; + buffer[ptr] |= (byte) (value << endbit); + + if (bits >= 8) { + buffer[ptr + 1] = (byte) (value >>> (8 - endbit)); + if (bits >= 16) { + buffer[ptr + 2] = (byte) (value >>> (16 - endbit)); + if (bits >= 24) { + buffer[ptr + 3] = (byte) (value >>> (24 - endbit)); + if (bits >= 32) { + if (endbit > 0) + buffer[ptr + 4] = (byte) (value >>> (32 - endbit)); + else + buffer[ptr + 4] = 0; + } + } + } + } + + endbyte += bits / 8; + ptr += bits / 8; + endbit = bits & 7; + } + + public int look(int bits) { + int ret; + int m = mask[bits]; + + bits += endbit; + + if (endbyte + 4 >= storage) { + if (endbyte + (bits - 1) / 8 >= storage) + return (-1); + } + + ret = ((buffer[ptr]) & 0xff) >>> endbit; + if (bits > 8) { + ret |= ((buffer[ptr + 1]) & 0xff) << (8 - endbit); + if (bits > 16) { + ret |= ((buffer[ptr + 2]) & 0xff) << (16 - endbit); + if (bits > 24) { + ret |= ((buffer[ptr + 3]) & 0xff) << (24 - endbit); + if (bits > 32 && endbit != 0) { + ret |= ((buffer[ptr + 4]) & 0xff) << (32 - endbit); + } + } + } + } + return (m & ret); + } + + public int look1() { + if (endbyte >= storage) + return (-1); + return ((buffer[ptr] >> endbit) & 1); + } + + public void adv(int bits) { + bits += endbit; + ptr += bits / 8; + endbyte += bits / 8; + endbit = bits & 7; + } + + public void adv1() { + ++endbit; + if (endbit > 7) { + endbit = 0; + ptr++; + endbyte++; + } + } + + public int read(int bits) { + int ret; + int m = mask[bits]; + + bits += endbit; + + if (endbyte + 4 >= storage) { + ret = -1; + if (endbyte + (bits - 1) / 8 >= storage) { + ptr += bits / 8; + endbyte += bits / 8; + endbit = bits & 7; + return (ret); + } + } + + ret = ((buffer[ptr]) & 0xff) >>> endbit; + if (bits > 8) { + ret |= ((buffer[ptr + 1]) & 0xff) << (8 - endbit); + if (bits > 16) { + ret |= ((buffer[ptr + 2]) & 0xff) << (16 - endbit); + if (bits > 24) { + ret |= ((buffer[ptr + 3]) & 0xff) << (24 - endbit); + if (bits > 32 && endbit != 0) { + ret |= ((buffer[ptr + 4]) & 0xff) << (32 - endbit); + } + } + } + } + + ret &= m; + + ptr += bits / 8; + endbyte += bits / 8; + endbit = bits & 7; + return (ret); + } + + public int readB(int bits) { + int ret; + int m = 32 - bits; + + bits += endbit; + + if (endbyte + 4 >= storage) { + /* not the main path */ + ret = -1; + if (endbyte * 8 + bits > storage * 8) { + ptr += bits / 8; + endbyte += bits / 8; + endbit = bits & 7; + return (ret); + } + } + + ret = (buffer[ptr] & 0xff) << (24 + endbit); + if (bits > 8) { + ret |= (buffer[ptr + 1] & 0xff) << (16 + endbit); + if (bits > 16) { + ret |= (buffer[ptr + 2] & 0xff) << (8 + endbit); + if (bits > 24) { + ret |= (buffer[ptr + 3] & 0xff) << (endbit); + if (bits > 32 && (endbit != 0)) + ret |= (buffer[ptr + 4] & 0xff) >> (8 - endbit); + } + } + } + ret = (ret >>> (m >> 1)) >>> ((m + 1) >> 1); + + ptr += bits / 8; + endbyte += bits / 8; + endbit = bits & 7; + return (ret); + } + + public int read1() { + int ret; + if (endbyte >= storage) { + ret = -1; + endbit++; + if (endbit > 7) { + endbit = 0; + ptr++; + endbyte++; + } + return (ret); + } + + ret = (buffer[ptr] >> endbit) & 1; + + endbit++; + if (endbit > 7) { + endbit = 0; + ptr++; + endbyte++; + } + return (ret); + } + + public int bytes() { + return (endbyte + (endbit + 7) / 8); + } + + public int bits() { + return (endbyte * 8 + endbit); + } + + public byte[] buffer() { + return (buffer); + } + + public static int ilog(int v) { + int ret = 0; + while (v > 0) { + ret++; + v >>>= 1; + } + return (ret); + } + + public static void report(String in) { + System.err.println(in); + System.exit(1); + } +} diff --git a/src/teavm/java/com/jcraft/jogg/Packet.java b/src/teavm/java/com/jcraft/jogg/Packet.java new file mode 100755 index 0000000..d78e6f5 --- /dev/null +++ b/src/teavm/java/com/jcraft/jogg/Packet.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jogg; + +public class Packet { + public byte[] packet_base; + public int packet; + public int bytes; + public int b_o_s; + public int e_o_s; + + public long granulepos; + + /** + * sequence number for decode; the framing knows where there's a hole in the + * data, but we need coupling so that the codec (which is in a seperate + * abstraction layer) also knows about the gap + */ + public long packetno; + +} diff --git a/src/teavm/java/com/jcraft/jogg/Page.java b/src/teavm/java/com/jcraft/jogg/Page.java new file mode 100755 index 0000000..f9d29fe --- /dev/null +++ b/src/teavm/java/com/jcraft/jogg/Page.java @@ -0,0 +1,130 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jogg; + +public class Page { + private static int[] crc_lookup = new int[256]; + static { + for (int i = 0; i < crc_lookup.length; i++) { + crc_lookup[i] = crc_entry(i); + } + } + + private static int crc_entry(int index) { + int r = index << 24; + for (int i = 0; i < 8; i++) { + if ((r & 0x80000000) != 0) { + r = (r << 1) ^ 0x04c11db7; /* + * The same as the ethernet generator polynomial, although we use an + * unreflected alg and an init/final of 0, not 0xffffffff + */ + } else { + r <<= 1; + } + } + return (r & 0xffffffff); + } + + public byte[] header_base; + public int header; + public int header_len; + public byte[] body_base; + public int body; + public int body_len; + + int version() { + return header_base[header + 4] & 0xff; + } + + int continued() { + return (header_base[header + 5] & 0x01); + } + + public int bos() { + return (header_base[header + 5] & 0x02); + } + + public int eos() { + return (header_base[header + 5] & 0x04); + } + + public long granulepos() { + long foo = header_base[header + 13] & 0xff; + foo = (foo << 8) | (header_base[header + 12] & 0xff); + foo = (foo << 8) | (header_base[header + 11] & 0xff); + foo = (foo << 8) | (header_base[header + 10] & 0xff); + foo = (foo << 8) | (header_base[header + 9] & 0xff); + foo = (foo << 8) | (header_base[header + 8] & 0xff); + foo = (foo << 8) | (header_base[header + 7] & 0xff); + foo = (foo << 8) | (header_base[header + 6] & 0xff); + return (foo); + } + + public int serialno() { + return (header_base[header + 14] & 0xff) | ((header_base[header + 15] & 0xff) << 8) + | ((header_base[header + 16] & 0xff) << 16) | ((header_base[header + 17] & 0xff) << 24); + } + + int pageno() { + return (header_base[header + 18] & 0xff) | ((header_base[header + 19] & 0xff) << 8) + | ((header_base[header + 20] & 0xff) << 16) | ((header_base[header + 21] & 0xff) << 24); + } + + void checksum() { + int crc_reg = 0; + + for (int i = 0; i < header_len; i++) { + crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >>> 24) & 0xff) ^ (header_base[header + i] & 0xff)]; + } + for (int i = 0; i < body_len; i++) { + crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >>> 24) & 0xff) ^ (body_base[body + i] & 0xff)]; + } + header_base[header + 22] = (byte) crc_reg; + header_base[header + 23] = (byte) (crc_reg >>> 8); + header_base[header + 24] = (byte) (crc_reg >>> 16); + header_base[header + 25] = (byte) (crc_reg >>> 24); + } + + public Page copy() { + return copy(new Page()); + } + + public Page copy(Page p) { + byte[] tmp = new byte[header_len]; + System.arraycopy(header_base, header, tmp, 0, header_len); + p.header_len = header_len; + p.header_base = tmp; + p.header = 0; + tmp = new byte[body_len]; + System.arraycopy(body_base, body, tmp, 0, body_len); + p.body_len = body_len; + p.body_base = tmp; + p.body = 0; + return p; + } + +} diff --git a/src/teavm/java/com/jcraft/jogg/StreamState.java b/src/teavm/java/com/jcraft/jogg/StreamState.java new file mode 100755 index 0000000..e9c3663 --- /dev/null +++ b/src/teavm/java/com/jcraft/jogg/StreamState.java @@ -0,0 +1,538 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jogg; + +public class StreamState { + byte[] body_data; /* bytes from packet bodies */ + int body_storage; /* storage elements allocated */ + int body_fill; /* elements stored; fill mark */ + private int body_returned; /* elements of fill returned */ + + int[] lacing_vals; /* The values that will go to the segment table */ + long[] granule_vals; /* + * pcm_pos values for headers. Not compact this way, but it is simple coupled to + * the lacing fifo + */ + int lacing_storage; + int lacing_fill; + int lacing_packet; + int lacing_returned; + + byte[] header = new byte[282]; /* working space for header encode */ + int header_fill; + + public int e_o_s; /* + * set when we have buffered the last packet in the logical bitstream + */ + int b_o_s; /* + * set after we've written the initial page of a logical bitstream + */ + int serialno; + int pageno; + long packetno; /* + * sequence number for decode; the framing knows where there's a hole in the + * data, but we need coupling so that the codec (which is in a seperate + * abstraction layer) also knows about the gap + */ + long granulepos; + + public StreamState() { + init(); + } + + StreamState(int serialno) { + this(); + init(serialno); + } + + void init() { + body_storage = 16 * 1024; + body_data = new byte[body_storage]; + lacing_storage = 1024; + lacing_vals = new int[lacing_storage]; + granule_vals = new long[lacing_storage]; + } + + public void init(int serialno) { + if (body_data == null) { + init(); + } else { + for (int i = 0; i < body_data.length; i++) + body_data[i] = 0; + for (int i = 0; i < lacing_vals.length; i++) + lacing_vals[i] = 0; + for (int i = 0; i < granule_vals.length; i++) + granule_vals[i] = 0; + } + this.serialno = serialno; + } + + public void clear() { + body_data = null; + lacing_vals = null; + granule_vals = null; + } + + void destroy() { + clear(); + } + + void body_expand(int needed) { + if (body_storage <= body_fill + needed) { + body_storage += (needed + 1024); + byte[] foo = new byte[body_storage]; + System.arraycopy(body_data, 0, foo, 0, body_data.length); + body_data = foo; + } + } + + void lacing_expand(int needed) { + if (lacing_storage <= lacing_fill + needed) { + lacing_storage += (needed + 32); + int[] foo = new int[lacing_storage]; + System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length); + lacing_vals = foo; + + long[] bar = new long[lacing_storage]; + System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length); + granule_vals = bar; + } + } + + /* submit data to the internal buffer of the framing engine */ + public int packetin(Packet op) { + int lacing_val = op.bytes / 255 + 1; + + if (body_returned != 0) { + /* + * advance packet data according to the body_returned pointer. We had to keep it + * around to return a pointer into the buffer last call + */ + + body_fill -= body_returned; + if (body_fill != 0) { + System.arraycopy(body_data, body_returned, body_data, 0, body_fill); + } + body_returned = 0; + } + + /* make sure we have the buffer storage */ + body_expand(op.bytes); + lacing_expand(lacing_val); + + /* + * Copy in the submitted packet. Yes, the copy is a waste; this is the liability + * of overly clean abstraction for the time being. It will actually be fairly + * easy to eliminate the extra copy in the future + */ + + System.arraycopy(op.packet_base, op.packet, body_data, body_fill, op.bytes); + body_fill += op.bytes; + + /* Store lacing vals for this packet */ + int j; + for (j = 0; j < lacing_val - 1; j++) { + lacing_vals[lacing_fill + j] = 255; + granule_vals[lacing_fill + j] = granulepos; + } + lacing_vals[lacing_fill + j] = (op.bytes) % 255; + granulepos = granule_vals[lacing_fill + j] = op.granulepos; + + /* flag the first segment as the beginning of the packet */ + lacing_vals[lacing_fill] |= 0x100; + + lacing_fill += lacing_val; + + /* for the sake of completeness */ + packetno++; + + if (op.e_o_s != 0) + e_o_s = 1; + return (0); + } + + public int packetout(Packet op) { + + /* + * The last part of decode. We have the stream broken into packet segments. Now + * we need to group them into packets (or return the out of sync markers) + */ + + int ptr = lacing_returned; + + if (lacing_packet <= ptr) { + return (0); + } + + if ((lacing_vals[ptr] & 0x400) != 0) { + /* We lost sync here; let the app know */ + lacing_returned++; + + /* + * we need to tell the codec there's a gap; it might need to handle previous + * packet dependencies. + */ + packetno++; + return (-1); + } + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size = lacing_vals[ptr] & 0xff; + int bytes = 0; + + op.packet_base = body_data; + op.packet = body_returned; + op.e_o_s = lacing_vals[ptr] & 0x200; /* last packet of the stream? */ + op.b_o_s = lacing_vals[ptr] & 0x100; /* first packet of the stream? */ + bytes += size; + + while (size == 255) { + int val = lacing_vals[++ptr]; + size = val & 0xff; + if ((val & 0x200) != 0) + op.e_o_s = 0x200; + bytes += size; + } + + op.packetno = packetno; + op.granulepos = granule_vals[ptr]; + op.bytes = bytes; + + body_returned += bytes; + + lacing_returned = ptr + 1; + } + packetno++; + return (1); + } + + // add the incoming page to the stream state; we decompose the page + // into packet segments here as well. + + public int pagein(Page og) { + byte[] header_base = og.header_base; + int header = og.header; + byte[] body_base = og.body_base; + int body = og.body; + int bodysize = og.body_len; + int segptr = 0; + + int version = og.version(); + int continued = og.continued(); + int bos = og.bos(); + int eos = og.eos(); + long granulepos = og.granulepos(); + int _serialno = og.serialno(); + int _pageno = og.pageno(); + int segments = header_base[header + 26] & 0xff; + + // clean up 'returned data' + { + int lr = lacing_returned; + int br = body_returned; + + // body data + if (br != 0) { + body_fill -= br; + if (body_fill != 0) { + System.arraycopy(body_data, br, body_data, 0, body_fill); + } + body_returned = 0; + } + + if (lr != 0) { + // segment table + if ((lacing_fill - lr) != 0) { + System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill - lr); + System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill - lr); + } + lacing_fill -= lr; + lacing_packet -= lr; + lacing_returned = 0; + } + } + + // check the serial number + if (_serialno != serialno) + return (-1); + if (version > 0) + return (-1); + + lacing_expand(segments + 1); + + // are we in sequence? + if (_pageno != pageno) { + int i; + + // unroll previous partial packet (if any) + for (i = lacing_packet; i < lacing_fill; i++) { + body_fill -= lacing_vals[i] & 0xff; + // System.out.println("??"); + } + lacing_fill = lacing_packet; + + // make a note of dropped data in segment table + if (pageno != -1) { + lacing_vals[lacing_fill++] = 0x400; + lacing_packet++; + } + + // are we a 'continued packet' page? If so, we'll need to skip + // some segments + if (continued != 0) { + bos = 0; + for (; segptr < segments; segptr++) { + int val = (header_base[header + 27 + segptr] & 0xff); + body += val; + bodysize -= val; + if (val < 255) { + segptr++; + break; + } + } + } + } + + if (bodysize != 0) { + body_expand(bodysize); + System.arraycopy(body_base, body, body_data, body_fill, bodysize); + body_fill += bodysize; + } + + { + int saved = -1; + while (segptr < segments) { + int val = (header_base[header + 27 + segptr] & 0xff); + lacing_vals[lacing_fill] = val; + granule_vals[lacing_fill] = -1; + + if (bos != 0) { + lacing_vals[lacing_fill] |= 0x100; + bos = 0; + } + + if (val < 255) + saved = lacing_fill; + + lacing_fill++; + segptr++; + + if (val < 255) + lacing_packet = lacing_fill; + } + + /* set the granulepos on the last pcmval of the last full packet */ + if (saved != -1) { + granule_vals[saved] = granulepos; + } + } + + if (eos != 0) { + e_o_s = 1; + if (lacing_fill > 0) + lacing_vals[lacing_fill - 1] |= 0x200; + } + + pageno = _pageno + 1; + return (0); + } + + /* + * This will flush remaining packets into a page (returning nonzero), even if + * there is not enough data to trigger a flush normally (undersized page). If + * there are no packets or partial packets to flush, ogg_stream_flush returns 0. + * Note that ogg_stream_flush will try to flush a normal sized page like + * ogg_stream_pageout; a call to ogg_stream_flush does not gurantee that all + * packets have flushed. Only a return value of 0 from ogg_stream_flush + * indicates all packet data is flushed into pages. + * + * ogg_stream_page will flush the last page in a stream even if it's undersized; + * you almost certainly want to use ogg_stream_pageout (and *not* + * ogg_stream_flush) unless you need to flush an undersized page in the middle + * of a stream for some reason. + */ + + public int flush(Page og) { + + int i; + int vals = 0; + int maxvals = (lacing_fill > 255 ? 255 : lacing_fill); + int bytes = 0; + int acc = 0; + long granule_pos = granule_vals[0]; + + if (maxvals == 0) + return (0); + + /* construct a page */ + /* decide how many segments to include */ + + /* + * If this is the initial header case, the first page must only include the + * initial header packet + */ + if (b_o_s == 0) { /* 'initial header page' case */ + granule_pos = 0; + for (vals = 0; vals < maxvals; vals++) { + if ((lacing_vals[vals] & 0x0ff) < 255) { + vals++; + break; + } + } + } else { + for (vals = 0; vals < maxvals; vals++) { + if (acc > 4096) + break; + acc += (lacing_vals[vals] & 0x0ff); + granule_pos = granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + System.arraycopy("OggS".getBytes(), 0, header, 0, 4); + + /* stream structure version */ + header[4] = 0x00; + + /* continued packet flag? */ + header[5] = 0x00; + if ((lacing_vals[0] & 0x100) == 0) + header[5] |= 0x01; + /* first page flag? */ + if (b_o_s == 0) + header[5] |= 0x02; + /* last page flag? */ + if (e_o_s != 0 && lacing_fill == vals) + header[5] |= 0x04; + b_o_s = 1; + + /* 64 bits of PCM position */ + for (i = 6; i < 14; i++) { + header[i] = (byte) granule_pos; + granule_pos >>>= 8; + } + + /* 32 bits of stream serial number */ + { + int _serialno = serialno; + for (i = 14; i < 18; i++) { + header[i] = (byte) _serialno; + _serialno >>>= 8; + } + } + + /* + * 32 bits of page counter (we have both counter and page header because this + * val can roll over) + */ + if (pageno == -1) + pageno = 0; /* + * because someone called stream_reset; this would be a strange thing to do in + * an encode stream, but it has plausible uses + */ + { + int _pageno = pageno++; + for (i = 18; i < 22; i++) { + header[i] = (byte) _pageno; + _pageno >>>= 8; + } + } + + /* zero for computation; filled in later */ + header[22] = 0; + header[23] = 0; + header[24] = 0; + header[25] = 0; + + /* segment table */ + header[26] = (byte) vals; + for (i = 0; i < vals; i++) { + header[i + 27] = (byte) lacing_vals[i]; + bytes += (header[i + 27] & 0xff); + } + + /* set pointers in the ogg_page struct */ + og.header_base = header; + og.header = 0; + og.header_len = header_fill = vals + 27; + og.body_base = body_data; + og.body = body_returned; + og.body_len = bytes; + + /* advance the lacing data and set the body_returned pointer */ + + lacing_fill -= vals; + System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill * 4); + System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill * 8); + body_returned += bytes; + + /* calculate the checksum */ + + og.checksum(); + + /* done */ + return (1); + } + + /* + * This constructs pages from buffered packet segments. The pointers returned + * are to static buffers; do not free. The returned buffers are good only until + * the next call (using the same ogg_stream_state) + */ + public int pageout(Page og) { + if ((e_o_s != 0 && lacing_fill != 0) || /* 'were done, now flush' case */ + body_fill - body_returned > 4096 || /* 'page nominal size' case */ + lacing_fill >= 255 || /* 'segment table full' case */ + (lacing_fill != 0 && b_o_s == 0)) { /* 'initial header page' case */ + return flush(og); + } + return 0; + } + + public int eof() { + return e_o_s; + } + + public int reset() { + body_fill = 0; + body_returned = 0; + + lacing_fill = 0; + lacing_packet = 0; + lacing_returned = 0; + + header_fill = 0; + + e_o_s = 0; + b_o_s = 0; + pageno = -1; + packetno = 0; + granulepos = 0; + return (0); + } +} diff --git a/src/teavm/java/com/jcraft/jogg/SyncState.java b/src/teavm/java/com/jcraft/jogg/SyncState.java new file mode 100755 index 0000000..a706c4f --- /dev/null +++ b/src/teavm/java/com/jcraft/jogg/SyncState.java @@ -0,0 +1,273 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jogg; + +// DECODING PRIMITIVES: packet streaming layer + +// This has two layers to place more of the multi-serialno and paging +// control in the application's hands. First, we expose a data buffer +// using ogg_decode_buffer(). The app either copies into the +// buffer, or passes it directly to read(), etc. We then call +// ogg_decode_wrote() to tell how many bytes we just added. +// +// Pages are returned (pointers into the buffer in ogg_sync_state) +// by ogg_decode_stream(). The page is then submitted to +// ogg_decode_page() along with the appropriate +// ogg_stream_state* (ie, matching serialno). We then get raw +// packets out calling ogg_stream_packet() with a +// ogg_stream_state. See the 'frame-prog.txt' docs for details and +// example code. + +public class SyncState { + + public byte[] data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; + + public int clear() { + data = null; + return (0); + } + + public int buffer(int size) { + // first, clear out any space that has been previously returned + if (returned != 0) { + fill -= returned; + if (fill > 0) { + System.arraycopy(data, returned, data, 0, fill); + } + returned = 0; + } + + if (size > storage - fill) { + // We need to extend the internal buffer + int newsize = size + fill + 4096; // an extra page to be nice + if (data != null) { + byte[] foo = new byte[newsize]; + System.arraycopy(data, 0, foo, 0, data.length); + data = foo; + } else { + data = new byte[newsize]; + } + storage = newsize; + } + + return (fill); + } + + public int wrote(int bytes) { + if (fill + bytes > storage) + return (-1); + fill += bytes; + return (0); + } + + // sync the stream. This is meant to be useful for finding page + // boundaries. + // + // return values for this: + // -n) skipped n bytes + // 0) page not ready; more data (no bytes skipped) + // n) page synced at current location; page length n bytes + private Page pageseek = new Page(); + private byte[] chksum = new byte[4]; + + public int pageseek(Page og) { + int page = returned; + int next; + int bytes = fill - returned; + + if (headerbytes == 0) { + int _headerbytes, i; + if (bytes < 27) + return (0); // not enough for a header + + /* verify capture pattern */ + if (data[page] != 'O' || data[page + 1] != 'g' || data[page + 2] != 'g' || data[page + 3] != 'S') { + headerbytes = 0; + bodybytes = 0; + + // search for possible capture + next = 0; + for (int ii = 0; ii < bytes - 1; ii++) { + if (data[page + 1 + ii] == 'O') { + next = page + 1 + ii; + break; + } + } + // next=memchr(page+1,'O',bytes-1); + if (next == 0) + next = fill; + + returned = next; + return (-(next - page)); + } + _headerbytes = (data[page + 26] & 0xff) + 27; + if (bytes < _headerbytes) + return (0); // not enough for header + seg table + + // count up body length in the segment table + + for (i = 0; i < (data[page + 26] & 0xff); i++) { + bodybytes += (data[page + 27 + i] & 0xff); + } + headerbytes = _headerbytes; + } + + if (bodybytes + headerbytes > bytes) + return (0); + + // The whole test page is buffered. Verify the checksum + synchronized (chksum) { + // Grab the checksum bytes, set the header field to zero + + System.arraycopy(data, page + 22, chksum, 0, 4); + data[page + 22] = 0; + data[page + 23] = 0; + data[page + 24] = 0; + data[page + 25] = 0; + + // set up a temp page struct and recompute the checksum + Page log = pageseek; + log.header_base = data; + log.header = page; + log.header_len = headerbytes; + + log.body_base = data; + log.body = page + headerbytes; + log.body_len = bodybytes; + log.checksum(); + + // Compare + if (chksum[0] != data[page + 22] || chksum[1] != data[page + 23] || chksum[2] != data[page + 24] + || chksum[3] != data[page + 25]) { + // D'oh. Mismatch! Corrupt page (or miscapture and not a page at all) + // replace the computed checksum with the one actually read in + System.arraycopy(chksum, 0, data, page + 22, 4); + // Bad checksum. Lose sync */ + + headerbytes = 0; + bodybytes = 0; + // search for possible capture + next = 0; + for (int ii = 0; ii < bytes - 1; ii++) { + if (data[page + 1 + ii] == 'O') { + next = page + 1 + ii; + break; + } + } + // next=memchr(page+1,'O',bytes-1); + if (next == 0) + next = fill; + returned = next; + return (-(next - page)); + } + } + + // yes, have a whole page all ready to go + { + page = returned; + + if (og != null) { + og.header_base = data; + og.header = page; + og.header_len = headerbytes; + og.body_base = data; + og.body = page + headerbytes; + og.body_len = bodybytes; + } + + unsynced = 0; + returned += (bytes = headerbytes + bodybytes); + headerbytes = 0; + bodybytes = 0; + return (bytes); + } + } + + // sync the stream and get a page. Keep trying until we find a page. + // Supress 'sync errors' after reporting the first. + // + // return values: + // -1) recapture (hole in data) + // 0) need more data + // 1) page returned + // + // Returns pointers into buffered data; invalidated by next call to + // _stream, _clear, _init, or _buffer + + public int pageout(Page og) { + // all we need to do is verify a page at the head of the stream + // buffer. If it doesn't verify, we look for the next potential + // frame + + while (true) { + int ret = pageseek(og); + if (ret > 0) { + // have a page + return (1); + } + if (ret == 0) { + // need more data + return (0); + } + + // head did not start a synced page... skipped some bytes + if (unsynced == 0) { + unsynced = 1; + return (-1); + } + // loop. keep looking + } + } + + // clear things to an initial state. Good to call, eg, before seeking + public int reset() { + fill = 0; + returned = 0; + unsynced = 0; + headerbytes = 0; + bodybytes = 0; + return (0); + } + + public void init() { + } + + public int getDataOffset() { + return returned; + } + + public int getBufferOffset() { + return fill; + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Block.java b/src/teavm/java/com/jcraft/jorbis/Block.java new file mode 100755 index 0000000..0a0e166 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Block.java @@ -0,0 +1,126 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +public class Block { + /// necessary stream state for linking to the framing abstraction + float[][] pcm = new float[0][]; // this is a pointer into local storage + Buffer opb = new Buffer(); + + int lW; + int W; + int nW; + int pcmend; + int mode; + + int eofflag; + long granulepos; + long sequence; + DspState vd; // For read-only access of configuration + + // bitmetrics for the frame + int glue_bits; + int time_bits; + int floor_bits; + int res_bits; + + public Block(DspState vd) { + this.vd = vd; + if (vd.analysisp != 0) { + opb.writeinit(); + } + } + + public void init(DspState vd) { + this.vd = vd; + } + + public int clear() { + if (vd != null) { + if (vd.analysisp != 0) { + opb.writeclear(); + } + } + return (0); + } + + public int synthesis(Packet op) { + Info vi = vd.vi; + + // first things first. Make sure decode is ready + opb.readinit(op.packet_base, op.packet, op.bytes); + + // Check the packet type + if (opb.read(1) != 0) { + // Oops. This is not an audio data packet + return (-1); + } + + // read our mode and pre/post windowsize + int _mode = opb.read(vd.modebits); + if (_mode == -1) + return (-1); + + mode = _mode; + W = vi.mode_param[mode].blockflag; + if (W != 0) { + lW = opb.read(1); + nW = opb.read(1); + if (nW == -1) + return (-1); + } else { + lW = 0; + nW = 0; + } + + // more setup + granulepos = op.granulepos; + sequence = op.packetno - 3; // first block is third packet + eofflag = op.e_o_s; + + // alloc pcm passback storage + pcmend = vi.blocksizes[W]; + if (pcm.length < vi.channels) { + pcm = new float[vi.channels][]; + } + for (int i = 0; i < vi.channels; i++) { + if (pcm[i] == null || pcm[i].length < pcmend) { + pcm[i] = new float[pcmend]; + } else { + for (int j = 0; j < pcmend; j++) { + pcm[i][j] = 0; + } + } + } + + // unpack_header enforces range checking + int type = vi.map_type[vi.mode_param[mode].mapping]; + return (FuncMapping.mapping_P[type].inverse(this, vd.mode[mode])); + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/CodeBook.java b/src/teavm/java/com/jcraft/jorbis/CodeBook.java new file mode 100755 index 0000000..c6c7f9f --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/CodeBook.java @@ -0,0 +1,471 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class CodeBook { + int dim; // codebook dimensions (elements per vector) + int entries; // codebook entries + StaticCodeBook c = new StaticCodeBook(); + + float[] valuelist; // list of dim*entries actual entry values + int[] codelist; // list of bitstream codewords for each entry + DecodeAux decode_tree; + + // returns the number of bits + int encode(int a, Buffer b) { + b.write(codelist[a], c.lengthlist[a]); + return (c.lengthlist[a]); + } + + // One the encode side, our vector writers are each designed for a + // specific purpose, and the encoder is not flexible without modification: + // + // The LSP vector coder uses a single stage nearest-match with no + // interleave, so no step and no error return. This is specced by floor0 + // and doesn't change. + // + // Residue0 encoding interleaves, uses multiple stages, and each stage + // peels of a specific amount of resolution from a lattice (thus we want + // to match by threshhold, not nearest match). Residue doesn't *have* to + // be encoded that way, but to change it, one will need to add more + // infrastructure on the encode side (decode side is specced and simpler) + + // floor0 LSP (single stage, non interleaved, nearest match) + // returns entry number and *modifies a* to the quantization value + int errorv(float[] a) { + int best = best(a, 1); + for (int k = 0; k < dim; k++) { + a[k] = valuelist[best * dim + k]; + } + return (best); + } + + // returns the number of bits and *modifies a* to the quantization value + int encodev(int best, float[] a, Buffer b) { + for (int k = 0; k < dim; k++) { + a[k] = valuelist[best * dim + k]; + } + return (encode(best, b)); + } + + // res0 (multistage, interleave, lattice) + // returns the number of bits and *modifies a* to the remainder value + int encodevs(float[] a, Buffer b, int step, int addmul) { + int best = besterror(a, step, addmul); + return (encode(best, b)); + } + + private int[] t = new int[15]; // decodevs_add is synchronized for re-using t. + + synchronized int decodevs_add(float[] a, int offset, Buffer b, int n) { + int step = n / dim; + int entry; + int i, j, o; + + if (t.length < step) { + t = new int[step]; + } + + for (i = 0; i < step; i++) { + entry = decode(b); + if (entry == -1) + return (-1); + t[i] = entry * dim; + } + for (i = 0, o = 0; i < dim; i++, o += step) { + for (j = 0; j < step; j++) { + a[offset + o + j] += valuelist[t[j] + i]; + } + } + + return (0); + } + + int decodev_add(float[] a, int offset, Buffer b, int n) { + int i, j, entry; + int t; + + if (dim > 8) { + for (i = 0; i < n;) { + entry = decode(b); + if (entry == -1) + return (-1); + t = entry * dim; + for (j = 0; j < dim;) { + a[offset + (i++)] += valuelist[t + (j++)]; + } + } + } else { + for (i = 0; i < n;) { + entry = decode(b); + if (entry == -1) + return (-1); + t = entry * dim; + j = 0; + switch (dim) { + case 8: + a[offset + (i++)] += valuelist[t + (j++)]; + case 7: + a[offset + (i++)] += valuelist[t + (j++)]; + case 6: + a[offset + (i++)] += valuelist[t + (j++)]; + case 5: + a[offset + (i++)] += valuelist[t + (j++)]; + case 4: + a[offset + (i++)] += valuelist[t + (j++)]; + case 3: + a[offset + (i++)] += valuelist[t + (j++)]; + case 2: + a[offset + (i++)] += valuelist[t + (j++)]; + case 1: + a[offset + (i++)] += valuelist[t + (j++)]; + case 0: + break; + } + } + } + return (0); + } + + int decodev_set(float[] a, int offset, Buffer b, int n) { + int i, j, entry; + int t; + + for (i = 0; i < n;) { + entry = decode(b); + if (entry == -1) + return (-1); + t = entry * dim; + for (j = 0; j < dim;) { + a[offset + i++] = valuelist[t + (j++)]; + } + } + return (0); + } + + int decodevv_add(float[][] a, int offset, int ch, Buffer b, int n) { + int i, j, entry; + int chptr = 0; + + for (i = offset / ch; i < (offset + n) / ch;) { + entry = decode(b); + if (entry == -1) + return (-1); + + int t = entry * dim; + for (j = 0; j < dim; j++) { + a[chptr++][i] += valuelist[t + j]; + if (chptr == ch) { + chptr = 0; + i++; + } + } + } + return (0); + } + + // Decode side is specced and easier, because we don't need to find + // matches using different criteria; we simply read and map. There are + // two things we need to do 'depending': + // + // We may need to support interleave. We don't really, but it's + // convenient to do it here rather than rebuild the vector later. + // + // Cascades may be additive or multiplicitive; this is not inherent in + // the codebook, but set in the code using the codebook. Like + // interleaving, it's easiest to do it here. + // stage==0 -> declarative (set the value) + // stage==1 -> additive + // stage==2 -> multiplicitive + + // returns the entry number or -1 on eof + int decode(Buffer b) { + int ptr = 0; + DecodeAux t = decode_tree; + int lok = b.look(t.tabn); + + if (lok >= 0) { + ptr = t.tab[lok]; + b.adv(t.tabl[lok]); + if (ptr <= 0) { + return -ptr; + } + } + do { + switch (b.read1()) { + case 0: + ptr = t.ptr0[ptr]; + break; + case 1: + ptr = t.ptr1[ptr]; + break; + case -1: + default: + return (-1); + } + } while (ptr > 0); + return (-ptr); + } + + // returns the entry number or -1 on eof + int decodevs(float[] a, int index, Buffer b, int step, int addmul) { + int entry = decode(b); + if (entry == -1) + return (-1); + switch (addmul) { + case -1: + for (int i = 0, o = 0; i < dim; i++, o += step) + a[index + o] = valuelist[entry * dim + i]; + break; + case 0: + for (int i = 0, o = 0; i < dim; i++, o += step) + a[index + o] += valuelist[entry * dim + i]; + break; + case 1: + for (int i = 0, o = 0; i < dim; i++, o += step) + a[index + o] *= valuelist[entry * dim + i]; + break; + default: + // System.err.println("CodeBook.decodeves: addmul="+addmul); + } + return (entry); + } + + int best(float[] a, int step) { + // brute force it! + { + int besti = -1; + float best = 0.f; + int e = 0; + for (int i = 0; i < entries; i++) { + if (c.lengthlist[i] > 0) { + float _this = dist(dim, valuelist, e, a, step); + if (besti == -1 || _this < best) { + best = _this; + besti = i; + } + } + e += dim; + } + return (besti); + } + } + + // returns the entry number and *modifies a* to the remainder value + int besterror(float[] a, int step, int addmul) { + int best = best(a, step); + switch (addmul) { + case 0: + for (int i = 0, o = 0; i < dim; i++, o += step) + a[o] -= valuelist[best * dim + i]; + break; + case 1: + for (int i = 0, o = 0; i < dim; i++, o += step) { + float val = valuelist[best * dim + i]; + if (val == 0) { + a[o] = 0; + } else { + a[o] /= val; + } + } + break; + } + return (best); + } + + void clear() { + } + + private static float dist(int el, float[] ref, int index, float[] b, int step) { + float acc = (float) 0.; + for (int i = 0; i < el; i++) { + float val = (ref[index + i] - b[i * step]); + acc += val * val; + } + return (acc); + } + + int init_decode(StaticCodeBook s) { + c = s; + entries = s.entries; + dim = s.dim; + valuelist = s.unquantize(); + + decode_tree = make_decode_tree(); + if (decode_tree == null) { + clear(); + return (-1); + } + return (0); + } + + // given a list of word lengths, generate a list of codewords. Works + // for length ordered or unordered, always assigns the lowest valued + // codewords first. Extended to handle unused entries (length 0) + static int[] make_words(int[] l, int n) { + int[] marker = new int[33]; + int[] r = new int[n]; + + for (int i = 0; i < n; i++) { + int length = l[i]; + if (length > 0) { + int entry = marker[length]; + + // when we claim a node for an entry, we also claim the nodes + // below it (pruning off the imagined tree that may have dangled + // from it) as well as blocking the use of any nodes directly + // above for leaves + + // update ourself + if (length < 32 && (entry >>> length) != 0) { + // error condition; the lengths must specify an overpopulated tree + // free(r); + return (null); + } + r[i] = entry; + + // Look to see if the next shorter marker points to the node + // above. if so, update it and repeat. + { + for (int j = length; j > 0; j--) { + if ((marker[j] & 1) != 0) { + // have to jump branches + if (j == 1) + marker[1]++; + else + marker[j] = marker[j - 1] << 1; + break; // invariant says next upper marker would already + // have been moved if it was on the same path + } + marker[j]++; + } + } + + // prune the tree; the implicit invariant says all the longer + // markers were dangling from our just-taken node. Dangle them + // from our *new* node. + for (int j = length + 1; j < 33; j++) { + if ((marker[j] >>> 1) == entry) { + entry = marker[j]; + marker[j] = marker[j - 1] << 1; + } else { + break; + } + } + } + } + + // bitreverse the words because our bitwise packer/unpacker is LSb + // endian + for (int i = 0; i < n; i++) { + int temp = 0; + for (int j = 0; j < l[i]; j++) { + temp <<= 1; + temp |= (r[i] >>> j) & 1; + } + r[i] = temp; + } + + return (r); + } + + // build the decode helper tree from the codewords + DecodeAux make_decode_tree() { + int top = 0; + DecodeAux t = new DecodeAux(); + int[] ptr0 = t.ptr0 = new int[entries * 2]; + int[] ptr1 = t.ptr1 = new int[entries * 2]; + int[] codelist = make_words(c.lengthlist, c.entries); + + if (codelist == null) + return (null); + t.aux = entries * 2; + + for (int i = 0; i < entries; i++) { + if (c.lengthlist[i] > 0) { + int ptr = 0; + int j; + for (j = 0; j < c.lengthlist[i] - 1; j++) { + int bit = (codelist[i] >>> j) & 1; + if (bit == 0) { + if (ptr0[ptr] == 0) { + ptr0[ptr] = ++top; + } + ptr = ptr0[ptr]; + } else { + if (ptr1[ptr] == 0) { + ptr1[ptr] = ++top; + } + ptr = ptr1[ptr]; + } + } + + if (((codelist[i] >>> j) & 1) == 0) { + ptr0[ptr] = -i; + } else { + ptr1[ptr] = -i; + } + + } + } + + t.tabn = Util.ilog(entries) - 4; + + if (t.tabn < 5) + t.tabn = 5; + int n = 1 << t.tabn; + t.tab = new int[n]; + t.tabl = new int[n]; + for (int i = 0; i < n; i++) { + int p = 0; + int j = 0; + for (j = 0; j < t.tabn && (p > 0 || j == 0); j++) { + if ((i & (1 << j)) != 0) { + p = ptr1[p]; + } else { + p = ptr0[p]; + } + } + t.tab[i] = p; // -code + t.tabl[i] = j; // length + } + + return (t); + } + + class DecodeAux { + int[] tab; + int[] tabl; + int tabn; + + int[] ptr0; + int[] ptr1; + int aux; // number of tree entries + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Comment.java b/src/teavm/java/com/jcraft/jorbis/Comment.java new file mode 100755 index 0000000..77f95db --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Comment.java @@ -0,0 +1,240 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +// the comments are not part of vorbis_info so that vorbis_info can be +// static storage +public class Comment { + private static byte[] _vorbis = "vorbis".getBytes(); + private static byte[] _vendor = "Xiphophorus libVorbis I 20000508".getBytes(); + + private static final int OV_EIMPL = -130; + + // unlimited user comment fields. + public byte[][] user_comments; + public int[] comment_lengths; + public int comments; + public byte[] vendor; + + public void init() { + user_comments = null; + comments = 0; + vendor = null; + } + + public void add(String comment) { + add(comment.getBytes()); + } + + private void add(byte[] comment) { + byte[][] foo = new byte[comments + 2][]; + if (user_comments != null) { + System.arraycopy(user_comments, 0, foo, 0, comments); + } + user_comments = foo; + + int[] goo = new int[comments + 2]; + if (comment_lengths != null) { + System.arraycopy(comment_lengths, 0, goo, 0, comments); + } + comment_lengths = goo; + + byte[] bar = new byte[comment.length + 1]; + System.arraycopy(comment, 0, bar, 0, comment.length); + user_comments[comments] = bar; + comment_lengths[comments] = comment.length; + comments++; + user_comments[comments] = null; + } + + public void add_tag(String tag, String contents) { + if (contents == null) + contents = ""; + add(tag + "=" + contents); + } + + static boolean tagcompare(byte[] s1, byte[] s2, int n) { + int c = 0; + byte u1, u2; + while (c < n) { + u1 = s1[c]; + u2 = s2[c]; + if ('Z' >= u1 && u1 >= 'A') + u1 = (byte) (u1 - 'A' + 'a'); + if ('Z' >= u2 && u2 >= 'A') + u2 = (byte) (u2 - 'A' + 'a'); + if (u1 != u2) { + return false; + } + c++; + } + return true; + } + + public String query(String tag) { + return query(tag, 0); + } + + public String query(String tag, int count) { + int foo = query(tag.getBytes(), count); + if (foo == -1) + return null; + byte[] comment = user_comments[foo]; + for (int i = 0; i < comment_lengths[foo]; i++) { + if (comment[i] == '=') { + return new String(comment, i + 1, comment_lengths[foo] - (i + 1)); + } + } + return null; + } + + private int query(byte[] tag, int count) { + int i = 0; + int found = 0; + int fulltaglen = tag.length + 1; + byte[] fulltag = new byte[fulltaglen]; + System.arraycopy(tag, 0, fulltag, 0, tag.length); + fulltag[tag.length] = (byte) '='; + + for (i = 0; i < comments; i++) { + if (tagcompare(user_comments[i], fulltag, fulltaglen)) { + if (count == found) { + // We return a pointer to the data, not a copy + // return user_comments[i] + taglen + 1; + return i; + } else { + found++; + } + } + } + return -1; + } + + int unpack(Buffer opb) { + int vendorlen = opb.read(32); + if (vendorlen < 0) { + clear(); + return (-1); + } + vendor = new byte[vendorlen + 1]; + opb.read(vendor, vendorlen); + comments = opb.read(32); + if (comments < 0) { + clear(); + return (-1); + } + user_comments = new byte[comments + 1][]; + comment_lengths = new int[comments + 1]; + + for (int i = 0; i < comments; i++) { + int len = opb.read(32); + if (len < 0) { + clear(); + return (-1); + } + comment_lengths[i] = len; + user_comments[i] = new byte[len + 1]; + opb.read(user_comments[i], len); + } + if (opb.read(1) != 1) { + clear(); + return (-1); + + } + return (0); + } + + int pack(Buffer opb) { + // preamble + opb.write(0x03, 8); + opb.write(_vorbis); + + // vendor + opb.write(_vendor.length, 32); + opb.write(_vendor); + + // comments + opb.write(comments, 32); + if (comments != 0) { + for (int i = 0; i < comments; i++) { + if (user_comments[i] != null) { + opb.write(comment_lengths[i], 32); + opb.write(user_comments[i]); + } else { + opb.write(0, 32); + } + } + } + opb.write(1, 1); + return (0); + } + + public int header_out(Packet op) { + Buffer opb = new Buffer(); + opb.writeinit(); + + if (pack(opb) != 0) + return OV_EIMPL; + + op.packet_base = new byte[opb.bytes()]; + op.packet = 0; + op.bytes = opb.bytes(); + System.arraycopy(opb.buffer(), 0, op.packet_base, 0, op.bytes); + op.b_o_s = 0; + op.e_o_s = 0; + op.granulepos = 0; + return 0; + } + + void clear() { + for (int i = 0; i < comments; i++) + user_comments[i] = null; + user_comments = null; + vendor = null; + } + + public String getVendor() { + return new String(vendor, 0, vendor.length - 1); + } + + public String getComment(int i) { + if (comments <= i) + return null; + return new String(user_comments[i], 0, user_comments[i].length - 1); + } + + public String toString() { + String foo = "Vendor: " + new String(vendor, 0, vendor.length - 1); + for (int i = 0; i < comments; i++) { + foo = foo + "\nComment: " + new String(user_comments[i], 0, user_comments[i].length - 1); + } + foo = foo + "\n"; + return foo; + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Drft.java b/src/teavm/java/com/jcraft/jorbis/Drft.java new file mode 100755 index 0000000..de8aad4 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Drft.java @@ -0,0 +1,1319 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class Drft { + int n; + float[] trigcache; + int[] splitcache; + + void backward(float[] data) { + if (n == 1) + return; + drftb1(n, data, trigcache, trigcache, n, splitcache); + } + + void init(int n) { + this.n = n; + trigcache = new float[3 * n]; + splitcache = new int[32]; + fdrffti(n, trigcache, splitcache); + } + + void clear() { + if (trigcache != null) + trigcache = null; + if (splitcache != null) + splitcache = null; + } + + static int[] ntryh = { 4, 2, 3, 5 }; + static float tpi = 6.28318530717958647692528676655900577f; + static float hsqt2 = .70710678118654752440084436210485f; + static float taui = .86602540378443864676372317075293618f; + static float taur = -.5f; + static float sqrt2 = 1.4142135623730950488016887242097f; + + static void drfti1(int n, float[] wa, int index, int[] ifac) { + float arg, argh, argld, fi; + int ntry = 0, i, j = -1; + int k1, l1, l2, ib; + int ld, ii, ip, is, nq, nr; + int ido, ipm, nfm1; + int nl = n; + int nf = 0; + + int state = 101; + + loop: while (true) { + switch (state) { + case 101: + j++; + if (j < 4) + ntry = ntryh[j]; + else + ntry += 2; + case 104: + nq = nl / ntry; + nr = nl - ntry * nq; + if (nr != 0) { + state = 101; + break; + } + nf++; + ifac[nf + 1] = ntry; + nl = nq; + if (ntry != 2) { + state = 107; + break; + } + if (nf == 1) { + state = 107; + break; + } + + for (i = 1; i < nf; i++) { + ib = nf - i + 1; + ifac[ib + 1] = ifac[ib]; + } + ifac[2] = 2; + case 107: + if (nl != 1) { + state = 104; + break; + } + ifac[0] = n; + ifac[1] = nf; + argh = tpi / n; + is = 0; + nfm1 = nf - 1; + l1 = 1; + + if (nfm1 == 0) + return; + + for (k1 = 0; k1 < nfm1; k1++) { + ip = ifac[k1 + 2]; + ld = 0; + l2 = l1 * ip; + ido = n / l2; + ipm = ip - 1; + + for (j = 0; j < ipm; j++) { + ld += l1; + i = is; + argld = (float) ld * argh; + fi = 0.f; + for (ii = 2; ii < ido; ii += 2) { + fi += 1.f; + arg = fi * argld; + wa[index + i++] = (float) Math.cos(arg); + wa[index + i++] = (float) Math.sin(arg); + } + is += ido; + } + l1 = l2; + } + break loop; + } + } + } + + static void fdrffti(int n, float[] wsave, int[] ifac) { + if (n == 1) + return; + drfti1(n, wsave, n, ifac); + } + + static void dradf2(int ido, int l1, float[] cc, float[] ch, float[] wa1, int index) { + int i, k; + float ti2, tr2; + int t0, t1, t2, t3, t4, t5, t6; + + t1 = 0; + t0 = (t2 = l1 * ido); + t3 = ido << 1; + for (k = 0; k < l1; k++) { + ch[t1 << 1] = cc[t1] + cc[t2]; + ch[(t1 << 1) + t3 - 1] = cc[t1] - cc[t2]; + t1 += ido; + t2 += ido; + } + + if (ido < 2) + return; + + if (ido != 2) { + t1 = 0; + t2 = t0; + for (k = 0; k < l1; k++) { + t3 = t2; + t4 = (t1 << 1) + (ido << 1); + t5 = t1; + t6 = t1 + t1; + for (i = 2; i < ido; i += 2) { + t3 += 2; + t4 -= 2; + t5 += 2; + t6 += 2; + tr2 = wa1[index + i - 2] * cc[t3 - 1] + wa1[index + i - 1] * cc[t3]; + ti2 = wa1[index + i - 2] * cc[t3] - wa1[index + i - 1] * cc[t3 - 1]; + ch[t6] = cc[t5] + ti2; + ch[t4] = ti2 - cc[t5]; + ch[t6 - 1] = cc[t5 - 1] + tr2; + ch[t4 - 1] = cc[t5 - 1] - tr2; + } + t1 += ido; + t2 += ido; + } + if (ido % 2 == 1) + return; + } + + t3 = (t2 = (t1 = ido) - 1); + t2 += t0; + for (k = 0; k < l1; k++) { + ch[t1] = -cc[t2]; + ch[t1 - 1] = cc[t3]; + t1 += ido << 1; + t2 += ido; + t3 += ido; + } + } + + static void dradf4(int ido, int l1, float[] cc, float[] ch, float[] wa1, int index1, float[] wa2, int index2, + float[] wa3, int index3) { + int i, k, t0, t1, t2, t3, t4, t5, t6; + float ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; + t0 = l1 * ido; + + t1 = t0; + t4 = t1 << 1; + t2 = t1 + (t1 << 1); + t3 = 0; + + for (k = 0; k < l1; k++) { + tr1 = cc[t1] + cc[t2]; + tr2 = cc[t3] + cc[t4]; + + ch[t5 = t3 << 2] = tr1 + tr2; + ch[(ido << 2) + t5 - 1] = tr2 - tr1; + ch[(t5 += (ido << 1)) - 1] = cc[t3] - cc[t4]; + ch[t5] = cc[t2] - cc[t1]; + + t1 += ido; + t2 += ido; + t3 += ido; + t4 += ido; + } + if (ido < 2) + return; + + if (ido != 2) { + t1 = 0; + for (k = 0; k < l1; k++) { + t2 = t1; + t4 = t1 << 2; + t5 = (t6 = ido << 1) + t4; + for (i = 2; i < ido; i += 2) { + t3 = (t2 += 2); + t4 += 2; + t5 -= 2; + + t3 += t0; + cr2 = wa1[index1 + i - 2] * cc[t3 - 1] + wa1[index1 + i - 1] * cc[t3]; + ci2 = wa1[index1 + i - 2] * cc[t3] - wa1[index1 + i - 1] * cc[t3 - 1]; + t3 += t0; + cr3 = wa2[index2 + i - 2] * cc[t3 - 1] + wa2[index2 + i - 1] * cc[t3]; + ci3 = wa2[index2 + i - 2] * cc[t3] - wa2[index2 + i - 1] * cc[t3 - 1]; + t3 += t0; + cr4 = wa3[index3 + i - 2] * cc[t3 - 1] + wa3[index3 + i - 1] * cc[t3]; + ci4 = wa3[index3 + i - 2] * cc[t3] - wa3[index3 + i - 1] * cc[t3 - 1]; + + tr1 = cr2 + cr4; + tr4 = cr4 - cr2; + ti1 = ci2 + ci4; + ti4 = ci2 - ci4; + + ti2 = cc[t2] + ci3; + ti3 = cc[t2] - ci3; + tr2 = cc[t2 - 1] + cr3; + tr3 = cc[t2 - 1] - cr3; + + ch[t4 - 1] = tr1 + tr2; + ch[t4] = ti1 + ti2; + + ch[t5 - 1] = tr3 - ti4; + ch[t5] = tr4 - ti3; + + ch[t4 + t6 - 1] = ti4 + tr3; + ch[t4 + t6] = tr4 + ti3; + + ch[t5 + t6 - 1] = tr2 - tr1; + ch[t5 + t6] = ti1 - ti2; + } + t1 += ido; + } + if ((ido & 1) != 0) + return; + } + + t2 = (t1 = t0 + ido - 1) + (t0 << 1); + t3 = ido << 2; + t4 = ido; + t5 = ido << 1; + t6 = ido; + + for (k = 0; k < l1; k++) { + ti1 = -hsqt2 * (cc[t1] + cc[t2]); + tr1 = hsqt2 * (cc[t1] - cc[t2]); + + ch[t4 - 1] = tr1 + cc[t6 - 1]; + ch[t4 + t5 - 1] = cc[t6 - 1] - tr1; + + ch[t4] = ti1 - cc[t1 + t0]; + ch[t4 + t5] = ti1 + cc[t1 + t0]; + + t1 += ido; + t2 += ido; + t4 += t3; + t6 += ido; + } + } + + static void dradfg(int ido, int ip, int l1, int idl1, float[] cc, float[] c1, float[] c2, float[] ch, float[] ch2, + float[] wa, int index) { + int idij, ipph, i, j, k, l, ic, ik, is; + int t0, t1, t2 = 0, t3, t4, t5, t6, t7, t8, t9, t10; + float dc2, ai1, ai2, ar1, ar2, ds2; + int nbd; + float dcp = 0, arg, dsp = 0, ar1h, ar2h; + int idp2, ipp2; + + arg = tpi / (float) ip; + dcp = (float) Math.cos(arg); + dsp = (float) Math.sin(arg); + ipph = (ip + 1) >> 1; + ipp2 = ip; + idp2 = ido; + nbd = (ido - 1) >> 1; + t0 = l1 * ido; + t10 = ip * ido; + + int state = 100; + loop: while (true) { + switch (state) { + case 101: + if (ido == 1) { + state = 119; + break; + } + for (ik = 0; ik < idl1; ik++) + ch2[ik] = c2[ik]; + + t1 = 0; + for (j = 1; j < ip; j++) { + t1 += t0; + t2 = t1; + for (k = 0; k < l1; k++) { + ch[t2] = c1[t2]; + t2 += ido; + } + } + + is = -ido; + t1 = 0; + if (nbd > l1) { + for (j = 1; j < ip; j++) { + t1 += t0; + is += ido; + t2 = -ido + t1; + for (k = 0; k < l1; k++) { + idij = is - 1; + t2 += ido; + t3 = t2; + for (i = 2; i < ido; i += 2) { + idij += 2; + t3 += 2; + ch[t3 - 1] = wa[index + idij - 1] * c1[t3 - 1] + wa[index + idij] * c1[t3]; + ch[t3] = wa[index + idij - 1] * c1[t3] - wa[index + idij] * c1[t3 - 1]; + } + } + } + } else { + + for (j = 1; j < ip; j++) { + is += ido; + idij = is - 1; + t1 += t0; + t2 = t1; + for (i = 2; i < ido; i += 2) { + idij += 2; + t2 += 2; + t3 = t2; + for (k = 0; k < l1; k++) { + ch[t3 - 1] = wa[index + idij - 1] * c1[t3 - 1] + wa[index + idij] * c1[t3]; + ch[t3] = wa[index + idij - 1] * c1[t3] - wa[index + idij] * c1[t3 - 1]; + t3 += ido; + } + } + } + } + + t1 = 0; + t2 = ipp2 * t0; + if (nbd < l1) { + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + for (i = 2; i < ido; i += 2) { + t3 += 2; + t4 += 2; + t5 = t3 - ido; + t6 = t4 - ido; + for (k = 0; k < l1; k++) { + t5 += ido; + t6 += ido; + c1[t5 - 1] = ch[t5 - 1] + ch[t6 - 1]; + c1[t6 - 1] = ch[t5] - ch[t6]; + c1[t5] = ch[t5] + ch[t6]; + c1[t6] = ch[t6 - 1] - ch[t5 - 1]; + } + } + } + } else { + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + for (k = 0; k < l1; k++) { + t5 = t3; + t6 = t4; + for (i = 2; i < ido; i += 2) { + t5 += 2; + t6 += 2; + c1[t5 - 1] = ch[t5 - 1] + ch[t6 - 1]; + c1[t6 - 1] = ch[t5] - ch[t6]; + c1[t5] = ch[t5] + ch[t6]; + c1[t6] = ch[t6 - 1] - ch[t5 - 1]; + } + t3 += ido; + t4 += ido; + } + } + } + case 119: + for (ik = 0; ik < idl1; ik++) + c2[ik] = ch2[ik]; + + t1 = 0; + t2 = ipp2 * idl1; + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1 - ido; + t4 = t2 - ido; + for (k = 0; k < l1; k++) { + t3 += ido; + t4 += ido; + c1[t3] = ch[t3] + ch[t4]; + c1[t4] = ch[t4] - ch[t3]; + } + } + + ar1 = 1.f; + ai1 = 0.f; + t1 = 0; + t2 = ipp2 * idl1; + t3 = (ip - 1) * idl1; + for (l = 1; l < ipph; l++) { + t1 += idl1; + t2 -= idl1; + ar1h = dcp * ar1 - dsp * ai1; + ai1 = dcp * ai1 + dsp * ar1; + ar1 = ar1h; + t4 = t1; + t5 = t2; + t6 = t3; + t7 = idl1; + + for (ik = 0; ik < idl1; ik++) { + ch2[t4++] = c2[ik] + ar1 * c2[t7++]; + ch2[t5++] = ai1 * c2[t6++]; + } + + dc2 = ar1; + ds2 = ai1; + ar2 = ar1; + ai2 = ai1; + + t4 = idl1; + t5 = (ipp2 - 1) * idl1; + for (j = 2; j < ipph; j++) { + t4 += idl1; + t5 -= idl1; + + ar2h = dc2 * ar2 - ds2 * ai2; + ai2 = dc2 * ai2 + ds2 * ar2; + ar2 = ar2h; + + t6 = t1; + t7 = t2; + t8 = t4; + t9 = t5; + for (ik = 0; ik < idl1; ik++) { + ch2[t6++] += ar2 * c2[t8++]; + ch2[t7++] += ai2 * c2[t9++]; + } + } + } + t1 = 0; + for (j = 1; j < ipph; j++) { + t1 += idl1; + t2 = t1; + for (ik = 0; ik < idl1; ik++) + ch2[ik] += c2[t2++]; + } + + if (ido < l1) { + state = 132; + break; + } + + t1 = 0; + t2 = 0; + for (k = 0; k < l1; k++) { + t3 = t1; + t4 = t2; + for (i = 0; i < ido; i++) + cc[t4++] = ch[t3++]; + t1 += ido; + t2 += t10; + } + state = 135; + break; + + case 132: + for (i = 0; i < ido; i++) { + t1 = i; + t2 = i; + for (k = 0; k < l1; k++) { + cc[t2] = ch[t1]; + t1 += ido; + t2 += t10; + } + } + case 135: + t1 = 0; + t2 = ido << 1; + t3 = 0; + t4 = ipp2 * t0; + for (j = 1; j < ipph; j++) { + t1 += t2; + t3 += t0; + t4 -= t0; + + t5 = t1; + t6 = t3; + t7 = t4; + + for (k = 0; k < l1; k++) { + cc[t5 - 1] = ch[t6]; + cc[t5] = ch[t7]; + t5 += t10; + t6 += ido; + t7 += ido; + } + } + + if (ido == 1) + return; + if (nbd < l1) { + state = 141; + break; + } + + t1 = -ido; + t3 = 0; + t4 = 0; + t5 = ipp2 * t0; + for (j = 1; j < ipph; j++) { + t1 += t2; + t3 += t2; + t4 += t0; + t5 -= t0; + t6 = t1; + t7 = t3; + t8 = t4; + t9 = t5; + for (k = 0; k < l1; k++) { + for (i = 2; i < ido; i += 2) { + ic = idp2 - i; + cc[i + t7 - 1] = ch[i + t8 - 1] + ch[i + t9 - 1]; + cc[ic + t6 - 1] = ch[i + t8 - 1] - ch[i + t9 - 1]; + cc[i + t7] = ch[i + t8] + ch[i + t9]; + cc[ic + t6] = ch[i + t9] - ch[i + t8]; + } + t6 += t10; + t7 += t10; + t8 += ido; + t9 += ido; + } + } + return; + case 141: + t1 = -ido; + t3 = 0; + t4 = 0; + t5 = ipp2 * t0; + for (j = 1; j < ipph; j++) { + t1 += t2; + t3 += t2; + t4 += t0; + t5 -= t0; + for (i = 2; i < ido; i += 2) { + t6 = idp2 + t1 - i; + t7 = i + t3; + t8 = i + t4; + t9 = i + t5; + for (k = 0; k < l1; k++) { + cc[t7 - 1] = ch[t8 - 1] + ch[t9 - 1]; + cc[t6 - 1] = ch[t8 - 1] - ch[t9 - 1]; + cc[t7] = ch[t8] + ch[t9]; + cc[t6] = ch[t9] - ch[t8]; + t6 += t10; + t7 += t10; + t8 += ido; + t9 += ido; + } + } + } + break loop; + } + } + } + + static void drftf1(int n, float[] c, float[] ch, float[] wa, int[] ifac) { + int i, k1, l1, l2; + int na, kh, nf; + int ip, iw, ido, idl1, ix2, ix3; + + nf = ifac[1]; + na = 1; + l2 = n; + iw = n; + + for (k1 = 0; k1 < nf; k1++) { + kh = nf - k1; + ip = ifac[kh + 1]; + l1 = l2 / ip; + ido = n / l2; + idl1 = ido * l1; + iw -= (ip - 1) * ido; + na = 1 - na; + + int state = 100; + loop: while (true) { + switch (state) { + case 100: + if (ip != 4) { + state = 102; + break; + } + + ix2 = iw + ido; + ix3 = ix2 + ido; + if (na != 0) + dradf4(ido, l1, ch, c, wa, iw - 1, wa, ix2 - 1, wa, ix3 - 1); + else + dradf4(ido, l1, c, ch, wa, iw - 1, wa, ix2 - 1, wa, ix3 - 1); + state = 110; + break; + case 102: + if (ip != 2) { + state = 104; + break; + } + if (na != 0) { + state = 103; + break; + } + dradf2(ido, l1, c, ch, wa, iw - 1); + state = 110; + break; + case 103: + dradf2(ido, l1, ch, c, wa, iw - 1); + case 104: + if (ido == 1) + na = 1 - na; + if (na != 0) { + state = 109; + break; + } + dradfg(ido, ip, l1, idl1, c, c, c, ch, ch, wa, iw - 1); + na = 1; + state = 110; + break; + case 109: + dradfg(ido, ip, l1, idl1, ch, ch, ch, c, c, wa, iw - 1); + na = 0; + case 110: + l2 = l1; + break loop; + } + } + } + if (na == 1) + return; + for (i = 0; i < n; i++) + c[i] = ch[i]; + } + + static void dradb2(int ido, int l1, float[] cc, float[] ch, float[] wa1, int index) { + int i, k, t0, t1, t2, t3, t4, t5, t6; + float ti2, tr2; + + t0 = l1 * ido; + + t1 = 0; + t2 = 0; + t3 = (ido << 1) - 1; + for (k = 0; k < l1; k++) { + ch[t1] = cc[t2] + cc[t3 + t2]; + ch[t1 + t0] = cc[t2] - cc[t3 + t2]; + t2 = (t1 += ido) << 1; + } + + if (ido < 2) + return; + if (ido != 2) { + t1 = 0; + t2 = 0; + for (k = 0; k < l1; k++) { + t3 = t1; + t5 = (t4 = t2) + (ido << 1); + t6 = t0 + t1; + for (i = 2; i < ido; i += 2) { + t3 += 2; + t4 += 2; + t5 -= 2; + t6 += 2; + ch[t3 - 1] = cc[t4 - 1] + cc[t5 - 1]; + tr2 = cc[t4 - 1] - cc[t5 - 1]; + ch[t3] = cc[t4] - cc[t5]; + ti2 = cc[t4] + cc[t5]; + ch[t6 - 1] = wa1[index + i - 2] * tr2 - wa1[index + i - 1] * ti2; + ch[t6] = wa1[index + i - 2] * ti2 + wa1[index + i - 1] * tr2; + } + t2 = (t1 += ido) << 1; + } + if ((ido % 2) == 1) + return; + } + + t1 = ido - 1; + t2 = ido - 1; + for (k = 0; k < l1; k++) { + ch[t1] = cc[t2] + cc[t2]; + ch[t1 + t0] = -(cc[t2 + 1] + cc[t2 + 1]); + t1 += ido; + t2 += ido << 1; + } + } + + static void dradb3(int ido, int l1, float[] cc, float[] ch, float[] wa1, int index1, float[] wa2, int index2) { + int i, k, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10; + float ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2; + t0 = l1 * ido; + + t1 = 0; + t2 = t0 << 1; + t3 = ido << 1; + t4 = ido + (ido << 1); + t5 = 0; + for (k = 0; k < l1; k++) { + tr2 = cc[t3 - 1] + cc[t3 - 1]; + cr2 = cc[t5] + (taur * tr2); + ch[t1] = cc[t5] + tr2; + ci3 = taui * (cc[t3] + cc[t3]); + ch[t1 + t0] = cr2 - ci3; + ch[t1 + t2] = cr2 + ci3; + t1 += ido; + t3 += t4; + t5 += t4; + } + + if (ido == 1) + return; + + t1 = 0; + t3 = ido << 1; + for (k = 0; k < l1; k++) { + t7 = t1 + (t1 << 1); + t6 = (t5 = t7 + t3); + t8 = t1; + t10 = (t9 = t1 + t0) + t0; + + for (i = 2; i < ido; i += 2) { + t5 += 2; + t6 -= 2; + t7 += 2; + t8 += 2; + t9 += 2; + t10 += 2; + tr2 = cc[t5 - 1] + cc[t6 - 1]; + cr2 = cc[t7 - 1] + (taur * tr2); + ch[t8 - 1] = cc[t7 - 1] + tr2; + ti2 = cc[t5] - cc[t6]; + ci2 = cc[t7] + (taur * ti2); + ch[t8] = cc[t7] + ti2; + cr3 = taui * (cc[t5 - 1] - cc[t6 - 1]); + ci3 = taui * (cc[t5] + cc[t6]); + dr2 = cr2 - ci3; + dr3 = cr2 + ci3; + di2 = ci2 + cr3; + di3 = ci2 - cr3; + ch[t9 - 1] = wa1[index1 + i - 2] * dr2 - wa1[index1 + i - 1] * di2; + ch[t9] = wa1[index1 + i - 2] * di2 + wa1[index1 + i - 1] * dr2; + ch[t10 - 1] = wa2[index2 + i - 2] * dr3 - wa2[index2 + i - 1] * di3; + ch[t10] = wa2[index2 + i - 2] * di3 + wa2[index2 + i - 1] * dr3; + } + t1 += ido; + } + } + + static void dradb4(int ido, int l1, float[] cc, float[] ch, float[] wa1, int index1, float[] wa2, int index2, + float[] wa3, int index3) { + int i, k, t0, t1, t2, t3, t4, t5, t6, t7, t8; + float ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; + t0 = l1 * ido; + + t1 = 0; + t2 = ido << 2; + t3 = 0; + t6 = ido << 1; + for (k = 0; k < l1; k++) { + t4 = t3 + t6; + t5 = t1; + tr3 = cc[t4 - 1] + cc[t4 - 1]; + tr4 = cc[t4] + cc[t4]; + tr1 = cc[t3] - cc[(t4 += t6) - 1]; + tr2 = cc[t3] + cc[t4 - 1]; + ch[t5] = tr2 + tr3; + ch[t5 += t0] = tr1 - tr4; + ch[t5 += t0] = tr2 - tr3; + ch[t5 += t0] = tr1 + tr4; + t1 += ido; + t3 += t2; + } + + if (ido < 2) + return; + if (ido != 2) { + t1 = 0; + for (k = 0; k < l1; k++) { + t5 = (t4 = (t3 = (t2 = t1 << 2) + t6)) + t6; + t7 = t1; + for (i = 2; i < ido; i += 2) { + t2 += 2; + t3 += 2; + t4 -= 2; + t5 -= 2; + t7 += 2; + ti1 = cc[t2] + cc[t5]; + ti2 = cc[t2] - cc[t5]; + ti3 = cc[t3] - cc[t4]; + tr4 = cc[t3] + cc[t4]; + tr1 = cc[t2 - 1] - cc[t5 - 1]; + tr2 = cc[t2 - 1] + cc[t5 - 1]; + ti4 = cc[t3 - 1] - cc[t4 - 1]; + tr3 = cc[t3 - 1] + cc[t4 - 1]; + ch[t7 - 1] = tr2 + tr3; + cr3 = tr2 - tr3; + ch[t7] = ti2 + ti3; + ci3 = ti2 - ti3; + cr2 = tr1 - tr4; + cr4 = tr1 + tr4; + ci2 = ti1 + ti4; + ci4 = ti1 - ti4; + + ch[(t8 = t7 + t0) - 1] = wa1[index1 + i - 2] * cr2 - wa1[index1 + i - 1] * ci2; + ch[t8] = wa1[index1 + i - 2] * ci2 + wa1[index1 + i - 1] * cr2; + ch[(t8 += t0) - 1] = wa2[index2 + i - 2] * cr3 - wa2[index2 + i - 1] * ci3; + ch[t8] = wa2[index2 + i - 2] * ci3 + wa2[index2 + i - 1] * cr3; + ch[(t8 += t0) - 1] = wa3[index3 + i - 2] * cr4 - wa3[index3 + i - 1] * ci4; + ch[t8] = wa3[index3 + i - 2] * ci4 + wa3[index3 + i - 1] * cr4; + } + t1 += ido; + } + if (ido % 2 == 1) + return; + } + + t1 = ido; + t2 = ido << 2; + t3 = ido - 1; + t4 = ido + (ido << 1); + for (k = 0; k < l1; k++) { + t5 = t3; + ti1 = cc[t1] + cc[t4]; + ti2 = cc[t4] - cc[t1]; + tr1 = cc[t1 - 1] - cc[t4 - 1]; + tr2 = cc[t1 - 1] + cc[t4 - 1]; + ch[t5] = tr2 + tr2; + ch[t5 += t0] = sqrt2 * (tr1 - ti1); + ch[t5 += t0] = ti2 + ti2; + ch[t5 += t0] = -sqrt2 * (tr1 + ti1); + + t3 += ido; + t1 += t2; + t4 += t2; + } + } + + static void dradbg(int ido, int ip, int l1, int idl1, float[] cc, float[] c1, float[] c2, float[] ch, float[] ch2, + float[] wa, int index) { + + int idij, ipph = 0, i, j, k, l, ik, is, t0 = 0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 = 0, t11, t12; + float dc2, ai1, ai2, ar1, ar2, ds2; + int nbd = 0; + float dcp = 0, arg, dsp = 0, ar1h, ar2h; + int ipp2 = 0; + + int state = 100; + + loop: while (true) { + switch (state) { + case 100: + t10 = ip * ido; + t0 = l1 * ido; + arg = tpi / (float) ip; + dcp = (float) Math.cos(arg); + dsp = (float) Math.sin(arg); + nbd = (ido - 1) >>> 1; + ipp2 = ip; + ipph = (ip + 1) >>> 1; + if (ido < l1) { + state = 103; + break; + } + t1 = 0; + t2 = 0; + for (k = 0; k < l1; k++) { + t3 = t1; + t4 = t2; + for (i = 0; i < ido; i++) { + ch[t3] = cc[t4]; + t3++; + t4++; + } + t1 += ido; + t2 += t10; + } + state = 106; + break; + case 103: + t1 = 0; + for (i = 0; i < ido; i++) { + t2 = t1; + t3 = t1; + for (k = 0; k < l1; k++) { + ch[t2] = cc[t3]; + t2 += ido; + t3 += t10; + } + t1++; + } + case 106: + t1 = 0; + t2 = ipp2 * t0; + t7 = (t5 = ido << 1); + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + t6 = t5; + for (k = 0; k < l1; k++) { + ch[t3] = cc[t6 - 1] + cc[t6 - 1]; + ch[t4] = cc[t6] + cc[t6]; + t3 += ido; + t4 += ido; + t6 += t10; + } + t5 += t7; + } + if (ido == 1) { + state = 116; + break; + } + if (nbd < l1) { + state = 112; + break; + } + + t1 = 0; + t2 = ipp2 * t0; + t7 = 0; + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + + t7 += (ido << 1); + t8 = t7; + for (k = 0; k < l1; k++) { + t5 = t3; + t6 = t4; + t9 = t8; + t11 = t8; + for (i = 2; i < ido; i += 2) { + t5 += 2; + t6 += 2; + t9 += 2; + t11 -= 2; + ch[t5 - 1] = cc[t9 - 1] + cc[t11 - 1]; + ch[t6 - 1] = cc[t9 - 1] - cc[t11 - 1]; + ch[t5] = cc[t9] - cc[t11]; + ch[t6] = cc[t9] + cc[t11]; + } + t3 += ido; + t4 += ido; + t8 += t10; + } + } + state = 116; + break; + case 112: + t1 = 0; + t2 = ipp2 * t0; + t7 = 0; + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + t7 += (ido << 1); + t8 = t7; + t9 = t7; + for (i = 2; i < ido; i += 2) { + t3 += 2; + t4 += 2; + t8 += 2; + t9 -= 2; + t5 = t3; + t6 = t4; + t11 = t8; + t12 = t9; + for (k = 0; k < l1; k++) { + ch[t5 - 1] = cc[t11 - 1] + cc[t12 - 1]; + ch[t6 - 1] = cc[t11 - 1] - cc[t12 - 1]; + ch[t5] = cc[t11] - cc[t12]; + ch[t6] = cc[t11] + cc[t12]; + t5 += ido; + t6 += ido; + t11 += t10; + t12 += t10; + } + } + } + case 116: + ar1 = 1.f; + ai1 = 0.f; + t1 = 0; + t9 = (t2 = ipp2 * idl1); + t3 = (ip - 1) * idl1; + for (l = 1; l < ipph; l++) { + t1 += idl1; + t2 -= idl1; + + ar1h = dcp * ar1 - dsp * ai1; + ai1 = dcp * ai1 + dsp * ar1; + ar1 = ar1h; + t4 = t1; + t5 = t2; + t6 = 0; + t7 = idl1; + t8 = t3; + for (ik = 0; ik < idl1; ik++) { + c2[t4++] = ch2[t6++] + ar1 * ch2[t7++]; + c2[t5++] = ai1 * ch2[t8++]; + } + dc2 = ar1; + ds2 = ai1; + ar2 = ar1; + ai2 = ai1; + + t6 = idl1; + t7 = t9 - idl1; + for (j = 2; j < ipph; j++) { + t6 += idl1; + t7 -= idl1; + ar2h = dc2 * ar2 - ds2 * ai2; + ai2 = dc2 * ai2 + ds2 * ar2; + ar2 = ar2h; + t4 = t1; + t5 = t2; + t11 = t6; + t12 = t7; + for (ik = 0; ik < idl1; ik++) { + c2[t4++] += ar2 * ch2[t11++]; + c2[t5++] += ai2 * ch2[t12++]; + } + } + } + + t1 = 0; + for (j = 1; j < ipph; j++) { + t1 += idl1; + t2 = t1; + for (ik = 0; ik < idl1; ik++) + ch2[ik] += ch2[t2++]; + } + + t1 = 0; + t2 = ipp2 * t0; + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + for (k = 0; k < l1; k++) { + ch[t3] = c1[t3] - c1[t4]; + ch[t4] = c1[t3] + c1[t4]; + t3 += ido; + t4 += ido; + } + } + + if (ido == 1) { + state = 132; + break; + } + if (nbd < l1) { + state = 128; + break; + } + + t1 = 0; + t2 = ipp2 * t0; + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + for (k = 0; k < l1; k++) { + t5 = t3; + t6 = t4; + for (i = 2; i < ido; i += 2) { + t5 += 2; + t6 += 2; + ch[t5 - 1] = c1[t5 - 1] - c1[t6]; + ch[t6 - 1] = c1[t5 - 1] + c1[t6]; + ch[t5] = c1[t5] + c1[t6 - 1]; + ch[t6] = c1[t5] - c1[t6 - 1]; + } + t3 += ido; + t4 += ido; + } + } + state = 132; + break; + case 128: + t1 = 0; + t2 = ipp2 * t0; + for (j = 1; j < ipph; j++) { + t1 += t0; + t2 -= t0; + t3 = t1; + t4 = t2; + for (i = 2; i < ido; i += 2) { + t3 += 2; + t4 += 2; + t5 = t3; + t6 = t4; + for (k = 0; k < l1; k++) { + ch[t5 - 1] = c1[t5 - 1] - c1[t6]; + ch[t6 - 1] = c1[t5 - 1] + c1[t6]; + ch[t5] = c1[t5] + c1[t6 - 1]; + ch[t6] = c1[t5] - c1[t6 - 1]; + t5 += ido; + t6 += ido; + } + } + } + case 132: + if (ido == 1) + return; + + for (ik = 0; ik < idl1; ik++) + c2[ik] = ch2[ik]; + + t1 = 0; + for (j = 1; j < ip; j++) { + t2 = (t1 += t0); + for (k = 0; k < l1; k++) { + c1[t2] = ch[t2]; + t2 += ido; + } + } + + if (nbd > l1) { + state = 139; + break; + } + + is = -ido - 1; + t1 = 0; + for (j = 1; j < ip; j++) { + is += ido; + t1 += t0; + idij = is; + t2 = t1; + for (i = 2; i < ido; i += 2) { + t2 += 2; + idij += 2; + t3 = t2; + for (k = 0; k < l1; k++) { + c1[t3 - 1] = wa[index + idij - 1] * ch[t3 - 1] - wa[index + idij] * ch[t3]; + c1[t3] = wa[index + idij - 1] * ch[t3] + wa[index + idij] * ch[t3 - 1]; + t3 += ido; + } + } + } + return; + + case 139: + is = -ido - 1; + t1 = 0; + for (j = 1; j < ip; j++) { + is += ido; + t1 += t0; + t2 = t1; + for (k = 0; k < l1; k++) { + idij = is; + t3 = t2; + for (i = 2; i < ido; i += 2) { + idij += 2; + t3 += 2; + c1[t3 - 1] = wa[index + idij - 1] * ch[t3 - 1] - wa[index + idij] * ch[t3]; + c1[t3] = wa[index + idij - 1] * ch[t3] + wa[index + idij] * ch[t3 - 1]; + } + t2 += ido; + } + } + break loop; + } + } + } + + static void drftb1(int n, float[] c, float[] ch, float[] wa, int index, int[] ifac) { + int i, k1, l1, l2 = 0; + int na; + int nf, ip = 0, iw, ix2, ix3, ido = 0, idl1 = 0; + + nf = ifac[1]; + na = 0; + l1 = 1; + iw = 1; + + for (k1 = 0; k1 < nf; k1++) { + int state = 100; + loop: while (true) { + switch (state) { + case 100: + ip = ifac[k1 + 2]; + l2 = ip * l1; + ido = n / l2; + idl1 = ido * l1; + if (ip != 4) { + state = 103; + break; + } + ix2 = iw + ido; + ix3 = ix2 + ido; + + if (na != 0) + dradb4(ido, l1, ch, c, wa, index + iw - 1, wa, index + ix2 - 1, wa, index + ix3 - 1); + else + dradb4(ido, l1, c, ch, wa, index + iw - 1, wa, index + ix2 - 1, wa, index + ix3 - 1); + na = 1 - na; + state = 115; + break; + case 103: + if (ip != 2) { + state = 106; + break; + } + + if (na != 0) + dradb2(ido, l1, ch, c, wa, index + iw - 1); + else + dradb2(ido, l1, c, ch, wa, index + iw - 1); + na = 1 - na; + state = 115; + break; + + case 106: + if (ip != 3) { + state = 109; + break; + } + + ix2 = iw + ido; + if (na != 0) + dradb3(ido, l1, ch, c, wa, index + iw - 1, wa, index + ix2 - 1); + else + dradb3(ido, l1, c, ch, wa, index + iw - 1, wa, index + ix2 - 1); + na = 1 - na; + state = 115; + break; + case 109: + if (na != 0) + dradbg(ido, ip, l1, idl1, ch, ch, ch, c, c, wa, index + iw - 1); + else + dradbg(ido, ip, l1, idl1, c, c, c, ch, ch, wa, index + iw - 1); + if (ido == 1) + na = 1 - na; + + case 115: + l1 = l2; + iw += (ip - 1) * ido; + break loop; + } + } + } + if (na == 0) + return; + for (i = 0; i < n; i++) + c[i] = ch[i]; + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/DspState.java b/src/teavm/java/com/jcraft/jorbis/DspState.java new file mode 100755 index 0000000..997397f --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/DspState.java @@ -0,0 +1,369 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +public class DspState { + static final float M_PI = 3.1415926539f; + static final int VI_TRANSFORMB = 1; + static final int VI_WINDOWB = 1; + + int analysisp; + Info vi; + int modebits; + + float[][] pcm; + int pcm_storage; + int pcm_current; + int pcm_returned; + + float[] multipliers; + int envelope_storage; + int envelope_current; + + int eofflag; + + int lW; + int W; + int nW; + int centerW; + + long granulepos; + long sequence; + + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + // local lookup storage + float[][][][][] window; // block, leadin, leadout, type + Object[][] transform; + CodeBook[] fullbooks; + // backend lookups are tied to the mode, not the backend or naked mapping + Object[] mode; + + // local storage, only used on the encoding side. This way the + // application does not need to worry about freeing some packets' + // memory and not others'; packet storage is always tracked. + // Cleared next call to a _dsp_ function + byte[] header; + byte[] header1; + byte[] header2; + + public DspState() { + transform = new Object[2][]; + window = new float[2][][][][]; + window[0] = new float[2][][][]; + window[0][0] = new float[2][][]; + window[0][1] = new float[2][][]; + window[0][0][0] = new float[2][]; + window[0][0][1] = new float[2][]; + window[0][1][0] = new float[2][]; + window[0][1][1] = new float[2][]; + window[1] = new float[2][][][]; + window[1][0] = new float[2][][]; + window[1][1] = new float[2][][]; + window[1][0][0] = new float[2][]; + window[1][0][1] = new float[2][]; + window[1][1][0] = new float[2][]; + window[1][1][1] = new float[2][]; + } + + static float[] window(int type, int window, int left, int right) { + float[] ret = new float[window]; + switch (type) { + case 0: + // The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi) + { + int leftbegin = window / 4 - left / 2; + int rightbegin = window - window / 4 - right / 2; + + for (int i = 0; i < left; i++) { + float x = (float) ((i + .5) / left * M_PI / 2.); + x = (float) Math.sin(x); + x *= x; + x *= M_PI / 2.; + x = (float) Math.sin(x); + ret[i + leftbegin] = x; + } + + for (int i = leftbegin + left; i < rightbegin; i++) { + ret[i] = 1.f; + } + + for (int i = 0; i < right; i++) { + float x = (float) ((right - i - .5) / right * M_PI / 2.); + x = (float) Math.sin(x); + x *= x; + x *= M_PI / 2.; + x = (float) Math.sin(x); + ret[i + rightbegin] = x; + } + } + break; + default: + // free(ret); + return (null); + } + return (ret); + } + + // Analysis side code, but directly related to blocking. Thus it's + // here and not in analysis.c (which is for analysis transforms only). + // The init is here because some of it is shared + + int init(Info vi, boolean encp) { + this.vi = vi; + modebits = Util.ilog2(vi.modes); + + transform[0] = new Object[VI_TRANSFORMB]; + transform[1] = new Object[VI_TRANSFORMB]; + + // MDCT is tranform 0 + + transform[0][0] = new Mdct(); + transform[1][0] = new Mdct(); + ((Mdct) transform[0][0]).init(vi.blocksizes[0]); + ((Mdct) transform[1][0]).init(vi.blocksizes[1]); + + window[0][0][0] = new float[VI_WINDOWB][]; + window[0][0][1] = window[0][0][0]; + window[0][1][0] = window[0][0][0]; + window[0][1][1] = window[0][0][0]; + window[1][0][0] = new float[VI_WINDOWB][]; + window[1][0][1] = new float[VI_WINDOWB][]; + window[1][1][0] = new float[VI_WINDOWB][]; + window[1][1][1] = new float[VI_WINDOWB][]; + + for (int i = 0; i < VI_WINDOWB; i++) { + window[0][0][0][i] = window(i, vi.blocksizes[0], vi.blocksizes[0] / 2, vi.blocksizes[0] / 2); + window[1][0][0][i] = window(i, vi.blocksizes[1], vi.blocksizes[0] / 2, vi.blocksizes[0] / 2); + window[1][0][1][i] = window(i, vi.blocksizes[1], vi.blocksizes[0] / 2, vi.blocksizes[1] / 2); + window[1][1][0][i] = window(i, vi.blocksizes[1], vi.blocksizes[1] / 2, vi.blocksizes[0] / 2); + window[1][1][1][i] = window(i, vi.blocksizes[1], vi.blocksizes[1] / 2, vi.blocksizes[1] / 2); + } + + fullbooks = new CodeBook[vi.books]; + for (int i = 0; i < vi.books; i++) { + fullbooks[i] = new CodeBook(); + fullbooks[i].init_decode(vi.book_param[i]); + } + + // initialize the storage vectors to a decent size greater than the + // minimum + + pcm_storage = 8192; // we'll assume later that we have + // a minimum of twice the blocksize of + // accumulated samples in analysis + pcm = new float[vi.channels][]; + { + for (int i = 0; i < vi.channels; i++) { + pcm[i] = new float[pcm_storage]; + } + } + + // all 1 (large block) or 0 (small block) + // explicitly set for the sake of clarity + lW = 0; // previous window size + W = 0; // current window size + + // all vector indexes; multiples of samples_per_envelope_step + centerW = vi.blocksizes[1] / 2; + + pcm_current = centerW; + + // initialize all the mapping/backend lookups + mode = new Object[vi.modes]; + for (int i = 0; i < vi.modes; i++) { + int mapnum = vi.mode_param[i].mapping; + int maptype = vi.map_type[mapnum]; + mode[i] = FuncMapping.mapping_P[maptype].look(this, vi.mode_param[i], vi.map_param[mapnum]); + } + return (0); + } + + public int synthesis_init(Info vi) { + init(vi, false); + // Adjust centerW to allow an easier mechanism for determining output + pcm_returned = centerW; + centerW -= vi.blocksizes[W] / 4 + vi.blocksizes[lW] / 4; + granulepos = -1; + sequence = -1; + return (0); + } + + DspState(Info vi) { + this(); + init(vi, false); + // Adjust centerW to allow an easier mechanism for determining output + pcm_returned = centerW; + centerW -= vi.blocksizes[W] / 4 + vi.blocksizes[lW] / 4; + granulepos = -1; + sequence = -1; + } + + // Unike in analysis, the window is only partially applied for each + // block. The time domain envelope is not yet handled at the point of + // calling (as it relies on the previous block). + + public int synthesis_blockin(Block vb) { + // Shift out any PCM/multipliers that we returned previously + // centerW is currently the center of the last block added + if (centerW > vi.blocksizes[1] / 2 && pcm_returned > 8192) { + // don't shift too much; we need to have a minimum PCM buffer of + // 1/2 long block + + int shiftPCM = centerW - vi.blocksizes[1] / 2; + shiftPCM = (pcm_returned < shiftPCM ? pcm_returned : shiftPCM); + + pcm_current -= shiftPCM; + centerW -= shiftPCM; + pcm_returned -= shiftPCM; + if (shiftPCM != 0) { + for (int i = 0; i < vi.channels; i++) { + System.arraycopy(pcm[i], shiftPCM, pcm[i], 0, pcm_current); + } + } + } + + lW = W; + W = vb.W; + nW = -1; + + glue_bits += vb.glue_bits; + time_bits += vb.time_bits; + floor_bits += vb.floor_bits; + res_bits += vb.res_bits; + + if (sequence + 1 != vb.sequence) + granulepos = -1; // out of sequence; lose count + + sequence = vb.sequence; + + { + int sizeW = vi.blocksizes[W]; + int _centerW = centerW + vi.blocksizes[lW] / 4 + sizeW / 4; + int beginW = _centerW - sizeW / 2; + int endW = beginW + sizeW; + int beginSl = 0; + int endSl = 0; + + // Do we have enough PCM/mult storage for the block? + if (endW > pcm_storage) { + // expand the storage + pcm_storage = endW + vi.blocksizes[1]; + for (int i = 0; i < vi.channels; i++) { + float[] foo = new float[pcm_storage]; + System.arraycopy(pcm[i], 0, foo, 0, pcm[i].length); + pcm[i] = foo; + } + } + + // overlap/add PCM + switch (W) { + case 0: + beginSl = 0; + endSl = vi.blocksizes[0] / 2; + break; + case 1: + beginSl = vi.blocksizes[1] / 4 - vi.blocksizes[lW] / 4; + endSl = beginSl + vi.blocksizes[lW] / 2; + break; + } + + for (int j = 0; j < vi.channels; j++) { + int _pcm = beginW; + // the overlap/add section + int i = 0; + for (i = beginSl; i < endSl; i++) { + pcm[j][_pcm + i] += vb.pcm[j][i]; + } + // the remaining section + for (; i < sizeW; i++) { + pcm[j][_pcm + i] = vb.pcm[j][i]; + } + } + + // track the frame number... This is for convenience, but also + // making sure our last packet doesn't end with added padding. If + // the last packet is partial, the number of samples we'll have to + // return will be past the vb->granulepos. + // + // This is not foolproof! It will be confused if we begin + // decoding at the last page after a seek or hole. In that case, + // we don't have a starting point to judge where the last frame + // is. For this reason, vorbisfile will always try to make sure + // it reads the last two marked pages in proper sequence + + if (granulepos == -1) { + granulepos = vb.granulepos; + } else { + granulepos += (_centerW - centerW); + if (vb.granulepos != -1 && granulepos != vb.granulepos) { + if (granulepos > vb.granulepos && vb.eofflag != 0) { + // partial last frame. Strip the padding off + _centerW -= (granulepos - vb.granulepos); + } // else{ Shouldn't happen *unless* the bitstream is out of + // spec. Either way, believe the bitstream } + granulepos = vb.granulepos; + } + } + + // Update, cleanup + + centerW = _centerW; + pcm_current = endW; + if (vb.eofflag != 0) + eofflag = 1; + } + return (0); + } + + // pcm==NULL indicates we just want the pending samples, no more + public int synthesis_pcmout(float[][][] _pcm, int[] index) { + if (pcm_returned < centerW) { + if (_pcm != null) { + for (int i = 0; i < vi.channels; i++) { + index[i] = pcm_returned; + } + _pcm[0] = pcm; + } + return (centerW - pcm_returned); + } + return (0); + } + + public int synthesis_read(int bytes) { + if (bytes != 0 && pcm_returned + bytes > centerW) + return (-1); + pcm_returned += bytes; + return (0); + } + + public void clear() { + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Floor0.java b/src/teavm/java/com/jcraft/jorbis/Floor0.java new file mode 100755 index 0000000..b2d49c6 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Floor0.java @@ -0,0 +1,332 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Floor0 extends FuncFloor { + + void pack(Object i, Buffer opb) { + InfoFloor0 info = (InfoFloor0) i; + opb.write(info.order, 8); + opb.write(info.rate, 16); + opb.write(info.barkmap, 16); + opb.write(info.ampbits, 6); + opb.write(info.ampdB, 8); + opb.write(info.numbooks - 1, 4); + for (int j = 0; j < info.numbooks; j++) + opb.write(info.books[j], 8); + } + + Object unpack(Info vi, Buffer opb) { + InfoFloor0 info = new InfoFloor0(); + info.order = opb.read(8); + info.rate = opb.read(16); + info.barkmap = opb.read(16); + info.ampbits = opb.read(6); + info.ampdB = opb.read(8); + info.numbooks = opb.read(4) + 1; + + if ((info.order < 1) || (info.rate < 1) || (info.barkmap < 1) || (info.numbooks < 1)) { + return (null); + } + + for (int j = 0; j < info.numbooks; j++) { + info.books[j] = opb.read(8); + if (info.books[j] < 0 || info.books[j] >= vi.books) { + return (null); + } + } + return (info); + } + + Object look(DspState vd, InfoMode mi, Object i) { + float scale; + Info vi = vd.vi; + InfoFloor0 info = (InfoFloor0) i; + LookFloor0 look = new LookFloor0(); + look.m = info.order; + look.n = vi.blocksizes[mi.blockflag] / 2; + look.ln = info.barkmap; + look.vi = info; + look.lpclook.init(look.ln, look.m); + + // we choose a scaling constant so that: + scale = look.ln / toBARK((float) (info.rate / 2.)); + + // the mapping from a linear scale to a smaller bark scale is + // straightforward. We do *not* make sure that the linear mapping + // does not skip bark-scale bins; the decoder simply skips them and + // the encoder may do what it wishes in filling them. They're + // necessary in some mapping combinations to keep the scale spacing + // accurate + look.linearmap = new int[look.n]; + for (int j = 0; j < look.n; j++) { + int val = (int) Math.floor(toBARK((float) ((info.rate / 2.) / look.n * j)) * scale); // bark numbers + // represent band + // edges + if (val >= look.ln) + val = look.ln; // guard against the approximation + look.linearmap[j] = val; + } + return look; + } + + static float toBARK(float f) { + return (float) (13.1 * Math.atan(.00074 * (f)) + 2.24 * Math.atan((f) * (f) * 1.85e-8) + 1e-4 * (f)); + } + + Object state(Object i) { + EchstateFloor0 state = new EchstateFloor0(); + InfoFloor0 info = (InfoFloor0) i; + + // a safe size if usually too big (dim==1) + state.codewords = new int[info.order]; + state.curve = new float[info.barkmap]; + state.frameno = -1; + return (state); + } + + void free_info(Object i) { + } + + void free_look(Object i) { + } + + void free_state(Object vs) { + } + + int forward(Block vb, Object i, float[] in, float[] out, Object vs) { + return 0; + } + + float[] lsp = null; + + int inverse(Block vb, Object i, float[] out) { + // System.err.println("Floor0.inverse "+i.getClass()+"]"); + LookFloor0 look = (LookFloor0) i; + InfoFloor0 info = look.vi; + int ampraw = vb.opb.read(info.ampbits); + if (ampraw > 0) { // also handles the -1 out of data case + int maxval = (1 << info.ampbits) - 1; + float amp = (float) ampraw / maxval * info.ampdB; + int booknum = vb.opb.read(Util.ilog(info.numbooks)); + + if (booknum != -1 && booknum < info.numbooks) { + + synchronized (this) { + if (lsp == null || lsp.length < look.m) { + lsp = new float[look.m]; + } else { + for (int j = 0; j < look.m; j++) + lsp[j] = 0.f; + } + + CodeBook b = vb.vd.fullbooks[info.books[booknum]]; + float last = 0.f; + + for (int j = 0; j < look.m; j++) + out[j] = 0.0f; + + for (int j = 0; j < look.m; j += b.dim) { + if (b.decodevs(lsp, j, vb.opb, 1, -1) == -1) { + for (int k = 0; k < look.n; k++) + out[k] = 0.0f; + return (0); + } + } + for (int j = 0; j < look.m;) { + for (int k = 0; k < b.dim; k++, j++) + lsp[j] += last; + last = lsp[j - 1]; + } + // take the coefficients back to a spectral envelope curve + Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp, info.ampdB); + + return (1); + } + } + } + return (0); + } + + Object inverse1(Block vb, Object i, Object memo) { + LookFloor0 look = (LookFloor0) i; + InfoFloor0 info = look.vi; + float[] lsp = null; + if (memo instanceof float[]) { + lsp = (float[]) memo; + } + + int ampraw = vb.opb.read(info.ampbits); + if (ampraw > 0) { // also handles the -1 out of data case + int maxval = (1 << info.ampbits) - 1; + float amp = (float) ampraw / maxval * info.ampdB; + int booknum = vb.opb.read(Util.ilog(info.numbooks)); + + if (booknum != -1 && booknum < info.numbooks) { + CodeBook b = vb.vd.fullbooks[info.books[booknum]]; + float last = 0.f; + + if (lsp == null || lsp.length < look.m + 1) { + lsp = new float[look.m + 1]; + } else { + for (int j = 0; j < lsp.length; j++) + lsp[j] = 0.f; + } + + for (int j = 0; j < look.m; j += b.dim) { + if (b.decodev_set(lsp, j, vb.opb, b.dim) == -1) { + return (null); + } + } + + for (int j = 0; j < look.m;) { + for (int k = 0; k < b.dim; k++, j++) + lsp[j] += last; + last = lsp[j - 1]; + } + lsp[look.m] = amp; + return (lsp); + } + } + return (null); + } + + int inverse2(Block vb, Object i, Object memo, float[] out) { + LookFloor0 look = (LookFloor0) i; + InfoFloor0 info = look.vi; + + if (memo != null) { + float[] lsp = (float[]) memo; + float amp = lsp[look.m]; + + Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp, info.ampdB); + return (1); + } + for (int j = 0; j < look.n; j++) { + out[j] = 0.f; + } + return (0); + } + + static float fromdB(float x) { + return (float) (Math.exp((x) * .11512925)); + } + + static void lsp_to_lpc(float[] lsp, float[] lpc, int m) { + int i, j, m2 = m / 2; + float[] O = new float[m2]; + float[] E = new float[m2]; + float A; + float[] Ae = new float[m2 + 1]; + float[] Ao = new float[m2 + 1]; + float B; + float[] Be = new float[m2]; + float[] Bo = new float[m2]; + float temp; + + // even/odd roots setup + for (i = 0; i < m2; i++) { + O[i] = (float) (-2. * Math.cos(lsp[i * 2])); + E[i] = (float) (-2. * Math.cos(lsp[i * 2 + 1])); + } + + // set up impulse response + for (j = 0; j < m2; j++) { + Ae[j] = 0.f; + Ao[j] = 1.f; + Be[j] = 0.f; + Bo[j] = 1.f; + } + Ao[j] = 1.f; + Ae[j] = 1.f; + + // run impulse response + for (i = 1; i < m + 1; i++) { + A = B = 0.f; + for (j = 0; j < m2; j++) { + temp = O[j] * Ao[j] + Ae[j]; + Ae[j] = Ao[j]; + Ao[j] = A; + A += temp; + + temp = E[j] * Bo[j] + Be[j]; + Be[j] = Bo[j]; + Bo[j] = B; + B += temp; + } + lpc[i - 1] = (A + Ao[j] + B - Ae[j]) / 2; + Ao[j] = A; + Ae[j] = B; + } + } + + static void lpc_to_curve(float[] curve, float[] lpc, float amp, LookFloor0 l, String name, int frameno) { + // l->m+1 must be less than l->ln, but guard in case we get a bad stream + float[] lcurve = new float[Math.max(l.ln * 2, l.m * 2 + 2)]; + + if (amp == 0) { + for (int j = 0; j < l.n; j++) + curve[j] = 0.0f; + return; + } + l.lpclook.lpc_to_curve(lcurve, lpc, amp); + + for (int i = 0; i < l.n; i++) + curve[i] = lcurve[l.linearmap[i]]; + } + + class InfoFloor0 { + int order; + int rate; + int barkmap; + + int ampbits; + int ampdB; + + int numbooks; // <= 16 + int[] books = new int[16]; + } + + class LookFloor0 { + int n; + int ln; + int m; + int[] linearmap; + + InfoFloor0 vi; + Lpc lpclook = new Lpc(); + } + + class EchstateFloor0 { + int[] codewords; + float[] curve; + long frameno; + long codes; + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Floor1.java b/src/teavm/java/com/jcraft/jorbis/Floor1.java new file mode 100755 index 0000000..c9f77c3 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Floor1.java @@ -0,0 +1,584 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Floor1 extends FuncFloor { + static final int floor1_rangedb = 140; + static final int VIF_POSIT = 63; + + void pack(Object i, Buffer opb) { + InfoFloor1 info = (InfoFloor1) i; + + int count = 0; + int rangebits; + int maxposit = info.postlist[1]; + int maxclass = -1; + + /* save out partitions */ + opb.write(info.partitions, 5); /* only 0 to 31 legal */ + for (int j = 0; j < info.partitions; j++) { + opb.write(info.partitionclass[j], 4); /* only 0 to 15 legal */ + if (maxclass < info.partitionclass[j]) + maxclass = info.partitionclass[j]; + } + + /* save out partition classes */ + for (int j = 0; j < maxclass + 1; j++) { + opb.write(info.class_dim[j] - 1, 3); /* 1 to 8 */ + opb.write(info.class_subs[j], 2); /* 0 to 3 */ + if (info.class_subs[j] != 0) { + opb.write(info.class_book[j], 8); + } + for (int k = 0; k < (1 << info.class_subs[j]); k++) { + opb.write(info.class_subbook[j][k] + 1, 8); + } + } + + /* save out the post list */ + opb.write(info.mult - 1, 2); /* only 1,2,3,4 legal now */ + opb.write(Util.ilog2(maxposit), 4); + rangebits = Util.ilog2(maxposit); + + for (int j = 0, k = 0; j < info.partitions; j++) { + count += info.class_dim[info.partitionclass[j]]; + for (; k < count; k++) { + opb.write(info.postlist[k + 2], rangebits); + } + } + } + + Object unpack(Info vi, Buffer opb) { + int count = 0, maxclass = -1, rangebits; + InfoFloor1 info = new InfoFloor1(); + + /* read partitions */ + info.partitions = opb.read(5); /* only 0 to 31 legal */ + for (int j = 0; j < info.partitions; j++) { + info.partitionclass[j] = opb.read(4); /* only 0 to 15 legal */ + if (maxclass < info.partitionclass[j]) + maxclass = info.partitionclass[j]; + } + + /* read partition classes */ + for (int j = 0; j < maxclass + 1; j++) { + info.class_dim[j] = opb.read(3) + 1; /* 1 to 8 */ + info.class_subs[j] = opb.read(2); /* 0,1,2,3 bits */ + if (info.class_subs[j] < 0) { + info.free(); + return (null); + } + if (info.class_subs[j] != 0) { + info.class_book[j] = opb.read(8); + } + if (info.class_book[j] < 0 || info.class_book[j] >= vi.books) { + info.free(); + return (null); + } + for (int k = 0; k < (1 << info.class_subs[j]); k++) { + info.class_subbook[j][k] = opb.read(8) - 1; + if (info.class_subbook[j][k] < -1 || info.class_subbook[j][k] >= vi.books) { + info.free(); + return (null); + } + } + } + + /* read the post list */ + info.mult = opb.read(2) + 1; /* only 1,2,3,4 legal now */ + rangebits = opb.read(4); + + for (int j = 0, k = 0; j < info.partitions; j++) { + count += info.class_dim[info.partitionclass[j]]; + for (; k < count; k++) { + int t = info.postlist[k + 2] = opb.read(rangebits); + if (t < 0 || t >= (1 << rangebits)) { + info.free(); + return (null); + } + } + } + info.postlist[0] = 0; + info.postlist[1] = 1 << rangebits; + + return (info); + } + + Object look(DspState vd, InfoMode mi, Object i) { + int _n = 0; + + int[] sortpointer = new int[VIF_POSIT + 2]; + + // Info vi=vd.vi; + + InfoFloor1 info = (InfoFloor1) i; + LookFloor1 look = new LookFloor1(); + look.vi = info; + look.n = info.postlist[1]; + + /* + * we drop each position value in-between already decoded values, and use linear + * interpolation to predict each new value past the edges. The positions are + * read in the order of the position list... we precompute the bounding + * positions in the lookup. Of course, the neighbors can change (if a position + * is declined), but this is an initial mapping + */ + + for (int j = 0; j < info.partitions; j++) { + _n += info.class_dim[info.partitionclass[j]]; + } + _n += 2; + look.posts = _n; + + /* also store a sorted position index */ + for (int j = 0; j < _n; j++) { + sortpointer[j] = j; + } + // qsort(sortpointer,n,sizeof(int),icomp); // !! + + int foo; + for (int j = 0; j < _n - 1; j++) { + for (int k = j; k < _n; k++) { + if (info.postlist[sortpointer[j]] > info.postlist[sortpointer[k]]) { + foo = sortpointer[k]; + sortpointer[k] = sortpointer[j]; + sortpointer[j] = foo; + } + } + } + + /* points from sort order back to range number */ + for (int j = 0; j < _n; j++) { + look.forward_index[j] = sortpointer[j]; + } + /* points from range order to sorted position */ + for (int j = 0; j < _n; j++) { + look.reverse_index[look.forward_index[j]] = j; + } + /* we actually need the post values too */ + for (int j = 0; j < _n; j++) { + look.sorted_index[j] = info.postlist[look.forward_index[j]]; + } + + /* quantize values to multiplier spec */ + switch (info.mult) { + case 1: /* 1024 -> 256 */ + look.quant_q = 256; + break; + case 2: /* 1024 -> 128 */ + look.quant_q = 128; + break; + case 3: /* 1024 -> 86 */ + look.quant_q = 86; + break; + case 4: /* 1024 -> 64 */ + look.quant_q = 64; + break; + default: + look.quant_q = -1; + } + + /* + * discover our neighbors for decode where we don't use fit flags (that would + * push the neighbors outward) + */ + for (int j = 0; j < _n - 2; j++) { + int lo = 0; + int hi = 1; + int lx = 0; + int hx = look.n; + int currentx = info.postlist[j + 2]; + for (int k = 0; k < j + 2; k++) { + int x = info.postlist[k]; + if (x > lx && x < currentx) { + lo = k; + lx = x; + } + if (x < hx && x > currentx) { + hi = k; + hx = x; + } + } + look.loneighbor[j] = lo; + look.hineighbor[j] = hi; + } + + return look; + } + + void free_info(Object i) { + } + + void free_look(Object i) { + } + + void free_state(Object vs) { + } + + int forward(Block vb, Object i, float[] in, float[] out, Object vs) { + return 0; + } + + Object inverse1(Block vb, Object ii, Object memo) { + LookFloor1 look = (LookFloor1) ii; + InfoFloor1 info = look.vi; + CodeBook[] books = vb.vd.fullbooks; + + /* unpack wrapped/predicted values from stream */ + if (vb.opb.read(1) == 1) { + int[] fit_value = null; + if (memo instanceof int[]) { + fit_value = (int[]) memo; + } + if (fit_value == null || fit_value.length < look.posts) { + fit_value = new int[look.posts]; + } else { + for (int i = 0; i < fit_value.length; i++) + fit_value[i] = 0; + } + + fit_value[0] = vb.opb.read(Util.ilog(look.quant_q - 1)); + fit_value[1] = vb.opb.read(Util.ilog(look.quant_q - 1)); + + /* partition by partition */ + for (int i = 0, j = 2; i < info.partitions; i++) { + int clss = info.partitionclass[i]; + int cdim = info.class_dim[clss]; + int csubbits = info.class_subs[clss]; + int csub = 1 << csubbits; + int cval = 0; + + /* decode the partition's first stage cascade value */ + if (csubbits != 0) { + cval = books[info.class_book[clss]].decode(vb.opb); + + if (cval == -1) { + return (null); + } + } + + for (int k = 0; k < cdim; k++) { + int book = info.class_subbook[clss][cval & (csub - 1)]; + cval >>>= csubbits; + if (book >= 0) { + if ((fit_value[j + k] = books[book].decode(vb.opb)) == -1) { + return (null); + } + } else { + fit_value[j + k] = 0; + } + } + j += cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for (int i = 2; i < look.posts; i++) { + int predicted = render_point(info.postlist[look.loneighbor[i - 2]], + info.postlist[look.hineighbor[i - 2]], fit_value[look.loneighbor[i - 2]], + fit_value[look.hineighbor[i - 2]], info.postlist[i]); + int hiroom = look.quant_q - predicted; + int loroom = predicted; + int room = (hiroom < loroom ? hiroom : loroom) << 1; + int val = fit_value[i]; + + if (val != 0) { + if (val >= room) { + if (hiroom > loroom) { + val = val - loroom; + } else { + val = -1 - (val - hiroom); + } + } else { + if ((val & 1) != 0) { + val = -((val + 1) >>> 1); + } else { + val >>= 1; + } + } + + fit_value[i] = val + predicted; + fit_value[look.loneighbor[i - 2]] &= 0x7fff; + fit_value[look.hineighbor[i - 2]] &= 0x7fff; + } else { + fit_value[i] = predicted | 0x8000; + } + } + return (fit_value); + } + + return (null); + } + + private static int render_point(int x0, int x1, int y0, int y1, int x) { + y0 &= 0x7fff; /* mask off flag */ + y1 &= 0x7fff; + + { + int dy = y1 - y0; + int adx = x1 - x0; + int ady = Math.abs(dy); + int err = ady * (x - x0); + + int off = (int) (err / adx); + if (dy < 0) + return (y0 - off); + return (y0 + off); + } + } + + int inverse2(Block vb, Object i, Object memo, float[] out) { + LookFloor1 look = (LookFloor1) i; + InfoFloor1 info = look.vi; + int n = vb.vd.vi.blocksizes[vb.mode] / 2; + + if (memo != null) { + /* render the lines */ + int[] fit_value = (int[]) memo; + int hx = 0; + int lx = 0; + int ly = fit_value[0] * info.mult; + for (int j = 1; j < look.posts; j++) { + int current = look.forward_index[j]; + int hy = fit_value[current] & 0x7fff; + if (hy == fit_value[current]) { + hy *= info.mult; + hx = info.postlist[current]; + + render_line(lx, hx, ly, hy, out); + + lx = hx; + ly = hy; + } + } + for (int j = hx; j < n; j++) { + out[j] *= out[j - 1]; /* be certain */ + } + return (1); + } + for (int j = 0; j < n; j++) { + out[j] = 0.f; + } + return (0); + } + + private static float[] FLOOR_fromdB_LOOKUP = { 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, 1.7623575e-07F, 1.8768855e-07F, + 1.9988561e-07F, 2.128753e-07F, 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, 3.7516214e-07F, 3.9954229e-07F, + 4.2550680e-07F, 4.5315863e-07F, 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, 7.9862701e-07F, 8.5052630e-07F, + 9.0579828e-07F, 9.6466216e-07F, 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, 1.7000785e-06F, 1.8105592e-06F, + 1.9282195e-06F, 2.0535261e-06F, 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, 3.6190449e-06F, 3.8542308e-06F, + 4.1047004e-06F, 4.3714470e-06F, 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, 7.7040476e-06F, 8.2047000e-06F, + 8.7378876e-06F, 9.3057248e-06F, 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, 1.6400004e-05F, 1.7465768e-05F, + 1.8600792e-05F, 1.9809576e-05F, 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, 3.4911534e-05F, 3.7180282e-05F, + 3.9596466e-05F, 4.2169667e-05F, 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, 7.4317983e-05F, 7.9147585e-05F, + 8.4291040e-05F, 8.9768747e-05F, 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, 0.00015820453F, 0.00016848555F, + 0.00017943469F, 0.00019109536F, 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, 0.00033677814F, 0.00035866388F, + 0.00038197188F, 0.00040679456F, 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, 0.00071691700F, 0.00076350630F, + 0.00081312324F, 0.00086596457F, 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, 0.0011863665F, + 0.0012634633F, 0.0013455702F, 0.0014330129F, 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, 0.0025254795F, 0.0026895994F, 0.0028643847F, + 0.0030505286F, 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, 0.0041792066F, 0.0044507950F, + 0.0047400328F, 0.0050480668F, 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, 0.0069158225F, + 0.0073652516F, 0.0078438871F, 0.0083536271F, 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, 0.014722068F, 0.015678791F, 0.016697687F, + 0.017782797F, 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, 0.024362330F, 0.025945531F, + 0.027631618F, 0.029427276F, 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, 0.040315199F, + 0.042935108F, 0.045725273F, 0.048696758F, 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, 0.085821044F, 0.091398179F, 0.097337747F, + 0.10366330F, 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, 0.14201813F, 0.15124727F, 0.16107617F, + 0.17154380F, 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, 0.23501402F, 0.25028656F, 0.26655159F, + 0.28387361F, 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, 0.38890521F, 0.41417847F, 0.44109412F, + 0.46975890F, 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, 0.64356699F, 0.68538959F, 0.72993007F, + 0.77736504F, 0.82788260F, 0.88168307F, 0.9389798F, 1.F }; + + private static void render_line(int x0, int x1, int y0, int y1, float[] d) { + int dy = y1 - y0; + int adx = x1 - x0; + int ady = Math.abs(dy); + int base = dy / adx; + int sy = (dy < 0 ? base - 1 : base + 1); + int x = x0; + int y = y0; + int err = 0; + + ady -= Math.abs(base * adx); + + d[x] *= FLOOR_fromdB_LOOKUP[y]; + while (++x < x1) { + err = err + ady; + if (err >= adx) { + err -= adx; + y += sy; + } else { + y += base; + } + d[x] *= FLOOR_fromdB_LOOKUP[y]; + } + } + + class InfoFloor1 { + static final int VIF_POSIT = 63; + static final int VIF_CLASS = 16; + static final int VIF_PARTS = 31; + + int partitions; /* 0 to 31 */ + int[] partitionclass = new int[VIF_PARTS]; /* 0 to 15 */ + + int[] class_dim = new int[VIF_CLASS]; /* 1 to 8 */ + int[] class_subs = new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1< + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncFloor { + + public static FuncFloor[] floor_P = { new Floor0(), new Floor1() }; + + abstract void pack(Object i, Buffer opb); + + abstract Object unpack(Info vi, Buffer opb); + + abstract Object look(DspState vd, InfoMode mi, Object i); + + abstract void free_info(Object i); + + abstract void free_look(Object i); + + abstract void free_state(Object vs); + + abstract int forward(Block vb, Object i, float[] in, float[] out, Object vs); + + abstract Object inverse1(Block vb, Object i, Object memo); + + abstract int inverse2(Block vb, Object i, Object memo, float[] out); +} diff --git a/src/teavm/java/com/jcraft/jorbis/FuncMapping.java b/src/teavm/java/com/jcraft/jorbis/FuncMapping.java new file mode 100755 index 0000000..1a83098 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/FuncMapping.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncMapping { + public static FuncMapping[] mapping_P = { new Mapping0() }; + + abstract void pack(Info info, Object imap, Buffer buffer); + + abstract Object unpack(Info info, Buffer buffer); + + abstract Object look(DspState vd, InfoMode vm, Object m); + + abstract void free_info(Object imap); + + abstract void free_look(Object imap); + + abstract int inverse(Block vd, Object lm); +} diff --git a/src/teavm/java/com/jcraft/jorbis/FuncResidue.java b/src/teavm/java/com/jcraft/jorbis/FuncResidue.java new file mode 100755 index 0000000..33ab1e2 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/FuncResidue.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncResidue { + public static FuncResidue[] residue_P = { new Residue0(), new Residue1(), new Residue2() }; + + abstract void pack(Object vr, Buffer opb); + + abstract Object unpack(Info vi, Buffer opb); + + abstract Object look(DspState vd, InfoMode vm, Object vr); + + abstract void free_info(Object i); + + abstract void free_look(Object i); + + abstract int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch); +} diff --git a/src/teavm/java/com/jcraft/jorbis/FuncTime.java b/src/teavm/java/com/jcraft/jorbis/FuncTime.java new file mode 100755 index 0000000..019b634 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/FuncTime.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncTime { + public static FuncTime[] time_P = { new Time0() }; + + abstract void pack(Object i, Buffer opb); + + abstract Object unpack(Info vi, Buffer opb); + + abstract Object look(DspState vd, InfoMode vm, Object i); + + abstract void free_info(Object i); + + abstract void free_look(Object i); + + abstract int inverse(Block vb, Object i, float[] in, float[] out); +} diff --git a/src/teavm/java/com/jcraft/jorbis/Info.java b/src/teavm/java/com/jcraft/jorbis/Info.java new file mode 100755 index 0000000..e0f0285 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Info.java @@ -0,0 +1,468 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +public class Info { + private static final int OV_EBADPACKET = -136; + private static final int OV_ENOTAUDIO = -135; + + private static byte[] _vorbis = "vorbis".getBytes(); + private static final int VI_TIMEB = 1; + // private static final int VI_FLOORB=1; + private static final int VI_FLOORB = 2; + // private static final int VI_RESB=1; + private static final int VI_RESB = 3; + private static final int VI_MAPB = 1; + private static final int VI_WINDOWB = 1; + + public int version; + public int channels; + public int rate; + + // The below bitrate declarations are *hints*. + // Combinations of the three values carry the following implications: + // + // all three set to the same value: + // implies a fixed rate bitstream + // only nominal set: + // implies a VBR stream that averages the nominal bitrate. No hard + // upper/lower limit + // upper and or lower set: + // implies a VBR bitstream that obeys the bitrate limits. nominal + // may also be set to give a nominal rate. + // none set: + // the coder does not care to speculate. + + int bitrate_upper; + int bitrate_nominal; + int bitrate_lower; + + // Vorbis supports only short and long blocks, but allows the + // encoder to choose the sizes + + int[] blocksizes = new int[2]; + + // modes are the primary means of supporting on-the-fly different + // blocksizes, different channel mappings (LR or mid-side), + // different residue backends, etc. Each mode consists of a + // blocksize flag and a mapping (along with the mapping setup + + int modes; + int maps; + int times; + int floors; + int residues; + int books; + int psys; // encode only + + InfoMode[] mode_param = null; + + int[] map_type = null; + Object[] map_param = null; + + int[] time_type = null; + Object[] time_param = null; + + int[] floor_type = null; + Object[] floor_param = null; + + int[] residue_type = null; + Object[] residue_param = null; + + StaticCodeBook[] book_param = null; + + PsyInfo[] psy_param = new PsyInfo[64]; // encode only + + // for block long/sort tuning; encode only + int envelopesa; + float preecho_thresh; + float preecho_clamp; + + // used by synthesis, which has a full, alloced vi + public void init() { + rate = 0; + } + + public void clear() { + for (int i = 0; i < modes; i++) { + mode_param[i] = null; + } + mode_param = null; + + for (int i = 0; i < maps; i++) { // unpack does the range checking + FuncMapping.mapping_P[map_type[i]].free_info(map_param[i]); + } + map_param = null; + + for (int i = 0; i < times; i++) { // unpack does the range checking + FuncTime.time_P[time_type[i]].free_info(time_param[i]); + } + time_param = null; + + for (int i = 0; i < floors; i++) { // unpack does the range checking + FuncFloor.floor_P[floor_type[i]].free_info(floor_param[i]); + } + floor_param = null; + + for (int i = 0; i < residues; i++) { // unpack does the range checking + FuncResidue.residue_P[residue_type[i]].free_info(residue_param[i]); + } + residue_param = null; + + // the static codebooks *are* freed if you call info_clear, because + // decode side does alloc a 'static' codebook. Calling clear on the + // full codebook does not clear the static codebook (that's our + // responsibility) + for (int i = 0; i < books; i++) { + // just in case the decoder pre-cleared to save space + if (book_param[i] != null) { + book_param[i].clear(); + book_param[i] = null; + } + } + // if(vi->book_param)free(vi->book_param); + book_param = null; + + for (int i = 0; i < psys; i++) { + psy_param[i].free(); + } + + } + + // Header packing/unpacking + int unpack_info(Buffer opb) { + version = opb.read(32); + if (version != 0) + return (-1); + + channels = opb.read(8); + rate = opb.read(32); + + bitrate_upper = opb.read(32); + bitrate_nominal = opb.read(32); + bitrate_lower = opb.read(32); + + blocksizes[0] = 1 << opb.read(4); + blocksizes[1] = 1 << opb.read(4); + + if ((rate < 1) || (channels < 1) || (blocksizes[0] < 8) || (blocksizes[1] < blocksizes[0]) + || (opb.read(1) != 1)) { + clear(); + return (-1); + } + return (0); + } + + // all of the real encoding details are here. The modes, books, + // everything + int unpack_books(Buffer opb) { + + books = opb.read(8) + 1; + + if (book_param == null || book_param.length != books) + book_param = new StaticCodeBook[books]; + for (int i = 0; i < books; i++) { + book_param[i] = new StaticCodeBook(); + if (book_param[i].unpack(opb) != 0) { + clear(); + return (-1); + } + } + + // time backend settings + times = opb.read(6) + 1; + if (time_type == null || time_type.length != times) + time_type = new int[times]; + if (time_param == null || time_param.length != times) + time_param = new Object[times]; + for (int i = 0; i < times; i++) { + time_type[i] = opb.read(16); + if (time_type[i] < 0 || time_type[i] >= VI_TIMEB) { + clear(); + return (-1); + } + time_param[i] = FuncTime.time_P[time_type[i]].unpack(this, opb); + if (time_param[i] == null) { + clear(); + return (-1); + } + } + + // floor backend settings + floors = opb.read(6) + 1; + if (floor_type == null || floor_type.length != floors) + floor_type = new int[floors]; + if (floor_param == null || floor_param.length != floors) + floor_param = new Object[floors]; + + for (int i = 0; i < floors; i++) { + floor_type[i] = opb.read(16); + if (floor_type[i] < 0 || floor_type[i] >= VI_FLOORB) { + clear(); + return (-1); + } + + floor_param[i] = FuncFloor.floor_P[floor_type[i]].unpack(this, opb); + if (floor_param[i] == null) { + clear(); + return (-1); + } + } + + // residue backend settings + residues = opb.read(6) + 1; + + if (residue_type == null || residue_type.length != residues) + residue_type = new int[residues]; + + if (residue_param == null || residue_param.length != residues) + residue_param = new Object[residues]; + + for (int i = 0; i < residues; i++) { + residue_type[i] = opb.read(16); + if (residue_type[i] < 0 || residue_type[i] >= VI_RESB) { + clear(); + return (-1); + } + residue_param[i] = FuncResidue.residue_P[residue_type[i]].unpack(this, opb); + if (residue_param[i] == null) { + clear(); + return (-1); + } + } + + // map backend settings + maps = opb.read(6) + 1; + if (map_type == null || map_type.length != maps) + map_type = new int[maps]; + if (map_param == null || map_param.length != maps) + map_param = new Object[maps]; + for (int i = 0; i < maps; i++) { + map_type[i] = opb.read(16); + if (map_type[i] < 0 || map_type[i] >= VI_MAPB) { + clear(); + return (-1); + } + map_param[i] = FuncMapping.mapping_P[map_type[i]].unpack(this, opb); + if (map_param[i] == null) { + clear(); + return (-1); + } + } + + // mode settings + modes = opb.read(6) + 1; + if (mode_param == null || mode_param.length != modes) + mode_param = new InfoMode[modes]; + for (int i = 0; i < modes; i++) { + mode_param[i] = new InfoMode(); + mode_param[i].blockflag = opb.read(1); + mode_param[i].windowtype = opb.read(16); + mode_param[i].transformtype = opb.read(16); + mode_param[i].mapping = opb.read(8); + + if ((mode_param[i].windowtype >= VI_WINDOWB) || (mode_param[i].transformtype >= VI_WINDOWB) + || (mode_param[i].mapping >= maps)) { + clear(); + return (-1); + } + } + + if (opb.read(1) != 1) { + clear(); + return (-1); + } + + return (0); + } + + // The Vorbis header is in three packets; the initial small packet in + // the first page that identifies basic parameters, a second packet + // with bitstream comments and a third packet that holds the + // codebook. + + public int synthesis_headerin(Comment vc, Packet op) { + Buffer opb = new Buffer(); + + if (op != null) { + opb.readinit(op.packet_base, op.packet, op.bytes); + + // Which of the three types of header is this? + // Also verify header-ness, vorbis + { + byte[] buffer = new byte[6]; + int packtype = opb.read(8); + opb.read(buffer, 6); + if (buffer[0] != 'v' || buffer[1] != 'o' || buffer[2] != 'r' || buffer[3] != 'b' || buffer[4] != 'i' + || buffer[5] != 's') { + // not a vorbis header + return (-1); + } + switch (packtype) { + case 0x01: // least significant *bit* is read first + if (op.b_o_s == 0) { + // Not the initial packet + return (-1); + } + if (rate != 0) { + // previously initialized info header + return (-1); + } + return (unpack_info(opb)); + case 0x03: // least significant *bit* is read first + if (rate == 0) { + // um... we didn't get the initial header + return (-1); + } + return (vc.unpack(opb)); + case 0x05: // least significant *bit* is read first + if (rate == 0 || vc.vendor == null) { + // um... we didn;t get the initial header or comments yet + return (-1); + } + return (unpack_books(opb)); + default: + // Not a valid vorbis header type + // return(-1); + break; + } + } + } + return (-1); + } + + // pack side + int pack_info(Buffer opb) { + // preamble + opb.write(0x01, 8); + opb.write(_vorbis); + + // basic information about the stream + opb.write(0x00, 32); + opb.write(channels, 8); + opb.write(rate, 32); + + opb.write(bitrate_upper, 32); + opb.write(bitrate_nominal, 32); + opb.write(bitrate_lower, 32); + + opb.write(Util.ilog2(blocksizes[0]), 4); + opb.write(Util.ilog2(blocksizes[1]), 4); + opb.write(1, 1); + return (0); + } + + int pack_books(Buffer opb) { + opb.write(0x05, 8); + opb.write(_vorbis); + + // books + opb.write(books - 1, 8); + for (int i = 0; i < books; i++) { + if (book_param[i].pack(opb) != 0) { + // goto err_out; + return (-1); + } + } + + // times + opb.write(times - 1, 6); + for (int i = 0; i < times; i++) { + opb.write(time_type[i], 16); + FuncTime.time_P[time_type[i]].pack(this.time_param[i], opb); + } + + // floors + opb.write(floors - 1, 6); + for (int i = 0; i < floors; i++) { + opb.write(floor_type[i], 16); + FuncFloor.floor_P[floor_type[i]].pack(floor_param[i], opb); + } + + // residues + opb.write(residues - 1, 6); + for (int i = 0; i < residues; i++) { + opb.write(residue_type[i], 16); + FuncResidue.residue_P[residue_type[i]].pack(residue_param[i], opb); + } + + // maps + opb.write(maps - 1, 6); + for (int i = 0; i < maps; i++) { + opb.write(map_type[i], 16); + FuncMapping.mapping_P[map_type[i]].pack(this, map_param[i], opb); + } + + // modes + opb.write(modes - 1, 6); + for (int i = 0; i < modes; i++) { + opb.write(mode_param[i].blockflag, 1); + opb.write(mode_param[i].windowtype, 16); + opb.write(mode_param[i].transformtype, 16); + opb.write(mode_param[i].mapping, 8); + } + opb.write(1, 1); + return (0); + } + + public int blocksize(Packet op) { + // codec_setup_info + Buffer opb = new Buffer(); + + int mode; + + opb.readinit(op.packet_base, op.packet, op.bytes); + + /* Check the packet type */ + if (opb.read(1) != 0) { + /* Oops. This is not an audio data packet */ + return (OV_ENOTAUDIO); + } + { + int modebits = 0; + int v = modes; + while (v > 1) { + modebits++; + v >>>= 1; + } + + /* read our mode and pre/post windowsize */ + mode = opb.read(modebits); + } + if (mode == -1) + return (OV_EBADPACKET); + return (blocksizes[mode_param[mode].blockflag]); + } + + public String toString() { + return "version:" + version + ", channels:" + channels + ", rate:" + rate + + ", bitrate:" + bitrate_upper + "," + bitrate_nominal + "," + + bitrate_lower; + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/InfoMode.java b/src/teavm/java/com/jcraft/jorbis/InfoMode.java new file mode 100755 index 0000000..e7f203c --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/InfoMode.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class InfoMode { + int blockflag; + int windowtype; + int transformtype; + int mapping; +} diff --git a/src/teavm/java/com/jcraft/jorbis/JOrbisException.java b/src/teavm/java/com/jcraft/jorbis/JOrbisException.java new file mode 100755 index 0000000..7862e98 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/JOrbisException.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +public class JOrbisException extends Exception { + + private static final long serialVersionUID = 1L; + + public JOrbisException() { + super(); + } + + public JOrbisException(String s) { + super("JOrbis: " + s); + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Lookup.java b/src/teavm/java/com/jcraft/jorbis/Lookup.java new file mode 100755 index 0000000..5accc93 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Lookup.java @@ -0,0 +1,122 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class Lookup { + static final int COS_LOOKUP_SZ = 128; + static final float[] COS_LOOKUP = { +1.0000000000000f, +0.9996988186962f, +0.9987954562052f, +0.9972904566787f, + +0.9951847266722f, +0.9924795345987f, +0.9891765099648f, +0.9852776423889f, +0.9807852804032f, + +0.9757021300385f, +0.9700312531945f, +0.9637760657954f, +0.9569403357322f, +0.9495281805930f, + +0.9415440651830f, +0.9329927988347f, +0.9238795325113f, +0.9142097557035f, +0.9039892931234f, + +0.8932243011955f, +0.8819212643484f, +0.8700869911087f, +0.8577286100003f, +0.8448535652497f, + +0.8314696123025f, +0.8175848131516f, +0.8032075314806f, +0.7883464276266f, +0.7730104533627f, + +0.7572088465065f, +0.7409511253550f, +0.7242470829515f, +0.7071067811865f, +0.6895405447371f, + +0.6715589548470f, +0.6531728429538f, +0.6343932841636f, +0.6152315905806f, +0.5956993044924f, + +0.5758081914178f, +0.5555702330196f, +0.5349976198871f, +0.5141027441932f, +0.4928981922298f, + +0.4713967368260f, +0.4496113296546f, +0.4275550934303f, +0.4052413140050f, +0.3826834323651f, + +0.3598950365350f, +0.3368898533922f, +0.3136817403989f, +0.2902846772545f, +0.2667127574749f, + +0.2429801799033f, +0.2191012401569f, +0.1950903220161f, +0.1709618887603f, +0.1467304744554f, + +0.1224106751992f, +0.0980171403296f, +0.0735645635997f, +0.0490676743274f, +0.0245412285229f, + +0.0000000000000f, -0.0245412285229f, -0.0490676743274f, -0.0735645635997f, -0.0980171403296f, + -0.1224106751992f, -0.1467304744554f, -0.1709618887603f, -0.1950903220161f, -0.2191012401569f, + -0.2429801799033f, -0.2667127574749f, -0.2902846772545f, -0.3136817403989f, -0.3368898533922f, + -0.3598950365350f, -0.3826834323651f, -0.4052413140050f, -0.4275550934303f, -0.4496113296546f, + -0.4713967368260f, -0.4928981922298f, -0.5141027441932f, -0.5349976198871f, -0.5555702330196f, + -0.5758081914178f, -0.5956993044924f, -0.6152315905806f, -0.6343932841636f, -0.6531728429538f, + -0.6715589548470f, -0.6895405447371f, -0.7071067811865f, -0.7242470829515f, -0.7409511253550f, + -0.7572088465065f, -0.7730104533627f, -0.7883464276266f, -0.8032075314806f, -0.8175848131516f, + -0.8314696123025f, -0.8448535652497f, -0.8577286100003f, -0.8700869911087f, -0.8819212643484f, + -0.8932243011955f, -0.9039892931234f, -0.9142097557035f, -0.9238795325113f, -0.9329927988347f, + -0.9415440651830f, -0.9495281805930f, -0.9569403357322f, -0.9637760657954f, -0.9700312531945f, + -0.9757021300385f, -0.9807852804032f, -0.9852776423889f, -0.9891765099648f, -0.9924795345987f, + -0.9951847266722f, -0.9972904566787f, -0.9987954562052f, -0.9996988186962f, -1.0000000000000f, }; + + /* interpolated lookup based cos function, domain 0 to PI only */ + static float coslook(float a) { + double d = a * (.31830989 * (float) COS_LOOKUP_SZ); + int i = (int) d; + return COS_LOOKUP[i] + ((float) (d - i)) * (COS_LOOKUP[i + 1] - COS_LOOKUP[i]); + } + + static final int INVSQ_LOOKUP_SZ = 32; + static final float[] INVSQ_LOOKUP = { 1.414213562373f, 1.392621247646f, 1.371988681140f, 1.352246807566f, + 1.333333333333f, 1.315191898443f, 1.297771369046f, 1.281025230441f, 1.264911064067f, 1.249390095109f, + 1.234426799697f, 1.219988562661f, 1.206045378311f, 1.192569588000f, 1.179535649239f, 1.166919931983f, + 1.154700538379f, 1.142857142857f, 1.131370849898f, 1.120224067222f, 1.109400392450f, 1.098884511590f, + 1.088662107904f, 1.078719779941f, 1.069044967650f, 1.059625885652f, 1.050451462878f, 1.041511287847f, + 1.032795558989f, 1.024295039463f, 1.016001016002f, 1.007905261358f, 1.000000000000f, }; + + /* interpolated 1./sqrt(p) where .5 <= p < 1. */ + static float invsqlook(float a) { + double d = a * (2.f * (float) INVSQ_LOOKUP_SZ) - (float) INVSQ_LOOKUP_SZ; + int i = (int) d; + return INVSQ_LOOKUP[i] + ((float) (d - i)) * (INVSQ_LOOKUP[i + 1] - INVSQ_LOOKUP[i]); + } + + static final int INVSQ2EXP_LOOKUP_MIN = -32; + static final int INVSQ2EXP_LOOKUP_MAX = 32; + static final float[] INVSQ2EXP_LOOKUP = { 65536.f, 46340.95001f, 32768.f, 23170.47501f, 16384.f, 11585.2375f, + 8192.f, 5792.618751f, 4096.f, 2896.309376f, 2048.f, 1448.154688f, 1024.f, 724.0773439f, 512.f, 362.038672f, + 256.f, 181.019336f, 128.f, 90.50966799f, 64.f, 45.254834f, 32.f, 22.627417f, 16.f, 11.3137085f, 8.f, + 5.656854249f, 4.f, 2.828427125f, 2.f, 1.414213562f, 1.f, 0.7071067812f, 0.5f, 0.3535533906f, 0.25f, + 0.1767766953f, 0.125f, 0.08838834765f, 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, 0.015625f, + 0.01104854346f, 0.0078125f, 0.005524271728f, 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f, + 0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, 0.000244140625f, 0.0001726334915f, + 0.0001220703125f, 8.631674575e-05f, 6.103515625e-05f, 4.315837288e-05f, 3.051757812e-05f, 2.157918644e-05f, + 1.525878906e-05f, }; + + /* interpolated 1./sqrt(p) where .5 <= p < 1. */ + static float invsq2explook(int a) { + return INVSQ2EXP_LOOKUP[a - INVSQ2EXP_LOOKUP_MIN]; + } + + static final int FROMdB_LOOKUP_SZ = 35; + static final int FROMdB2_LOOKUP_SZ = 32; + static final int FROMdB_SHIFT = 5; + static final int FROMdB2_SHIFT = 3; + static final int FROMdB2_MASK = 31; + static final float[] FROMdB_LOOKUP = { 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, 0.1584893192f, 0.1f, + 0.06309573445f, 0.03981071706f, 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, 0.003981071706f, + 0.002511886432f, 0.001584893192f, 0.001f, 0.0006309573445f, 0.0003981071706f, 0.0002511886432f, + 0.0001584893192f, 0.0001f, 6.309573445e-05f, 3.981071706e-05f, 2.511886432e-05f, 1.584893192e-05f, 1e-05f, + 6.309573445e-06f, 3.981071706e-06f, 2.511886432e-06f, 1.584893192e-06f, 1e-06f, 6.309573445e-07f, + 3.981071706e-07f, 2.511886432e-07f, 1.584893192e-07f, }; + static final float[] FROMdB2_LOOKUP = { 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, 0.9372921937f, + 0.92390007f, 0.9106992942f, 0.8976871324f, 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f, + 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, 0.7886330981f, 0.7773650302f, 0.7662579617f, + 0.755309592f, 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, 0.7028699885f, 0.6928273125f, + 0.6829281272f, 0.6731703824f, 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, }; + + /* interpolated lookup based fromdB function, domain -140dB to 0dB only */ + static float fromdBlook(float a) { + int i = (int) (a * ((float) (-(1 << FROMdB2_SHIFT)))); + return (i < 0) ? 1.f + : ((i >= (FROMdB_LOOKUP_SZ << FROMdB_SHIFT)) ? 0.f + : FROMdB_LOOKUP[i >>> FROMdB_SHIFT] * FROMdB2_LOOKUP[i & FROMdB2_MASK]); + } + +} diff --git a/src/teavm/java/com/jcraft/jorbis/Lpc.java b/src/teavm/java/com/jcraft/jorbis/Lpc.java new file mode 100755 index 0000000..4160c50 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Lpc.java @@ -0,0 +1,185 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class Lpc { + // en/decode lookups + Drft fft = new Drft();; + + int ln; + int m; + + // Autocorrelation LPC coeff generation algorithm invented by + // N. Levinson in 1947, modified by J. Durbin in 1959. + + // Input : n elements of time doamin data + // Output: m lpc coefficients, excitation energy + + static float lpc_from_data(float[] data, float[] lpc, int n, int m) { + float[] aut = new float[m + 1]; + float error; + int i, j; + + // autocorrelation, p+1 lag coefficients + + j = m + 1; + while (j-- != 0) { + float d = 0; + for (i = j; i < n; i++) + d += data[i] * data[i - j]; + aut[j] = d; + } + + // Generate lpc coefficients from autocorr values + + error = aut[0]; + /* + * if(error==0){ for(int k=0; k + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +/* + function: LSP (also called LSF) conversion routines + + The LSP generation code is taken (with minimal modification) from + "On the Computation of the LSP Frequencies" by Joseph Rothweiler + , available at: + + http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html + ********************************************************************/ + +class Lsp { + + static final float M_PI = (float) (3.1415926539); + + static void lsp_to_curve(float[] curve, int[] map, int n, int ln, float[] lsp, int m, float amp, float ampoffset) { + int i; + float wdel = M_PI / ln; + for (i = 0; i < m; i++) + lsp[i] = Lookup.coslook(lsp[i]); + int m2 = (m / 2) * 2; + + i = 0; + while (i < n) { + int k = map[i]; + float p = .7071067812f; + float q = .7071067812f; + float w = Lookup.coslook(wdel * k); + + for (int j = 0; j < m2; j += 2) { + q *= lsp[j] - w; + p *= lsp[j + 1] - w; + } + + if ((m & 1) != 0) { + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + q *= lsp[m - 1] - w; + q *= q; + p *= p * (1.f - w * w); + } else { + /* even order filter; still symmetric */ + q *= q * (1.f + w); + p *= p * (1.f - w); + } + + // q=frexp(p+q,&qexp); + q = p + q; + int hx = Float.floatToIntBits(q); + int ix = 0x7fffffff & hx; + int qexp = 0; + + if (ix >= 0x7f800000 || (ix == 0)) { + // 0,inf,nan + } else { + if (ix < 0x00800000) { // subnormal + q *= 3.3554432000e+07; // 0x4c000000 + hx = Float.floatToIntBits(q); + ix = 0x7fffffff & hx; + qexp = -25; + } + qexp += ((ix >>> 23) - 126); + hx = (hx & 0x807fffff) | 0x3f000000; + q = Float.intBitsToFloat(hx); + } + + q = Lookup.fromdBlook(amp * Lookup.invsqlook(q) * Lookup.invsq2explook(qexp + m) - ampoffset); + + do { + curve[i++] *= q; + } while (i < n && map[i] == k); + + } + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Mapping0.java b/src/teavm/java/com/jcraft/jorbis/Mapping0.java new file mode 100755 index 0000000..005562c --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Mapping0.java @@ -0,0 +1,361 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Mapping0 extends FuncMapping { + static int seq = 0; + + void free_info(Object imap) { + }; + + void free_look(Object imap) { + } + + Object look(DspState vd, InfoMode vm, Object m) { + // System.err.println("Mapping0.look"); + Info vi = vd.vi; + LookMapping0 look = new LookMapping0(); + InfoMapping0 info = look.map = (InfoMapping0) m; + look.mode = vm; + + look.time_look = new Object[info.submaps]; + look.floor_look = new Object[info.submaps]; + look.residue_look = new Object[info.submaps]; + + look.time_func = new FuncTime[info.submaps]; + look.floor_func = new FuncFloor[info.submaps]; + look.residue_func = new FuncResidue[info.submaps]; + + for (int i = 0; i < info.submaps; i++) { + int timenum = info.timesubmap[i]; + int floornum = info.floorsubmap[i]; + int resnum = info.residuesubmap[i]; + + look.time_func[i] = FuncTime.time_P[vi.time_type[timenum]]; + look.time_look[i] = look.time_func[i].look(vd, vm, vi.time_param[timenum]); + look.floor_func[i] = FuncFloor.floor_P[vi.floor_type[floornum]]; + look.floor_look[i] = look.floor_func[i].look(vd, vm, vi.floor_param[floornum]); + look.residue_func[i] = FuncResidue.residue_P[vi.residue_type[resnum]]; + look.residue_look[i] = look.residue_func[i].look(vd, vm, vi.residue_param[resnum]); + + } + + if (vi.psys != 0 && vd.analysisp != 0) { + // ?? + } + + look.ch = vi.channels; + + return (look); + } + + void pack(Info vi, Object imap, Buffer opb) { + InfoMapping0 info = (InfoMapping0) imap; + + /* + * another 'we meant to do it this way' hack... up to beta 4, we packed 4 binary + * zeros here to signify one submapping in use. We now redefine that to mean + * four bitflags that indicate use of deeper features; bit0:submappings, + * bit1:coupling, bit2,3:reserved. This is backward compatable with all actual + * uses of the beta code. + */ + + if (info.submaps > 1) { + opb.write(1, 1); + opb.write(info.submaps - 1, 4); + } else { + opb.write(0, 1); + } + + if (info.coupling_steps > 0) { + opb.write(1, 1); + opb.write(info.coupling_steps - 1, 8); + for (int i = 0; i < info.coupling_steps; i++) { + opb.write(info.coupling_mag[i], Util.ilog2(vi.channels)); + opb.write(info.coupling_ang[i], Util.ilog2(vi.channels)); + } + } else { + opb.write(0, 1); + } + + opb.write(0, 2); /* 2,3:reserved */ + + /* we don't write the channel submappings if we only have one... */ + if (info.submaps > 1) { + for (int i = 0; i < vi.channels; i++) + opb.write(info.chmuxlist[i], 4); + } + for (int i = 0; i < info.submaps; i++) { + opb.write(info.timesubmap[i], 8); + opb.write(info.floorsubmap[i], 8); + opb.write(info.residuesubmap[i], 8); + } + } + + // also responsible for range checking + Object unpack(Info vi, Buffer opb) { + InfoMapping0 info = new InfoMapping0(); + + if (opb.read(1) != 0) { + info.submaps = opb.read(4) + 1; + } else { + info.submaps = 1; + } + + if (opb.read(1) != 0) { + info.coupling_steps = opb.read(8) + 1; + + for (int i = 0; i < info.coupling_steps; i++) { + int testM = info.coupling_mag[i] = opb.read(Util.ilog2(vi.channels)); + int testA = info.coupling_ang[i] = opb.read(Util.ilog2(vi.channels)); + + if (testM < 0 || testA < 0 || testM == testA || testM >= vi.channels || testA >= vi.channels) { + // goto err_out; + info.free(); + return (null); + } + } + } + + if (opb.read(2) > 0) { /* 2,3:reserved */ + info.free(); + return (null); + } + + if (info.submaps > 1) { + for (int i = 0; i < vi.channels; i++) { + info.chmuxlist[i] = opb.read(4); + if (info.chmuxlist[i] >= info.submaps) { + info.free(); + return (null); + } + } + } + + for (int i = 0; i < info.submaps; i++) { + info.timesubmap[i] = opb.read(8); + if (info.timesubmap[i] >= vi.times) { + info.free(); + return (null); + } + info.floorsubmap[i] = opb.read(8); + if (info.floorsubmap[i] >= vi.floors) { + info.free(); + return (null); + } + info.residuesubmap[i] = opb.read(8); + if (info.residuesubmap[i] >= vi.residues) { + info.free(); + return (null); + } + } + return info; + } + + float[][] pcmbundle = null; + int[] zerobundle = null; + int[] nonzero = null; + Object[] floormemo = null; + + synchronized int inverse(Block vb, Object l) { + DspState vd = vb.vd; + Info vi = vd.vi; + LookMapping0 look = (LookMapping0) l; + InfoMapping0 info = look.map; + InfoMode mode = look.mode; + int n = vb.pcmend = vi.blocksizes[vb.W]; + + float[] window = vd.window[vb.W][vb.lW][vb.nW][mode.windowtype]; + if (pcmbundle == null || pcmbundle.length < vi.channels) { + pcmbundle = new float[vi.channels][]; + nonzero = new int[vi.channels]; + zerobundle = new int[vi.channels]; + floormemo = new Object[vi.channels]; + } + + // time domain information decode (note that applying the + // information would have to happen later; we'll probably add a + // function entry to the harness for that later + // NOT IMPLEMENTED + + // recover the spectral envelope; store it in the PCM vector for now + for (int i = 0; i < vi.channels; i++) { + float[] pcm = vb.pcm[i]; + int submap = info.chmuxlist[i]; + + floormemo[i] = look.floor_func[submap].inverse1(vb, look.floor_look[submap], floormemo[i]); + if (floormemo[i] != null) { + nonzero[i] = 1; + } else { + nonzero[i] = 0; + } + for (int j = 0; j < n / 2; j++) { + pcm[j] = 0; + } + + } + + for (int i = 0; i < info.coupling_steps; i++) { + if (nonzero[info.coupling_mag[i]] != 0 || nonzero[info.coupling_ang[i]] != 0) { + nonzero[info.coupling_mag[i]] = 1; + nonzero[info.coupling_ang[i]] = 1; + } + } + + // recover the residue, apply directly to the spectral envelope + + for (int i = 0; i < info.submaps; i++) { + int ch_in_bundle = 0; + for (int j = 0; j < vi.channels; j++) { + if (info.chmuxlist[j] == i) { + if (nonzero[j] != 0) { + zerobundle[ch_in_bundle] = 1; + } else { + zerobundle[ch_in_bundle] = 0; + } + pcmbundle[ch_in_bundle++] = vb.pcm[j]; + } + } + + look.residue_func[i].inverse(vb, look.residue_look[i], pcmbundle, zerobundle, ch_in_bundle); + } + + for (int i = info.coupling_steps - 1; i >= 0; i--) { + float[] pcmM = vb.pcm[info.coupling_mag[i]]; + float[] pcmA = vb.pcm[info.coupling_ang[i]]; + + for (int j = 0; j < n / 2; j++) { + float mag = pcmM[j]; + float ang = pcmA[j]; + + if (mag > 0) { + if (ang > 0) { + pcmM[j] = mag; + pcmA[j] = mag - ang; + } else { + pcmA[j] = mag; + pcmM[j] = mag + ang; + } + } else { + if (ang > 0) { + pcmM[j] = mag; + pcmA[j] = mag + ang; + } else { + pcmA[j] = mag; + pcmM[j] = mag - ang; + } + } + } + } + + // /* compute and apply spectral envelope */ + + for (int i = 0; i < vi.channels; i++) { + float[] pcm = vb.pcm[i]; + int submap = info.chmuxlist[i]; + look.floor_func[submap].inverse2(vb, look.floor_look[submap], floormemo[i], pcm); + } + + // transform the PCM data; takes PCM vector, vb; modifies PCM vector + // only MDCT right now.... + + for (int i = 0; i < vi.channels; i++) { + float[] pcm = vb.pcm[i]; + // _analysis_output("out",seq+i,pcm,n/2,0,0); + ((Mdct) vd.transform[vb.W][0]).backward(pcm, pcm); + } + + // now apply the decoded pre-window time information + // NOT IMPLEMENTED + + // window the data + for (int i = 0; i < vi.channels; i++) { + float[] pcm = vb.pcm[i]; + if (nonzero[i] != 0) { + for (int j = 0; j < n; j++) { + pcm[j] *= window[j]; + } + } else { + for (int j = 0; j < n; j++) { + pcm[j] = 0.f; + } + } + } + + // now apply the decoded post-window time information + // NOT IMPLEMENTED + // all done! + return (0); + } + + class InfoMapping0 { + int submaps; // <= 16 + int[] chmuxlist = new int[256]; // up to 256 channels in a Vorbis stream + + int[] timesubmap = new int[16]; // [mux] + int[] floorsubmap = new int[16]; // [mux] submap to floors + int[] residuesubmap = new int[16];// [mux] submap to residue + int[] psysubmap = new int[16]; // [mux]; encode only + + int coupling_steps; + int[] coupling_mag = new int[256]; + int[] coupling_ang = new int[256]; + + void free() { + chmuxlist = null; + timesubmap = null; + floorsubmap = null; + residuesubmap = null; + psysubmap = null; + + coupling_mag = null; + coupling_ang = null; + } + } + + class LookMapping0 { + InfoMode mode; + InfoMapping0 map; + Object[] time_look; + Object[] floor_look; + Object[] floor_state; + Object[] residue_look; + PsyLook[] psy_look; + + FuncTime[] time_func; + FuncFloor[] floor_func; + FuncResidue[] residue_func; + + int ch; + float[][] decay; + int lastframe; // if a different mode is called, we need to + // invalidate decay and floor state + } + +} diff --git a/src/teavm/java/com/jcraft/jorbis/Mdct.java b/src/teavm/java/com/jcraft/jorbis/Mdct.java new file mode 100755 index 0000000..c2b29fb --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Mdct.java @@ -0,0 +1,249 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class Mdct { + + int n; + int log2n; + + float[] trig; + int[] bitrev; + + float scale; + + void init(int n) { + bitrev = new int[n / 4]; + trig = new float[n + n / 4]; + + log2n = (int) Math.rint(Math.log(n) / Math.log(2)); + this.n = n; + + int AE = 0; + int AO = 1; + int BE = AE + n / 2; + int BO = BE + 1; + int CE = BE + n / 2; + int CO = CE + 1; + // trig lookups... + for (int i = 0; i < n / 4; i++) { + trig[AE + i * 2] = (float) Math.cos((Math.PI / n) * (4 * i)); + trig[AO + i * 2] = (float) -Math.sin((Math.PI / n) * (4 * i)); + trig[BE + i * 2] = (float) Math.cos((Math.PI / (2 * n)) * (2 * i + 1)); + trig[BO + i * 2] = (float) Math.sin((Math.PI / (2 * n)) * (2 * i + 1)); + } + for (int i = 0; i < n / 8; i++) { + trig[CE + i * 2] = (float) Math.cos((Math.PI / n) * (4 * i + 2)); + trig[CO + i * 2] = (float) -Math.sin((Math.PI / n) * (4 * i + 2)); + } + + { + int mask = (1 << (log2n - 1)) - 1; + int msb = 1 << (log2n - 2); + for (int i = 0; i < n / 8; i++) { + int acc = 0; + for (int j = 0; msb >>> j != 0; j++) + if (((msb >>> j) & i) != 0) + acc |= 1 << j; + bitrev[i * 2] = ((~acc) & mask); + // bitrev[i*2]=((~acc)&mask)-1; + bitrev[i * 2 + 1] = acc; + } + } + scale = 4.f / n; + } + + void clear() { + } + + void forward(float[] in, float[] out) { + } + + float[] _x = new float[1024]; + float[] _w = new float[1024]; + + synchronized void backward(float[] in, float[] out) { + if (_x.length < n / 2) { + _x = new float[n / 2]; + } + if (_w.length < n / 2) { + _w = new float[n / 2]; + } + float[] x = _x; + float[] w = _w; + int n2 = n >>> 1; + int n4 = n >>> 2; + int n8 = n >>> 3; + + // rotate + step 1 + { + int inO = 1; + int xO = 0; + int A = n2; + + int i; + for (i = 0; i < n8; i++) { + A -= 2; + x[xO++] = -in[inO + 2] * trig[A + 1] - in[inO] * trig[A]; + x[xO++] = in[inO] * trig[A + 1] - in[inO + 2] * trig[A]; + inO += 4; + } + + inO = n2 - 4; + + for (i = 0; i < n8; i++) { + A -= 2; + x[xO++] = in[inO] * trig[A + 1] + in[inO + 2] * trig[A]; + x[xO++] = in[inO] * trig[A] - in[inO + 2] * trig[A + 1]; + inO -= 4; + } + } + + float[] xxx = mdct_kernel(x, w, n, n2, n4, n8); + int xx = 0; + + // step 8 + + { + int B = n2; + int o1 = n4, o2 = o1 - 1; + int o3 = n4 + n2, o4 = o3 - 1; + + for (int i = 0; i < n4; i++) { + float temp1 = (xxx[xx] * trig[B + 1] - xxx[xx + 1] * trig[B]); + float temp2 = -(xxx[xx] * trig[B] + xxx[xx + 1] * trig[B + 1]); + + out[o1] = -temp1; + out[o2] = temp1; + out[o3] = temp2; + out[o4] = temp2; + + o1++; + o2--; + o3++; + o4--; + xx += 2; + B += 2; + } + } + } + + private float[] mdct_kernel(float[] x, float[] w, int n, int n2, int n4, int n8) { + // step 2 + + int xA = n4; + int xB = 0; + int w2 = n4; + int A = n2; + + for (int i = 0; i < n4;) { + float x0 = x[xA] - x[xB]; + float x1; + w[w2 + i] = x[xA++] + x[xB++]; + + x1 = x[xA] - x[xB]; + A -= 4; + + w[i++] = x0 * trig[A] + x1 * trig[A + 1]; + w[i] = x1 * trig[A] - x0 * trig[A + 1]; + + w[w2 + i] = x[xA++] + x[xB++]; + i++; + } + + // step 3 + + { + for (int i = 0; i < log2n - 3; i++) { + int k0 = n >>> (i + 2); + int k1 = 1 << (i + 3); + int wbase = n2 - 2; + + A = 0; + float[] temp; + + for (int r = 0; r < (k0 >>> 2); r++) { + int w1 = wbase; + w2 = w1 - (k0 >> 1); + float AEv = trig[A], wA; + float AOv = trig[A + 1], wB; + wbase -= 2; + + k0++; + for (int s = 0; s < (2 << i); s++) { + wB = w[w1] - w[w2]; + x[w1] = w[w1] + w[w2]; + + wA = w[++w1] - w[++w2]; + x[w1] = w[w1] + w[w2]; + + x[w2] = wA * AEv - wB * AOv; + x[w2 - 1] = wB * AEv + wA * AOv; + + w1 -= k0; + w2 -= k0; + } + k0--; + A += k1; + } + + temp = w; + w = x; + x = temp; + } + } + + // step 4, 5, 6, 7 + { + int C = n; + int bit = 0; + int x1 = 0; + int x2 = n2 - 1; + + for (int i = 0; i < n8; i++) { + int t1 = bitrev[bit++]; + int t2 = bitrev[bit++]; + + float wA = w[t1] - w[t2 + 1]; + float wB = w[t1 - 1] + w[t2]; + float wC = w[t1] + w[t2 + 1]; + float wD = w[t1 - 1] - w[t2]; + + float wACE = wA * trig[C]; + float wBCE = wB * trig[C++]; + float wACO = wA * trig[C]; + float wBCO = wB * trig[C++]; + + x[x1++] = (wC + wACO + wBCE) * .5f; + x[x2--] = (-wD + wBCO - wACE) * .5f; + x[x1++] = (wD + wBCO - wACE) * .5f; + x[x2--] = (wC - wACO - wBCE) * .5f; + } + } + return (x); + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/PsyInfo.java b/src/teavm/java/com/jcraft/jorbis/PsyInfo.java new file mode 100755 index 0000000..906efab --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/PsyInfo.java @@ -0,0 +1,74 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +// psychoacoustic setup +class PsyInfo { + int athp; + int decayp; + int smoothp; + int noisefitp; + int noisefit_subblock; + float noisefit_threshdB; + + float ath_att; + + int tonemaskp; + float[] toneatt_125Hz = new float[5]; + float[] toneatt_250Hz = new float[5]; + float[] toneatt_500Hz = new float[5]; + float[] toneatt_1000Hz = new float[5]; + float[] toneatt_2000Hz = new float[5]; + float[] toneatt_4000Hz = new float[5]; + float[] toneatt_8000Hz = new float[5]; + + int peakattp; + float[] peakatt_125Hz = new float[5]; + float[] peakatt_250Hz = new float[5]; + float[] peakatt_500Hz = new float[5]; + float[] peakatt_1000Hz = new float[5]; + float[] peakatt_2000Hz = new float[5]; + float[] peakatt_4000Hz = new float[5]; + float[] peakatt_8000Hz = new float[5]; + + int noisemaskp; + float[] noiseatt_125Hz = new float[5]; + float[] noiseatt_250Hz = new float[5]; + float[] noiseatt_500Hz = new float[5]; + float[] noiseatt_1000Hz = new float[5]; + float[] noiseatt_2000Hz = new float[5]; + float[] noiseatt_4000Hz = new float[5]; + float[] noiseatt_8000Hz = new float[5]; + + float max_curve_dB; + + float attack_coeff; + float decay_coeff; + + void free() { + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/PsyLook.java b/src/teavm/java/com/jcraft/jorbis/PsyLook.java new file mode 100755 index 0000000..8fee7bf --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/PsyLook.java @@ -0,0 +1,42 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class PsyLook { + int n; + PsyInfo vi; + + float[][][] tonecurves; + float[][] peakatt; + float[][][] noisecurves; + + float[] ath; + int[] octave; + + void init(PsyInfo vi, int n, int rate) { + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Residue0.java b/src/teavm/java/com/jcraft/jorbis/Residue0.java new file mode 100755 index 0000000..d919709 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Residue0.java @@ -0,0 +1,326 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Residue0 extends FuncResidue { + void pack(Object vr, Buffer opb) { + InfoResidue0 info = (InfoResidue0) vr; + int acc = 0; + opb.write(info.begin, 24); + opb.write(info.end, 24); + + opb.write(info.grouping - 1, 24); /* + * residue vectors to group and code with a partitioned book + */ + opb.write(info.partitions - 1, 6); /* possible partition choices */ + opb.write(info.groupbook, 8); /* group huffman book */ + + /* + * secondstages is a bitmask; as encoding progresses pass by pass, a bitmask of + * one indicates this partition class has bits to write this pass + */ + for (int j = 0; j < info.partitions; j++) { + int i = info.secondstages[j]; + if (Util.ilog(i) > 3) { + /* yes, this is a minor hack due to not thinking ahead */ + opb.write(i, 3); + opb.write(1, 1); + opb.write(i >>> 3, 5); + } else { + opb.write(i, 4); /* trailing zero */ + } + acc += Util.icount(i); + } + for (int j = 0; j < acc; j++) { + opb.write(info.booklist[j], 8); + } + } + + Object unpack(Info vi, Buffer opb) { + int acc = 0; + InfoResidue0 info = new InfoResidue0(); + info.begin = opb.read(24); + info.end = opb.read(24); + info.grouping = opb.read(24) + 1; + info.partitions = opb.read(6) + 1; + info.groupbook = opb.read(8); + + for (int j = 0; j < info.partitions; j++) { + int cascade = opb.read(3); + if (opb.read(1) != 0) { + cascade |= (opb.read(5) << 3); + } + info.secondstages[j] = cascade; + acc += Util.icount(cascade); + } + + for (int j = 0; j < acc; j++) { + info.booklist[j] = opb.read(8); + } + + if (info.groupbook >= vi.books) { + free_info(info); + return (null); + } + + for (int j = 0; j < acc; j++) { + if (info.booklist[j] >= vi.books) { + free_info(info); + return (null); + } + } + return (info); + } + + Object look(DspState vd, InfoMode vm, Object vr) { + InfoResidue0 info = (InfoResidue0) vr; + LookResidue0 look = new LookResidue0(); + int acc = 0; + int dim; + int maxstage = 0; + look.info = info; + look.map = vm.mapping; + + look.parts = info.partitions; + look.fullbooks = vd.fullbooks; + look.phrasebook = vd.fullbooks[info.groupbook]; + + dim = look.phrasebook.dim; + + look.partbooks = new int[look.parts][]; + + for (int j = 0; j < look.parts; j++) { + int i = info.secondstages[j]; + int stages = Util.ilog(i); + if (stages != 0) { + if (stages > maxstage) + maxstage = stages; + look.partbooks[j] = new int[stages]; + for (int k = 0; k < stages; k++) { + if ((i & (1 << k)) != 0) { + look.partbooks[j][k] = info.booklist[acc++]; + } + } + } + } + + look.partvals = (int) Math.rint(Math.pow(look.parts, dim)); + look.stages = maxstage; + look.decodemap = new int[look.partvals][]; + for (int j = 0; j < look.partvals; j++) { + int val = j; + int mult = look.partvals / look.parts; + look.decodemap[j] = new int[dim]; + + for (int k = 0; k < dim; k++) { + int deco = val / mult; + val -= deco * mult; + mult /= look.parts; + look.decodemap[j][k] = deco; + } + } + return (look); + } + + void free_info(Object i) { + } + + void free_look(Object i) { + } + + private static int[][][] _01inverse_partword = new int[2][][]; // _01inverse is synchronized for + + // re-using partword + synchronized static int _01inverse(Block vb, Object vl, float[][] in, int ch, int decodepart) { + int i, j, k, l, s; + LookResidue0 look = (LookResidue0) vl; + InfoResidue0 info = look.info; + + // move all this setup out later + int samples_per_partition = info.grouping; + int partitions_per_word = look.phrasebook.dim; + int n = info.end - info.begin; + + int partvals = n / samples_per_partition; + int partwords = (partvals + partitions_per_word - 1) / partitions_per_word; + + if (_01inverse_partword.length < ch) { + _01inverse_partword = new int[ch][][]; + } + + for (j = 0; j < ch; j++) { + if (_01inverse_partword[j] == null || _01inverse_partword[j].length < partwords) { + _01inverse_partword[j] = new int[partwords][]; + } + } + + for (s = 0; s < look.stages; s++) { + // each loop decodes on partition codeword containing + // partitions_pre_word partitions + for (i = 0, l = 0; i < partvals; l++) { + if (s == 0) { + // fetch the partition word for each channel + for (j = 0; j < ch; j++) { + int temp = look.phrasebook.decode(vb.opb); + if (temp == -1) { + return (0); + } + _01inverse_partword[j][l] = look.decodemap[temp]; + if (_01inverse_partword[j][l] == null) { + return (0); + } + } + } + + // now we decode residual values for the partitions + for (k = 0; k < partitions_per_word && i < partvals; k++, i++) + for (j = 0; j < ch; j++) { + int offset = info.begin + i * samples_per_partition; + int index = _01inverse_partword[j][l][k]; + if ((info.secondstages[index] & (1 << s)) != 0) { + CodeBook stagebook = look.fullbooks[look.partbooks[index][s]]; + if (stagebook != null) { + if (decodepart == 0) { + if (stagebook.decodevs_add(in[j], offset, vb.opb, samples_per_partition) == -1) { + return (0); + } + } else if (decodepart == 1) { + if (stagebook.decodev_add(in[j], offset, vb.opb, samples_per_partition) == -1) { + return (0); + } + } + } + } + } + } + } + return (0); + } + + static int[][] _2inverse_partword = null; + + synchronized static int _2inverse(Block vb, Object vl, float[][] in, int ch) { + int i, k, l, s; + LookResidue0 look = (LookResidue0) vl; + InfoResidue0 info = look.info; + + // move all this setup out later + int samples_per_partition = info.grouping; + int partitions_per_word = look.phrasebook.dim; + int n = info.end - info.begin; + + int partvals = n / samples_per_partition; + int partwords = (partvals + partitions_per_word - 1) / partitions_per_word; + + if (_2inverse_partword == null || _2inverse_partword.length < partwords) { + _2inverse_partword = new int[partwords][]; + } + for (s = 0; s < look.stages; s++) { + for (i = 0, l = 0; i < partvals; l++) { + if (s == 0) { + // fetch the partition word for each channel + int temp = look.phrasebook.decode(vb.opb); + if (temp == -1) { + return (0); + } + _2inverse_partword[l] = look.decodemap[temp]; + if (_2inverse_partword[l] == null) { + return (0); + } + } + + // now we decode residual values for the partitions + for (k = 0; k < partitions_per_word && i < partvals; k++, i++) { + int offset = info.begin + i * samples_per_partition; + int index = _2inverse_partword[l][k]; + if ((info.secondstages[index] & (1 << s)) != 0) { + CodeBook stagebook = look.fullbooks[look.partbooks[index][s]]; + if (stagebook != null) { + if (stagebook.decodevv_add(in, offset, ch, vb.opb, samples_per_partition) == -1) { + return (0); + } + } + } + } + } + } + return (0); + } + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) { + int used = 0; + for (int i = 0; i < ch; i++) { + if (nonzero[i] != 0) { + in[used++] = in[i]; + } + } + if (used != 0) + return (_01inverse(vb, vl, in, used, 0)); + else + return (0); + } + + class LookResidue0 { + InfoResidue0 info; + int map; + + int parts; + int stages; + CodeBook[] fullbooks; + CodeBook phrasebook; + int[][] partbooks; + + int partvals; + int[][] decodemap; + + int postbits; + int phrasebits; + int frames; + } + + class InfoResidue0 { + // block-partitioned VQ coded straight residue + int begin; + int end; + + // first stage (lossless partitioning) + int grouping; // group n vectors per partition + int partitions; // possible codebooks for a partition + int groupbook; // huffbook for partitioning + int[] secondstages = new int[64]; // expanded out to pointers in lookup + int[] booklist = new int[256]; // list of second stage books + + // encode-only heuristic settings + float[] entmax = new float[64]; // book entropy threshholds + float[] ampmax = new float[64]; // book amp threshholds + int[] subgrp = new int[64]; // book heuristic subgroup size + int[] blimit = new int[64]; // subgroup position limits + } + +} diff --git a/src/teavm/java/com/jcraft/jorbis/Residue1.java b/src/teavm/java/com/jcraft/jorbis/Residue1.java new file mode 100755 index 0000000..55b0d9a --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Residue1.java @@ -0,0 +1,44 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class Residue1 extends Residue0 { + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) { + int used = 0; + for (int i = 0; i < ch; i++) { + if (nonzero[i] != 0) { + in[used++] = in[i]; + } + } + if (used != 0) { + return (_01inverse(vb, vl, in, used, 1)); + } else { + return 0; + } + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Residue2.java b/src/teavm/java/com/jcraft/jorbis/Residue2.java new file mode 100755 index 0000000..cb4ec8f --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Residue2.java @@ -0,0 +1,41 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +class Residue2 extends Residue0 { + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) { + int i = 0; + for (i = 0; i < ch; i++) + if (nonzero[i] != 0) + break; + if (i == ch) + return (0); /* no nonzero vectors */ + + return (_2inverse(vb, vl, in, ch)); + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/StaticCodeBook.java b/src/teavm/java/com/jcraft/jorbis/StaticCodeBook.java new file mode 100755 index 0000000..d06a2e8 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/StaticCodeBook.java @@ -0,0 +1,436 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class StaticCodeBook { + int dim; // codebook dimensions (elements per vector) + int entries; // codebook entries + int[] lengthlist; // codeword lengths in bits + + // mapping + int maptype; // 0=none + // 1=implicitly populated values from map column + // 2=listed arbitrary values + + // The below does a linear, single monotonic sequence mapping. + int q_min; // packed 32 bit float; quant value 0 maps to minval + int q_delta; // packed 32 bit float; val 1 - val 0 == delta + int q_quant; // bits: 0 < quant <= 16 + int q_sequencep; // bitflag + + // additional information for log (dB) mapping; the linear mapping + // is assumed to actually be values in dB. encodebias is used to + // assign an error weight to 0 dB. We have two additional flags: + // zeroflag indicates if entry zero is to represent -Inf dB; negflag + // indicates if we're to represent negative linear values in a + // mirror of the positive mapping. + + int[] quantlist; // map == 1: (int)(entries/dim) element column map + // map == 2: list of dim*entries quantized entry vals + + StaticCodeBook() { + } + + int pack(Buffer opb) { + int i; + boolean ordered = false; + + opb.write(0x564342, 24); + opb.write(dim, 16); + opb.write(entries, 24); + + // pack the codewords. There are two packings; length ordered and + // length random. Decide between the two now. + + for (i = 1; i < entries; i++) { + if (lengthlist[i] < lengthlist[i - 1]) + break; + } + if (i == entries) + ordered = true; + + if (ordered) { + // length ordered. We only need to say how many codewords of + // each length. The actual codewords are generated + // deterministically + + int count = 0; + opb.write(1, 1); // ordered + opb.write(lengthlist[0] - 1, 5); // 1 to 32 + + for (i = 1; i < entries; i++) { + int _this = lengthlist[i]; + int _last = lengthlist[i - 1]; + if (_this > _last) { + for (int j = _last; j < _this; j++) { + opb.write(i - count, Util.ilog(entries - count)); + count = i; + } + } + } + opb.write(i - count, Util.ilog(entries - count)); + } else { + // length random. Again, we don't code the codeword itself, just + // the length. This time, though, we have to encode each length + opb.write(0, 1); // unordered + + // algortihmic mapping has use for 'unused entries', which we tag + // here. The algorithmic mapping happens as usual, but the unused + // entry has no codeword. + for (i = 0; i < entries; i++) { + if (lengthlist[i] == 0) + break; + } + + if (i == entries) { + opb.write(0, 1); // no unused entries + for (i = 0; i < entries; i++) { + opb.write(lengthlist[i] - 1, 5); + } + } else { + opb.write(1, 1); // we have unused entries; thus we tag + for (i = 0; i < entries; i++) { + if (lengthlist[i] == 0) { + opb.write(0, 1); + } else { + opb.write(1, 1); + opb.write(lengthlist[i] - 1, 5); + } + } + } + } + + // is the entry number the desired return value, or do we have a + // mapping? If we have a mapping, what type? + opb.write(maptype, 4); + switch (maptype) { + case 0: + // no mapping + break; + case 1: + case 2: + // implicitly populated value mapping + // explicitly populated value mapping + if (quantlist == null) { + // no quantlist? error + return (-1); + } + + // values that define the dequantization + opb.write(q_min, 32); + opb.write(q_delta, 32); + opb.write(q_quant - 1, 4); + opb.write(q_sequencep, 1); + + { + int quantvals = 0; + switch (maptype) { + case 1: + // a single column of (c->entries/c->dim) quantized values for + // building a full value list algorithmically (square lattice) + quantvals = maptype1_quantvals(); + break; + case 2: + // every value (c->entries*c->dim total) specified explicitly + quantvals = entries * dim; + break; + } + + // quantized values + for (i = 0; i < quantvals; i++) { + opb.write(Math.abs(quantlist[i]), q_quant); + } + } + break; + default: + // error case; we don't have any other map types now + return (-1); + } + return (0); + } + + // unpacks a codebook from the packet buffer into the codebook struct, + // readies the codebook auxiliary structures for decode + int unpack(Buffer opb) { + int i; + // memset(s,0,sizeof(static_codebook)); + + // make sure alignment is correct + if (opb.read(24) != 0x564342) { + // goto _eofout; + clear(); + return (-1); + } + + // first the basic parameters + dim = opb.read(16); + entries = opb.read(24); + if (entries == -1) { + // goto _eofout; + clear(); + return (-1); + } + + // codeword ordering.... length ordered or unordered? + switch (opb.read(1)) { + case 0: + // unordered + lengthlist = new int[entries]; + + // allocated but unused entries? + if (opb.read(1) != 0) { + // yes, unused entries + + for (i = 0; i < entries; i++) { + if (opb.read(1) != 0) { + int num = opb.read(5); + if (num == -1) { + // goto _eofout; + clear(); + return (-1); + } + lengthlist[i] = num + 1; + } else { + lengthlist[i] = 0; + } + } + } else { + // all entries used; no tagging + for (i = 0; i < entries; i++) { + int num = opb.read(5); + if (num == -1) { + // goto _eofout; + clear(); + return (-1); + } + lengthlist[i] = num + 1; + } + } + break; + case 1: + // ordered + { + int length = opb.read(5) + 1; + lengthlist = new int[entries]; + + for (i = 0; i < entries;) { + int num = opb.read(Util.ilog(entries - i)); + if (num == -1) { + // goto _eofout; + clear(); + return (-1); + } + for (int j = 0; j < num; j++, i++) { + lengthlist[i] = length; + } + length++; + } + } + break; + default: + // EOF + return (-1); + } + + // Do we have a mapping to unpack? + switch ((maptype = opb.read(4))) { + case 0: + // no mapping + break; + case 1: + case 2: + // implicitly populated value mapping + // explicitly populated value mapping + q_min = opb.read(32); + q_delta = opb.read(32); + q_quant = opb.read(4) + 1; + q_sequencep = opb.read(1); + + { + int quantvals = 0; + switch (maptype) { + case 1: + quantvals = maptype1_quantvals(); + break; + case 2: + quantvals = entries * dim; + break; + } + + // quantized values + quantlist = new int[quantvals]; + for (i = 0; i < quantvals; i++) { + quantlist[i] = opb.read(q_quant); + } + if (quantlist[quantvals - 1] == -1) { + // goto _eofout; + clear(); + return (-1); + } + } + break; + default: + // goto _eofout; + clear(); + return (-1); + } + // all set + return (0); + // _errout: + // _eofout: + // vorbis_staticbook_clear(s); + // return(-1); + } + + // there might be a straightforward one-line way to do the below + // that's portable and totally safe against roundoff, but I haven't + // thought of it. Therefore, we opt on the side of caution + private int maptype1_quantvals() { + int vals = (int) (Math.floor(Math.pow(entries, 1. / dim))); + + // the above *should* be reliable, but we'll not assume that FP is + // ever reliable when bitstream sync is at stake; verify via integer + // means that vals really is the greatest value of dim for which + // vals^b->bim <= b->entries + // treat the above as an initial guess + while (true) { + int acc = 1; + int acc1 = 1; + for (int i = 0; i < dim; i++) { + acc *= vals; + acc1 *= vals + 1; + } + if (acc <= entries && acc1 > entries) { + return (vals); + } else { + if (acc > entries) { + vals--; + } else { + vals++; + } + } + } + } + + void clear() { + } + + // unpack the quantized list of values for encode/decode + // we need to deal with two map types: in map type 1, the values are + // generated algorithmically (each column of the vector counts through + // the values in the quant vector). in map type 2, all the values came + // in in an explicit list. Both value lists must be unpacked + float[] unquantize() { + + if (maptype == 1 || maptype == 2) { + int quantvals; + float mindel = float32_unpack(q_min); + float delta = float32_unpack(q_delta); + float[] r = new float[entries * dim]; + + // maptype 1 and 2 both use a quantized value vector, but + // different sizes + switch (maptype) { + case 1: + // most of the time, entries%dimensions == 0, but we need to be + // well defined. We define that the possible vales at each + // scalar is values == entries/dim. If entries%dim != 0, we'll + // have 'too few' values (values*dim "+val+" | ");} + val = Math.abs(val) * delta + mindel + last; + if (q_sequencep != 0) + last = val; + r[j * dim + k] = val; + // if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");} + } + } + // System.err.println("\nr[0]="+r[0]); + } + return (r); + } + return (null); + } + + // 32 bit float (not IEEE; nonnormalized mantissa + + // biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + // Why not IEEE? It's just not that important here. + + static final int VQ_FEXP = 10; + static final int VQ_FMAN = 21; + static final int VQ_FEXP_BIAS = 768; // bias toward values smaller than 1. + + // doesn't currently guard under/overflow + static long float32_pack(float val) { + int sign = 0; + int exp; + int mant; + if (val < 0) { + sign = 0x80000000; + val = -val; + } + exp = (int) Math.floor(Math.log(val) / Math.log(2)); + mant = (int) Math.rint(Math.pow(val, (VQ_FMAN - 1) - exp)); + exp = (exp + VQ_FEXP_BIAS) << VQ_FMAN; + return (sign | exp | mant); + } + + static float float32_unpack(int val) { + float mant = val & 0x1fffff; + float exp = (val & 0x7fe00000) >>> VQ_FMAN; + if ((val & 0x80000000) != 0) + mant = -mant; + return (ldexp(mant, ((int) exp) - (VQ_FMAN - 1) - VQ_FEXP_BIAS)); + } + + static float ldexp(float foo, int e) { + return (float) (foo * Math.pow(2, e)); + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Time0.java b/src/teavm/java/com/jcraft/jorbis/Time0.java new file mode 100755 index 0000000..3e9e81f --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Time0.java @@ -0,0 +1,52 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Time0 extends FuncTime { + void pack(Object i, Buffer opb) { + } + + Object unpack(Info vi, Buffer opb) { + return ""; + } + + Object look(DspState vd, InfoMode mi, Object i) { + return ""; + } + + void free_info(Object i) { + } + + void free_look(Object i) { + } + + int inverse(Block vb, Object i, float[] in, float[] out) { + return 0; + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/Util.java b/src/teavm/java/com/jcraft/jorbis/Util.java new file mode 100755 index 0000000..a8844c3 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/Util.java @@ -0,0 +1,30 @@ +package com.jcraft.jorbis; + +class Util { + static int ilog(int v) { + int ret = 0; + while (v != 0) { + ret++; + v >>>= 1; + } + return (ret); + } + + static int ilog2(int v) { + int ret = 0; + while (v > 1) { + ret++; + v >>>= 1; + } + return (ret); + } + + static int icount(int v) { + int ret = 0; + while (v != 0) { + ret += (v & 1); + v >>>= 1; + } + return (ret); + } +} diff --git a/src/teavm/java/com/jcraft/jorbis/VorbisFile.java b/src/teavm/java/com/jcraft/jorbis/VorbisFile.java new file mode 100755 index 0000000..4222740 --- /dev/null +++ b/src/teavm/java/com/jcraft/jorbis/VorbisFile.java @@ -0,0 +1,1348 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +import java.io.InputStream; +import java.io.IOException; + +public class VorbisFile { + static final int CHUNKSIZE = 8500; + static final int SEEK_SET = 0; + static final int SEEK_CUR = 1; + static final int SEEK_END = 2; + + static final int OV_FALSE = -1; + static final int OV_EOF = -2; + static final int OV_HOLE = -3; + + static final int OV_EREAD = -128; + static final int OV_EFAULT = -129; + static final int OV_EIMPL = -130; + static final int OV_EINVAL = -131; + static final int OV_ENOTVORBIS = -132; + static final int OV_EBADHEADER = -133; + static final int OV_EVERSION = -134; + static final int OV_ENOTAUDIO = -135; + static final int OV_EBADPACKET = -136; + static final int OV_EBADLINK = -137; + static final int OV_ENOSEEK = -138; + + InputStream datasource; + boolean seekable = false; + long offset; + long end; + + SyncState oy = new SyncState(); + + int links; + long[] offsets; + long[] dataoffsets; + int[] serialnos; + long[] pcmlengths; + Info[] vi; + Comment[] vc; + + // Decoding working state local storage + long pcm_offset; + boolean decode_ready = false; + + int current_serialno; + int current_link; + + float bittrack; + float samptrack; + + StreamState os = new StreamState(); // take physical pages, weld into a logical + // stream of packets + DspState vd = new DspState(); // central working state for + // the packet->PCM decoder + Block vb = new Block(vd); // local working space for packet->PCM decode + + // ov_callbacks callbacks; + + public VorbisFile(String file) throws JOrbisException { + super(); + InputStream is = null; + try { + is = new SeekableInputStream(file); + int ret = open(is, null, 0); + if (ret == -1) { + throw new JOrbisException("VorbisFile: open return -1"); + } + } catch (Exception e) { + throw new JOrbisException("VorbisFile: " + e.toString()); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public VorbisFile(InputStream is, byte[] initial, int ibytes) throws JOrbisException { + super(); + int ret = open(is, initial, ibytes); + if (ret == -1) { + } + } + + private int get_data() { + int index = oy.buffer(CHUNKSIZE); + byte[] buffer = oy.data; + int bytes = 0; + try { + bytes = datasource.read(buffer, index, CHUNKSIZE); + } catch (Exception e) { + return OV_EREAD; + } + oy.wrote(bytes); + if (bytes == -1) { + bytes = 0; + } + return bytes; + } + + private void seek_helper(long offst) { + fseek(datasource, offst, SEEK_SET); + this.offset = offst; + oy.reset(); + } + + private int get_next_page(Page page, long boundary) { + if (boundary > 0) + boundary += offset; + while (true) { + int more; + if (boundary > 0 && offset >= boundary) + return OV_FALSE; + more = oy.pageseek(page); + if (more < 0) { + offset -= more; + } else { + if (more == 0) { + if (boundary == 0) + return OV_FALSE; + int ret = get_data(); + if (ret == 0) + return OV_EOF; + if (ret < 0) + return OV_EREAD; + } else { + int ret = (int) offset; // !!! + offset += more; + return ret; + } + } + } + } + + private int get_prev_page(Page page) throws JOrbisException { + long begin = offset; // !!! + int ret; + int offst = -1; + while (offst == -1) { + begin -= CHUNKSIZE; + if (begin < 0) + begin = 0; + seek_helper(begin); + while (offset < begin + CHUNKSIZE) { + ret = get_next_page(page, begin + CHUNKSIZE - offset); + if (ret == OV_EREAD) { + return OV_EREAD; + } + if (ret < 0) { + if (offst == -1) + throw new JOrbisException(); + break; + } else { + offst = ret; + } + } + } + seek_helper(offst); // !!! + ret = get_next_page(page, CHUNKSIZE); + if (ret < 0) { + return OV_EFAULT; + } + return offst; + } + + int bisect_forward_serialno(long begin, long searched, long end, int currentno, int m) { + long endsearched = end; + long next = end; + Page page = new Page(); + int ret; + + while (searched < endsearched) { + long bisect; + if (endsearched - searched < CHUNKSIZE) { + bisect = searched; + } else { + bisect = (searched + endsearched) / 2; + } + + seek_helper(bisect); + ret = get_next_page(page, -1); + if (ret == OV_EREAD) + return OV_EREAD; + if (ret < 0 || page.serialno() != currentno) { + endsearched = bisect; + if (ret >= 0) + next = ret; + } else { + searched = ret + page.header_len + page.body_len; + } + } + seek_helper(next); + ret = get_next_page(page, -1); + if (ret == OV_EREAD) + return OV_EREAD; + + if (searched >= end || ret == -1) { + links = m + 1; + offsets = new long[m + 2]; + offsets[m + 1] = searched; + } else { + ret = bisect_forward_serialno(next, offset, end, page.serialno(), m + 1); + if (ret == OV_EREAD) + return OV_EREAD; + } + offsets[m] = begin; + return 0; + } + + // uses the local ogg_stream storage in vf; this is important for + // non-streaming input sources + int fetch_headers(Info vi, Comment vc, int[] serialno, Page og_ptr) { + Page og = new Page(); + Packet op = new Packet(); + int ret; + + if (og_ptr == null) { + ret = get_next_page(og, CHUNKSIZE); + if (ret == OV_EREAD) + return OV_EREAD; + if (ret < 0) + return OV_ENOTVORBIS; + og_ptr = og; + } + + if (serialno != null) + serialno[0] = og_ptr.serialno(); + + os.init(og_ptr.serialno()); + + // extract the initial header from the first page and verify that the + // Ogg bitstream is in fact Vorbis data + + vi.init(); + vc.init(); + + int i = 0; + while (i < 3) { + os.pagein(og_ptr); + while (i < 3) { + int result = os.packetout(op); + if (result == 0) + break; + if (result == -1) { + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + if (vi.synthesis_headerin(vc, op) != 0) { + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + i++; + } + if (i < 3) + if (get_next_page(og_ptr, 1) < 0) { + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + } + return 0; + } + + // last step of the OggVorbis_File initialization; get all the + // vorbis_info structs and PCM positions. Only called by the seekable + // initialization (local stream storage is hacked slightly; pay + // attention to how that's done) + void prefetch_all_headers(Info first_i, Comment first_c, int dataoffset) throws JOrbisException { + Page og = new Page(); + int ret; + + vi = new Info[links]; + vc = new Comment[links]; + dataoffsets = new long[links]; + pcmlengths = new long[links]; + serialnos = new int[links]; + + for (int i = 0; i < links; i++) { + if (first_i != null && first_c != null && i == 0) { + // we already grabbed the initial header earlier. This just + // saves the waste of grabbing it again + vi[i] = first_i; + vc[i] = first_c; + dataoffsets[i] = dataoffset; + } else { + // seek to the location of the initial header + seek_helper(offsets[i]); // !!! + vi[i] = new Info(); + vc[i] = new Comment(); + if (fetch_headers(vi[i], vc[i], null, null) == -1) { + dataoffsets[i] = -1; + } else { + dataoffsets[i] = offset; + os.clear(); + } + } + + // get the serial number and PCM length of this link. To do this, + // get the last page of the stream + { + long end = offsets[i + 1]; // !!! + seek_helper(end); + + while (true) { + ret = get_prev_page(og); + if (ret == -1) { + // this should not be possible + vi[i].clear(); + vc[i].clear(); + break; + } + if (og.granulepos() != -1) { + serialnos[i] = og.serialno(); + pcmlengths[i] = og.granulepos(); + break; + } + } + } + } + } + + private int make_decode_ready() { + if (decode_ready) + System.exit(1); + vd.synthesis_init(vi[0]); + vb.init(vd); + decode_ready = true; + return (0); + } + + int open_seekable() throws JOrbisException { + Info initial_i = new Info(); + Comment initial_c = new Comment(); + int serialno; + long end; + int ret; + int dataoffset; + Page og = new Page(); + // is this even vorbis...? + int[] foo = new int[1]; + ret = fetch_headers(initial_i, initial_c, foo, null); + serialno = foo[0]; + dataoffset = (int) offset; // !! + os.clear(); + if (ret == -1) + return (-1); + if (ret < 0) + return (ret); + // we can seek, so set out learning all about this file + seekable = true; + fseek(datasource, 0, SEEK_END); + offset = ftell(datasource); + end = offset; + // We get the offset for the last page of the physical bitstream. + // Most OggVorbis files will contain a single logical bitstream + end = get_prev_page(og); + // moer than one logical bitstream? + if (og.serialno() != serialno) { + // Chained bitstream. Bisect-search each logical bitstream + // section. Do so based on serial number only + if (bisect_forward_serialno(0, 0, end + 1, serialno, 0) < 0) { + clear(); + return OV_EREAD; + } + } else { + // Only one logical bitstream + if (bisect_forward_serialno(0, end, end + 1, serialno, 0) < 0) { + clear(); + return OV_EREAD; + } + } + prefetch_all_headers(initial_i, initial_c, dataoffset); + return 0; + } + + int open_nonseekable() { + // we cannot seek. Set up a 'single' (current) logical bitstream entry + links = 1; + vi = new Info[links]; + vi[0] = new Info(); // ?? + vc = new Comment[links]; + vc[0] = new Comment(); // ?? bug? + + // Try to fetch the headers, maintaining all the storage + int[] foo = new int[1]; + if (fetch_headers(vi[0], vc[0], foo, null) == -1) + return (-1); + current_serialno = foo[0]; + make_decode_ready(); + return 0; + } + + // clear out the current logical bitstream decoder + void decode_clear() { + os.clear(); + vd.clear(); + vb.clear(); + decode_ready = false; + bittrack = 0.f; + samptrack = 0.f; + } + + // fetch and process a packet. Handles the case where we're at a + // bitstream boundary and dumps the decoding machine. If the decoding + // machine is unloaded, it loads it. It also keeps pcm_offset up to + // date (seek and read both use this. seek uses a special hack with + // readp). + // + // return: -1) hole in the data (lost packet) + // 0) need more date (only if readp==0)/eof + // 1) got a packet + + int process_packet(int readp) { + Page og = new Page(); + + // handle one packet. Try to fetch it from current stream state + // extract packets from page + while (true) { + // process a packet if we can. If the machine isn't loaded, + // neither is a page + if (decode_ready) { + Packet op = new Packet(); + int result = os.packetout(op); + long granulepos; + // if(result==-1)return(-1); // hole in the data. For now, swallow + // and go. We'll need to add a real + // error code in a bit. + if (result > 0) { + // got a packet. process it + granulepos = op.granulepos; + if (vb.synthesis(op) == 0) { // lazy check for lazy + // header handling. The + // header packets aren't + // audio, so if/when we + // submit them, + // vorbis_synthesis will + // reject them + // suck in the synthesis data and track bitrate + { + int oldsamples = vd.synthesis_pcmout(null, null); + vd.synthesis_blockin(vb); + samptrack += vd.synthesis_pcmout(null, null) - oldsamples; + bittrack += op.bytes * 8; + } + + // update the pcm offset. + if (granulepos != -1 && op.e_o_s == 0) { + int link = (seekable ? current_link : 0); + int samples; + // this packet has a pcm_offset on it (the last packet + // completed on a page carries the offset) After processing + // (above), we know the pcm position of the *last* sample + // ready to be returned. Find the offset of the *first* + // + // As an aside, this trick is inaccurate if we begin + // reading anew right at the last page; the end-of-stream + // granulepos declares the last frame in the stream, and the + // last packet of the last page may be a partial frame. + // So, we need a previous granulepos from an in-sequence page + // to have a reference point. Thus the !op.e_o_s clause above + + samples = vd.synthesis_pcmout(null, null); + granulepos -= samples; + for (int i = 0; i < link; i++) { + granulepos += pcmlengths[i]; + } + pcm_offset = granulepos; + } + return (1); + } + } + } + + if (readp == 0) + return (0); + if (get_next_page(og, -1) < 0) + return (0); // eof. leave unitialized + + // bitrate tracking; add the header's bytes here, the body bytes + // are done by packet above + bittrack += og.header_len * 8; + + // has our decoding just traversed a bitstream boundary? + if (decode_ready) { + if (current_serialno != og.serialno()) { + decode_clear(); + } + } + + // Do we need to load a new machine before submitting the page? + // This is different in the seekable and non-seekable cases. + // + // In the seekable case, we already have all the header + // information loaded and cached; we just initialize the machine + // with it and continue on our merry way. + // + // In the non-seekable (streaming) case, we'll only be at a + // boundary if we just left the previous logical bitstream and + // we're now nominally at the header of the next bitstream + + if (!decode_ready) { + int i; + if (seekable) { + current_serialno = og.serialno(); + + // match the serialno to bitstream section. We use this rather than + // offset positions to avoid problems near logical bitstream + // boundaries + for (i = 0; i < links; i++) { + if (serialnos[i] == current_serialno) + break; + } + if (i == links) + return (-1); // sign of a bogus stream. error out, + // leave machine uninitialized + current_link = i; + + os.init(current_serialno); + os.reset(); + + } else { + // we're streaming + // fetch the three header packets, build the info struct + int foo[] = new int[1]; + int ret = fetch_headers(vi[0], vc[0], foo, og); + current_serialno = foo[0]; + if (ret != 0) + return ret; + current_link++; + i = 0; + } + make_decode_ready(); + } + os.pagein(og); + } + } + + // The helpers are over; it's all toplevel interface from here on out + // clear out the OggVorbis_File struct + int clear() { + vb.clear(); + vd.clear(); + os.clear(); + + if (vi != null && links != 0) { + for (int i = 0; i < links; i++) { + vi[i].clear(); + vc[i].clear(); + } + vi = null; + vc = null; + } + if (dataoffsets != null) + dataoffsets = null; + if (pcmlengths != null) + pcmlengths = null; + if (serialnos != null) + serialnos = null; + if (offsets != null) + offsets = null; + oy.clear(); + + return (0); + } + + static int fseek(InputStream fis, long off, int whence) { + if (fis instanceof SeekableInputStream) { + SeekableInputStream sis = (SeekableInputStream) fis; + try { + if (whence == SEEK_SET) { + sis.seek(off); + } else if (whence == SEEK_END) { + sis.seek(sis.getLength() - off); + } else { + } + } catch (Exception e) { + } + return 0; + } + try { + if (whence == 0) { + fis.reset(); + } + fis.skip(off); + } catch (Exception e) { + return -1; + } + return 0; + } + + static long ftell(InputStream fis) { + try { + if (fis instanceof SeekableInputStream) { + SeekableInputStream sis = (SeekableInputStream) fis; + return (sis.tell()); + } + } catch (Exception e) { + } + return 0; + } + + // inspects the OggVorbis file and finds/documents all the logical + // bitstreams contained in it. Tries to be tolerant of logical + // bitstream sections that are truncated/woogie. + // + // return: -1) error + // 0) OK + + int open(InputStream is, byte[] initial, int ibytes) throws JOrbisException { + return open_callbacks(is, initial, ibytes); + } + + int open_callbacks(InputStream is, byte[] initial, int ibytes// , callbacks callbacks + ) throws JOrbisException { + int ret; + datasource = is; + + oy.init(); + + // perhaps some data was previously read into a buffer for testing + // against other stream types. Allow initialization from this + // previously read data (as we may be reading from a non-seekable + // stream) + if (initial != null) { + int index = oy.buffer(ibytes); + System.arraycopy(initial, 0, oy.data, index, ibytes); + oy.wrote(ibytes); + } + // can we seek? Stevens suggests the seek test was portable + if (is instanceof SeekableInputStream) { + ret = open_seekable(); + } else { + ret = open_nonseekable(); + } + if (ret != 0) { + datasource = null; + clear(); + } + return ret; + } + + // How many logical bitstreams in this physical bitstream? + public int streams() { + return links; + } + + // Is the FILE * associated with vf seekable? + public boolean seekable() { + return seekable; + } + + // returns the bitrate for a given logical bitstream or the entire + // physical bitstream. If the file is open for random access, it will + // find the *actual* average bitrate. If the file is streaming, it + // returns the nominal bitrate (if set) else the average of the + // upper/lower bounds (if set) else -1 (unset). + // + // If you want the actual bitrate field settings, get them from the + // vorbis_info structs + + public int bitrate(int i) { + if (i >= links) + return (-1); + if (!seekable && i != 0) + return (bitrate(0)); + if (i < 0) { + long bits = 0; + for (int j = 0; j < links; j++) { + bits += (offsets[j + 1] - dataoffsets[j]) * 8; + } + return ((int) Math.rint(bits / time_total(-1))); + } else { + if (seekable) { + // return the actual bitrate + return ((int) Math.rint((offsets[i + 1] - dataoffsets[i]) * 8 / time_total(i))); + } else { + // return nominal if set + if (vi[i].bitrate_nominal > 0) { + return vi[i].bitrate_nominal; + } else { + if (vi[i].bitrate_upper > 0) { + if (vi[i].bitrate_lower > 0) { + return (vi[i].bitrate_upper + vi[i].bitrate_lower) / 2; + } else { + return vi[i].bitrate_upper; + } + } + return (-1); + } + } + } + } + + // returns the actual bitrate since last call. returns -1 if no + // additional data to offer since last call (or at beginning of stream) + public int bitrate_instant() { + int _link = (seekable ? current_link : 0); + if (samptrack == 0) + return (-1); + int ret = (int) (bittrack / samptrack * vi[_link].rate + .5); + bittrack = 0.f; + samptrack = 0.f; + return (ret); + } + + public int serialnumber(int i) { + if (i >= links) + return (-1); + if (!seekable && i >= 0) + return (serialnumber(-1)); + if (i < 0) { + return (current_serialno); + } else { + return (serialnos[i]); + } + } + + // returns: total raw (compressed) length of content if i==-1 + // raw (compressed) length of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + + public long raw_total(int i) { + if (!seekable || i >= links) + return (-1); + if (i < 0) { + long acc = 0; // bug? + for (int j = 0; j < links; j++) { + acc += raw_total(j); + } + return (acc); + } else { + return (offsets[i + 1] - offsets[i]); + } + } + + // returns: total PCM length (samples) of content if i==-1 + // PCM length (samples) of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + public long pcm_total(int i) { + if (!seekable || i >= links) + return (-1); + if (i < 0) { + long acc = 0; + for (int j = 0; j < links; j++) { + acc += pcm_total(j); + } + return (acc); + } else { + return (pcmlengths[i]); + } + } + + // returns: total seconds of content if i==-1 + // seconds in that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + public float time_total(int i) { + if (!seekable || i >= links) + return (-1); + if (i < 0) { + float acc = 0; + for (int j = 0; j < links; j++) { + acc += time_total(j); + } + return (acc); + } else { + return ((float) (pcmlengths[i]) / vi[i].rate); + } + } + + // seek to an offset relative to the *compressed* data. This also + // immediately sucks in and decodes pages to update the PCM cursor. It + // will cross a logical bitstream boundary, but only if it can't get + // any packets out of the tail of the bitstream we seek to (so no + // surprises). + // + // returns zero on success, nonzero on failure + + public int raw_seek(int pos) { + if (!seekable) + return (-1); // don't dump machine if we can't seek + if (pos < 0 || pos > offsets[links]) { + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + } + + // clear out decoding machine state + pcm_offset = -1; + decode_clear(); + + // seek + seek_helper(pos); + + // we need to make sure the pcm_offset is set. We use the + // _fetch_packet helper to process one packet with readp set, then + // call it until it returns '0' with readp not set (the last packet + // from a page has the 'granulepos' field set, and that's how the + // helper updates the offset + + switch (process_packet(1)) { + case 0: + // oh, eof. There are no packets remaining. Set the pcm offset to + // the end of file + pcm_offset = pcm_total(-1); + return (0); + case -1: + // error! missing data or invalid bitstream structure + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + default: + // all OK + break; + } + while (true) { + switch (process_packet(0)) { + case 0: + // the offset is set. If it's a bogus bitstream with no offset + // information, it's not but that's not our fault. We still run + // gracefully, we're just missing the offset + return (0); + case -1: + // error! missing data or invalid bitstream structure + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + default: + // continue processing packets + break; + } + } + + // seek_error: + // dump the machine so we're in a known state + // pcm_offset=-1; + // decode_clear(); + // return -1; + } + + // seek to a sample offset relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + + public int pcm_seek(long pos) { + int link = -1; + long total = pcm_total(-1); + + if (!seekable) + return (-1); // don't dump machine if we can't seek + if (pos < 0 || pos > total) { + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + } + + // which bitstream section does this pcm offset occur in? + for (link = links - 1; link >= 0; link--) { + total -= pcmlengths[link]; + if (pos >= total) + break; + } + + // search within the logical bitstream for the page with the highest + // pcm_pos preceeding (or equal to) pos. There is a danger here; + // missing pages or incorrect frame number information in the + // bitstream could make our task impossible. Account for that (it + // would be an error condition) + { + long target = pos - total; + long end = offsets[link + 1]; + long begin = offsets[link]; + int best = (int) begin; + + Page og = new Page(); + while (begin < end) { + long bisect; + int ret; + + if (end - begin < CHUNKSIZE) { + bisect = begin; + } else { + bisect = (end + begin) / 2; + } + + seek_helper(bisect); + ret = get_next_page(og, end - bisect); + + if (ret == -1) { + end = bisect; + } else { + long granulepos = og.granulepos(); + if (granulepos < target) { + best = ret; // raw offset of packet with granulepos + begin = offset; // raw offset of next packet + } else { + end = bisect; + } + } + } + // found our page. seek to it (call raw_seek). + if (raw_seek(best) != 0) { + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + } + } + + // verify result + if (pcm_offset >= pos) { + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + } + if (pos > pcm_total(-1)) { + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + } + + // discard samples until we reach the desired position. Crossing a + // logical bitstream boundary with abandon is OK. + while (pcm_offset < pos) { + int target = (int) (pos - pcm_offset); + float[][][] _pcm = new float[1][][]; + int[] _index = new int[getInfo(-1).channels]; + int samples = vd.synthesis_pcmout(_pcm, _index); + + if (samples > target) + samples = target; + vd.synthesis_read(samples); + pcm_offset += samples; + + if (samples < target) + if (process_packet(1) == 0) { + pcm_offset = pcm_total(-1); // eof + } + } + return 0; + + // seek_error: + // dump machine so we're in a known state + // pcm_offset=-1; + // decode_clear(); + // return -1; + } + + // seek to a playback time relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + int time_seek(float seconds) { + // translate time to PCM position and call pcm_seek + + int link = -1; + long pcm_total = pcm_total(-1); + float time_total = time_total(-1); + + if (!seekable) + return (-1); // don't dump machine if we can't seek + if (seconds < 0 || seconds > time_total) { + // goto seek_error; + pcm_offset = -1; + decode_clear(); + return -1; + } + + // which bitstream section does this time offset occur in? + for (link = links - 1; link >= 0; link--) { + pcm_total -= pcmlengths[link]; + time_total -= time_total(link); + if (seconds >= time_total) + break; + } + + // enough information to convert time offset to pcm offset + { + long target = (long) (pcm_total + (seconds - time_total) * vi[link].rate); + return (pcm_seek(target)); + } + + // seek_error: + // dump machine so we're in a known state + // pcm_offset=-1; + // decode_clear(); + // return -1; + } + + // tell the current stream offset cursor. Note that seek followed by + // tell will likely not give the set offset due to caching + public long raw_tell() { + return (offset); + } + + // return PCM offset (sample) of next PCM sample to be read + public long pcm_tell() { + return (pcm_offset); + } + + // return time offset (seconds) of next PCM sample to be read + public float time_tell() { + // translate time to PCM position and call pcm_seek + + int link = -1; + long pcm_total = 0; + float time_total = 0.f; + + if (seekable) { + pcm_total = pcm_total(-1); + time_total = time_total(-1); + + // which bitstream section does this time offset occur in? + for (link = links - 1; link >= 0; link--) { + pcm_total -= pcmlengths[link]; + time_total -= time_total(link); + if (pcm_offset >= pcm_total) + break; + } + } + + return ((float) time_total + (float) (pcm_offset - pcm_total) / vi[link].rate); + } + + // link: -1) return the vorbis_info struct for the bitstream section + // currently being decoded + // 0-n) to request information for a specific bitstream section + // + // In the case of a non-seekable bitstream, any call returns the + // current bitstream. NULL in the case that the machine is not + // initialized + + public Info getInfo(int link) { + if (seekable) { + if (link < 0) { + if (decode_ready) { + return vi[current_link]; + } else { + return null; + } + } else { + if (link >= links) { + return null; + } else { + return vi[link]; + } + } + } else { + if (decode_ready) { + return vi[0]; + } else { + return null; + } + } + } + + public Comment getComment(int link) { + if (seekable) { + if (link < 0) { + if (decode_ready) { + return vc[current_link]; + } else { + return null; + } + } else { + if (link >= links) { + return null; + } else { + return vc[link]; + } + } + } else { + if (decode_ready) { + return vc[0]; + } else { + return null; + } + } + } + + int host_is_big_endian() { + return 1; + // short pattern = 0xbabe; + // unsigned char *bytewise = (unsigned char *)&pattern; + // if (bytewise[0] == 0xba) return 1; + // assert(bytewise[0] == 0xbe); + // return 0; + } + + // up to this point, everything could more or less hide the multiple + // logical bitstream nature of chaining from the toplevel application + // if the toplevel application didn't particularly care. However, at + // the point that we actually read audio back, the multiple-section + // nature must surface: Multiple bitstream sections do not necessarily + // have to have the same number of channels or sampling rate. + // + // read returns the sequential logical bitstream number currently + // being decoded along with the PCM data in order that the toplevel + // application can take action on channel/sample rate changes. This + // number will be incremented even for streamed (non-seekable) streams + // (for seekable streams, it represents the actual logical bitstream + // index within the physical bitstream. Note that the accessor + // functions above are aware of this dichotomy). + // + // input values: buffer) a buffer to hold packed PCM data for return + // length) the byte length requested to be placed into buffer + // bigendianp) should the data be packed LSB first (0) or + // MSB first (1) + // word) word size for output. currently 1 (byte) or + // 2 (16 bit short) + // + // return values: -1) error/hole in data + // 0) EOF + // n) number of bytes of PCM actually returned. The + // below works on a packet-by-packet basis, so the + // return length is not related to the 'length' passed + // in, just guaranteed to fit. + // + // *section) set to the logical bitstream number + + int read(byte[] buffer, int length, int bigendianp, int word, int sgned, int[] bitstream) { + int host_endian = host_is_big_endian(); + int index = 0; + + while (true) { + if (decode_ready) { + float[][] pcm; + float[][][] _pcm = new float[1][][]; + int[] _index = new int[getInfo(-1).channels]; + int samples = vd.synthesis_pcmout(_pcm, _index); + pcm = _pcm[0]; + if (samples != 0) { + // yay! proceed to pack data into the byte buffer + int channels = getInfo(-1).channels; + int bytespersample = word * channels; + if (samples > length / bytespersample) + samples = length / bytespersample; + + // a tight loop to pack each size + { + int val; + if (word == 1) { + int off = (sgned != 0 ? 0 : 128); + for (int j = 0; j < samples; j++) { + for (int i = 0; i < channels; i++) { + val = (int) (pcm[i][_index[i] + j] * 128. + 0.5); + if (val > 127) + val = 127; + else if (val < -128) + val = -128; + buffer[index++] = (byte) (val + off); + } + } + } else { + int off = (sgned != 0 ? 0 : 32768); + + if (host_endian == bigendianp) { + if (sgned != 0) { + for (int i = 0; i < channels; i++) { // It's faster in this order + int src = _index[i]; + int dest = i; + for (int j = 0; j < samples; j++) { + val = (int) (pcm[i][src + j] * 32768. + 0.5); + if (val > 32767) + val = 32767; + else if (val < -32768) + val = -32768; + buffer[dest] = (byte) (val >>> 8); + buffer[dest + 1] = (byte) (val); + dest += channels * 2; + } + } + } else { + for (int i = 0; i < channels; i++) { + float[] src = pcm[i]; + int dest = i; + for (int j = 0; j < samples; j++) { + val = (int) (src[j] * 32768. + 0.5); + if (val > 32767) + val = 32767; + else if (val < -32768) + val = -32768; + buffer[dest] = (byte) ((val + off) >>> 8); + buffer[dest + 1] = (byte) (val + off); + dest += channels * 2; + } + } + } + } else if (bigendianp != 0) { + for (int j = 0; j < samples; j++) { + for (int i = 0; i < channels; i++) { + val = (int) (pcm[i][j] * 32768. + 0.5); + if (val > 32767) + val = 32767; + else if (val < -32768) + val = -32768; + val += off; + buffer[index++] = (byte) (val >>> 8); + buffer[index++] = (byte) val; + } + } + } else { + // int val; + for (int j = 0; j < samples; j++) { + for (int i = 0; i < channels; i++) { + val = (int) (pcm[i][j] * 32768. + 0.5); + if (val > 32767) + val = 32767; + else if (val < -32768) + val = -32768; + val += off; + buffer[index++] = (byte) val; + buffer[index++] = (byte) (val >>> 8); + } + } + } + } + } + + vd.synthesis_read(samples); + pcm_offset += samples; + if (bitstream != null) + bitstream[0] = current_link; + return (samples * bytespersample); + } + } + + // suck in another packet + switch (process_packet(1)) { + case 0: + return (0); + case -1: + return -1; + default: + break; + } + } + } + + public Info[] getInfo() { + return vi; + } + + public Comment[] getComment() { + return vc; + } + + public void close() throws java.io.IOException { + datasource.close(); + } + + class SeekableInputStream extends InputStream { + java.io.RandomAccessFile raf = null; + final String mode = "r"; + + SeekableInputStream(String file) throws java.io.IOException { + raf = new java.io.RandomAccessFile(file, mode); + } + + public int read() throws java.io.IOException { + return raf.read(); + } + + public int read(byte[] buf) throws java.io.IOException { + return raf.read(buf); + } + + public int read(byte[] buf, int s, int len) throws java.io.IOException { + return raf.read(buf, s, len); + } + + public long skip(long n) throws java.io.IOException { + return (long) (raf.skipBytes((int) n)); + } + + public long getLength() throws java.io.IOException { + return raf.length(); + } + + public long tell() throws java.io.IOException { + return raf.getFilePointer(); + } + + public int available() throws java.io.IOException { + return (raf.length() == raf.getFilePointer()) ? 0 : 1; + } + + public void close() throws java.io.IOException { + raf.close(); + } + + public synchronized void mark(int m) { + } + + public synchronized void reset() throws java.io.IOException { + } + + public boolean markSupported() { + return false; + } + + public void seek(long pos) throws java.io.IOException { + raf.seek(pos); + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java index cf0901d..623014d 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java @@ -1,165 +1,228 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import org.teavm.jso.webgl.WebGLBuffer; -import org.teavm.jso.webgl.WebGLFramebuffer; -import org.teavm.jso.webgl.WebGLProgram; -import org.teavm.jso.webgl.WebGLRenderbuffer; -import org.teavm.jso.webgl.WebGLShader; -import org.teavm.jso.webgl.WebGLTexture; -import org.teavm.jso.webgl.WebGLUniformLocation; - -import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLQuery; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLVertexArray; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -class OpenGLObjects { - - static class BufferGL implements IBufferGL { - - final WebGLBuffer ptr; - - BufferGL(WebGLBuffer ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteBuffers(this); - } - - } - - static class BufferArrayGL implements IBufferArrayGL { - - final WebGLVertexArray ptr; - - BufferArrayGL(WebGLVertexArray ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteVertexArrays(this); - } - - } - - static class TextureGL implements ITextureGL { - - final WebGLTexture ptr; - - TextureGL(WebGLTexture ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteTextures(this); - } - - } - - static class ProgramGL implements IProgramGL { - - final WebGLProgram ptr; - - ProgramGL(WebGLProgram ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteProgram(this); - } - - } - - static class UniformGL implements IUniformGL { - - final WebGLUniformLocation ptr; - - UniformGL(WebGLUniformLocation ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - } - - } - - static class ShaderGL implements IShaderGL { - - final WebGLShader ptr; - - ShaderGL(WebGLShader ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteShader(this); - } - - } - - static class FramebufferGL implements IFramebufferGL { - - final WebGLFramebuffer ptr; - - FramebufferGL(WebGLFramebuffer ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteFramebuffer(this); - } - - } - - static class RenderbufferGL implements IRenderbufferGL { - - final WebGLRenderbuffer ptr; - - RenderbufferGL(WebGLRenderbuffer ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteRenderbuffer(this); - } - - } - - static class QueryGL implements IQueryGL { - - final WebGLQuery ptr; - - QueryGL(WebGLQuery ptr) { - this.ptr = ptr; - } - - @Override - public void free() { - PlatformOpenGL._wglDeleteQueries(this); - } - - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import org.teavm.jso.webgl.WebGLBuffer; +import org.teavm.jso.webgl.WebGLFramebuffer; +import org.teavm.jso.webgl.WebGLProgram; +import org.teavm.jso.webgl.WebGLRenderbuffer; +import org.teavm.jso.webgl.WebGLShader; +import org.teavm.jso.webgl.WebGLTexture; +import org.teavm.jso.webgl.WebGLUniformLocation; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLQuery; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLVertexArray; + +/** + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +class OpenGLObjects { + + static class BufferGL implements IBufferGL { + + private static int hashGen = 0; + final WebGLBuffer ptr; + final int hash; + + BufferGL(WebGLBuffer ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteBuffers(this); + } + + } + + static class BufferArrayGL implements IBufferArrayGL { + + private static int hashGen = 0; + final WebGLVertexArray ptr; + final int hash; + + BufferArrayGL(WebGLVertexArray ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteVertexArrays(this); + } + + } + + static class TextureGL implements ITextureGL { + + private static int hashGen = 0; + final WebGLTexture ptr; + final int hash; + + TextureGL(WebGLTexture ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteTextures(this); + } + + } + + static class ProgramGL implements IProgramGL { + + private static int hashGen = 0; + final WebGLProgram ptr; + final int hash; + + ProgramGL(WebGLProgram ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteProgram(this); + } + + } + + static class UniformGL implements IUniformGL { + + private static int hashGen = 0; + final WebGLUniformLocation ptr; + final int hash; + + UniformGL(WebGLUniformLocation ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + } + + } + + static class ShaderGL implements IShaderGL { + + private static int hashGen = 0; + final WebGLShader ptr; + final int hash; + + ShaderGL(WebGLShader ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteShader(this); + } + + } + + static class FramebufferGL implements IFramebufferGL { + + private static int hashGen = 0; + final WebGLFramebuffer ptr; + final int hash; + + FramebufferGL(WebGLFramebuffer ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteFramebuffer(this); + } + + } + + static class RenderbufferGL implements IRenderbufferGL { + + private static int hashGen = 0; + final WebGLRenderbuffer ptr; + final int hash; + + RenderbufferGL(WebGLRenderbuffer ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteRenderbuffer(this); + } + + } + + static class QueryGL implements IQueryGL { + + private static int hashGen = 0; + final WebGLQuery ptr; + final int hash; + + QueryGL(WebGLQuery ptr) { + this.ptr = ptr; + this.hash = ++hashGen; + } + + public int hashCode() { + return hash; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteQueries(this); + } + + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java index c93657e..2e73745 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java @@ -1,326 +1,520 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.teavm.interop.Async; -import org.teavm.interop.AsyncCallback; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSFunctor; -import org.teavm.jso.JSObject; -import org.teavm.jso.browser.Storage; -import org.teavm.jso.browser.TimerHandler; -import org.teavm.jso.browser.Window; -import org.teavm.jso.canvas.CanvasRenderingContext2D; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.html.HTMLCanvasElement; -import org.teavm.jso.dom.html.HTMLDocument; -import org.teavm.jso.dom.html.HTMLInputElement; -import org.teavm.jso.typedarrays.ArrayBuffer; - -import net.lax1dude.eaglercraft.v1_8.Base64; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.DebugConsoleWindow; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformApplication { - - public static void openLink(String url) { - if (url.indexOf(':') == -1) { - url = "http://" + url; - } - Window.current().open(url, "_blank", "noopener,noreferrer"); - } - - public static void setClipboard(String text) { - try { - setClipboard0(text); - } catch (Throwable t) { - PlatformRuntime.logger.error("Exception setting clipboard data"); - } - } - - public static String getClipboard() { - try { - return getClipboard0(); - } catch (Throwable t) { - PlatformRuntime.logger.error("Exception getting clipboard data"); - return ""; - } - } - - @JSFunctor - private static interface StupidFunctionResolveString extends JSObject { - void resolveStr(String s); - } - - @Async - private static native String getClipboard0(); - - private static void getClipboard0(final AsyncCallback cb) { - final long start = System.currentTimeMillis(); - getClipboard1(new StupidFunctionResolveString() { - @Override - public void resolveStr(String s) { - if (System.currentTimeMillis() - start > 500l) { - PlatformInput.unpressCTRL = true; - } - cb.complete(s); - } - }); - } - - @JSBody(params = { - "cb" }, script = "if(!window.navigator.clipboard || !window.navigator.clipboard.readText) cb(\"\"); else window.navigator.clipboard.readText().then(function(s) { cb(s); }, function(s) { cb(\"\"); });") - private static native void getClipboard1(StupidFunctionResolveString cb); - - @JSBody(params = { "str" }, script = "if(window.navigator.clipboard) window.navigator.clipboard.writeText(str);") - private static native void setClipboard0(String str); - - public static void setLocalStorage(String name, byte[] data) { - setLocalStorage(name, data, true); - } - - public static void setLocalStorage(String name, byte[] data, boolean hooks) { - IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter(); - String eagName = adapter.getLocalStorageNamespace() + "." + name; - String b64 = Base64.encodeBase64String(data); - try { - Storage s = Window.current().getLocalStorage(); - if (s != null) { - if (data != null) { - s.setItem(eagName, b64); - } else { - s.removeItem(eagName); - } - } - } catch (Throwable t) { - } - if (hooks) { - adapter.getHooks().callLocalStorageSavedHook(name, b64); - } - } - - public static byte[] getLocalStorage(String name) { - return getLocalStorage(name, true); - } - - public static byte[] getLocalStorage(String name, boolean hooks) { - IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter(); - String eagName = adapter.getLocalStorageNamespace() + "." + name; - byte[] hooked = null; - if (hooks) { - String hookedStr = adapter.getHooks().callLocalStorageLoadHook(eagName); - if (hookedStr != null) { - try { - hooked = Base64.decodeBase64(hookedStr); - } catch (Throwable t) { - PlatformRuntime.logger.error("Invalid Base64 recieved from local storage hook!"); - hooked = null; - } - } - } - if (hooked == null) { - try { - Storage s = Window.current().getLocalStorage(); - if (s != null) { - String str = s.getItem(eagName); - if (str != null) { - return Base64.decodeBase64(str); - } else { - return null; - } - } else { - return null; - } - } catch (Throwable t) { - return null; - } - } else { - return null; - } - } - - private static final DateFormat dateFormatSS = EagRuntime - .fixDateFormat(new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")); - - public static String saveScreenshot() { - String name = "screenshot_" + dateFormatSS.format(new Date()).toString() + ".png"; - int w = PlatformRuntime.canvas.getWidth(); - int h = PlatformRuntime.canvas.getHeight(); - HTMLCanvasElement copyCanvas = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); - copyCanvas.setWidth(w); - copyCanvas.setHeight(h); - CanvasRenderingContext2D ctx = (CanvasRenderingContext2D) copyCanvas.getContext("2d"); - ctx.drawImage(PlatformRuntime.canvas, 0, 0); - saveScreenshot(name, copyCanvas); - return name; - } - - @JSBody(params = { "name", - "cvs" }, script = "var a=document.createElement(\"a\");a.href=cvs.toDataURL(\"image/png\");a.download=name;a.click();") - private static native void saveScreenshot(String name, HTMLCanvasElement cvs); - - public static void showPopup(final String msg) { - Window.setTimeout(new TimerHandler() { - @Override - public void onTimer() { - Window.alert(msg); - } - }, 1); - } - - @JSFunctor - private static interface FileChooserCallback extends JSObject { - void accept(String name, ArrayBuffer buffer); - } - - private static class FileChooserCallbackImpl implements FileChooserCallback { - - private static final FileChooserCallbackImpl instance = new FileChooserCallbackImpl(); - - @Override - public void accept(String name, ArrayBuffer buffer) { - fileChooserHasResult = true; - if (name == null) { - fileChooserResultObject = null; - } else { - fileChooserResultObject = new FileChooserResult(name, TeaVMUtils.wrapByteArrayBuffer(buffer)); - } - } - - } - - private static volatile boolean fileChooserHasResult = false; - private static volatile FileChooserResult fileChooserResultObject = null; - - @JSBody(params = { "inputElement", "callback" }, script = "if(inputElement.files.length > 0) {" - + "const value = inputElement.files[0];" - + "value.arrayBuffer().then(function(arr){ callback(value.name, arr); })" - + ".catch(function(){ callback(null, null); });" - + "} else callback(null, null);") - private static native void getFileChooserResult(HTMLInputElement inputElement, FileChooserCallback callback); - - @JSBody(params = { "inputElement", "value" }, script = "inputElement.accept = value;") - private static native void setAcceptSelection(HTMLInputElement inputElement, String value); - - @JSBody(params = { "inputElement", "enable" }, script = "inputElement.multiple = enable;") - private static native void setMultipleSelection(HTMLInputElement inputElement, boolean enable); - - public static void displayFileChooser(String mime, String ext) { - final HTMLInputElement inputElement = (HTMLInputElement) Window.current().getDocument().createElement("input"); - inputElement.setType("file"); - if (mime == null) { - setAcceptSelection(inputElement, "." + ext); - } else { - setAcceptSelection(inputElement, mime); - } - setMultipleSelection(inputElement, false); - inputElement.addEventListener("change", new EventListener() { - @Override - public void handleEvent(Event evt) { - getFileChooserResult(inputElement, FileChooserCallbackImpl.instance); - } - }); - inputElement.click(); - } - - public static boolean fileChooserHasResult() { - return fileChooserHasResult; - } - - public static FileChooserResult getFileChooserResult() { - fileChooserHasResult = false; - FileChooserResult res = fileChooserResultObject; - fileChooserResultObject = null; - return res; - } - - private static final String faviconURLString = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAR/SURBVEhLtZXZK3ZRFMYPcqXc+gv413DHxVuGIpIhkciQWaRccCNjSCkligwXSOZ5nmfv9zvn2e8+58V753sudmuvvdZ61l5r7XOc8H+GS/D19aUNkPz5+aktQH5/f//4+LBKZKuRkpUtQjCUYG5gD2T38vLy/PwsDfL9/f3Dw8PT05M0b29vnKLhCKCBT4L4gvBLBIei4//4+Hh1dUVEQutUuLu7E83FxQUGnKLBWKfQaA3S+AREVxaEOD8/Pzk50XpzcyMDcH19zdZG3N3d3dzc3Nvb01aX5pQUpQGGQJxcQpfNysoKhUIdHR1o1tbWbInYAgxIPDMzMy8vLzc3FxqOdMoRqwJK8G8ALUYIhHMiSEhIwI6CyIb0qQzC4eGhsXCc1tZWnZIEKzdQJQSXgKxfX18RCM3Z5eWlcfVAxKOjo+Pj49PTU88lTOk2NjbMsePc3t6SAfcgFdszOyMuAdeBg0CQi2lhYUHOeOLDCisN8FzcPFZXV3t7ezHY3t5GQ+6it+2xMASsKhEEWKsmRLRBBUpPvpJ/TpFKFBwKYAiITmicsbYhdHfJAltqhUCVsCQhwslmeXmZxiBQT9c0Ar9E2O3v72sYSE0N1yQArkKy0kBMXLqlZqIZHR3t6empqqqSDcBdhXEJSJ/bUc3q6uq+vj629GB9fR1WsLW1NTs7u7S0RN2locMjIyOEm5ubQ7+4uJienk4/+vv77Y1hwhLBEKhwWHitdVFfX9/Y2Gg2HuLi4owUAysrK8yCG97rh0+ApP5Q2ZycHFlPTExUVFRIBvn5+WhKSkp2dnaMKhptbW2426GgQ/rwuAQCZ1hwFayLiork9hMFBQV1dXVmE0BLS4vqw3QFB8kn4IAxoGPkYpxi4FeDmpqas7Mz4pClAgqGwD48rjY2NmacYqC0tJQ1KSlJWyE5OZkpUKkBAxZVIntAoZh04+Q48fHxPNGBgYHExMT29naj9cBodnZ2mo3jlJWVMeW2OGQck4B1amqqoaGhqamJjx2lGxwcpL0mUgR8fJhsWqJtSkoKU2SbHHUDpkhPBujd8xuQG6PJRM/Pz09PT7O1NNnZ2Tw3fgZkXVhYKCUlUhBATP+hCVyKZGky17RV0g04laayslJ6hlVeFHB4eFhKaogGd0LxtmTgE+hbhKDnPjMzgw8E3qGL2tpaBWpubjYqj2BoaEj6rq4uNATRZ0ZwCbiL6gXEzINk5vCBQJ9rMD4+rkA8QNK036uDg4Py8vLu7m680KjIBNR3zBDoWQM1g98snyB+VSoRW8C/UwR81/SvhgNj9JOTkwwVERUdRBEI0BAdLRVERkhLS8vIyEDQlrsTPTU1lVFhKxARvZgUlFLbegCf4BvIsbi4mIg4E5EogIHhiKCMtU0WUFiVy06j5fAJIDdSBDQw+PegDfBRcbOPwH4F9LuFWIIQdQNKwWqzIE0aoFUaBsw+SQuFw0uNtC9A+F4i3QNrbg3IDn+SAsHh+wYiEpeyBEMLv/cAO6KzAijxxB+Y4wisBhssJUhjEbPJf4Nw+B+JXqLW3bw+wQAAAABJRU5ErkJggg=="; - - public static String faviconURLTeaVM() { - return faviconURLString.substring(0); - } - - @JSBody(params = { "doc", "str" }, script = "doc.write(str);doc.close();") - private static native void documentWrite(HTMLDocument doc, String str); - - public static void openCreditsPopup(String text) { - Window currentWin = Window.current(); - - int w = (int) (850 * currentWin.getDevicePixelRatio()); - int h = (int) (700 * currentWin.getDevicePixelRatio()); - - int x = (currentWin.getScreen().getWidth() - w) / 2; - int y = (currentWin.getScreen().getHeight() - h) / 2; - - Window newWin = Window.current().open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h - + ",menubar=0,status=0,titlebar=0,toolbar=0"); - if (newWin == null) { - Window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!"); - return; - } - - newWin.focus(); - documentWrite(newWin.getDocument(), "" - + "EaglercraftL 1.9 Credits" - + "" - + "
" + text + "
"); - } - - public static void clearFileChooserResult() { - fileChooserHasResult = false; - fileChooserResultObject = null; - } - - @JSBody(params = { "name", - "buf" }, script = "var hr = window.URL.createObjectURL(new Blob([buf], {type: \"octet/stream\"}));" + - "var a = document.createElement(\"a\");" + - "a.href = hr; a.download = name; a.click();" + - "window.URL.revokeObjectURL(hr);") - private static final native void downloadBytesImpl(String str, ArrayBuffer buf); - - public static final void downloadFileWithName(String str, byte[] dat) { - downloadBytesImpl(str, TeaVMUtils.unwrapArrayBuffer(dat)); - } - - public static void showDebugConsole() { - DebugConsoleWindow.showDebugConsole(); - } - - public static void addLogMessage(String text, boolean err) { - DebugConsoleWindow.addLogMessage(text, err); - } - - public static boolean isShowingDebugConsole() { - return DebugConsoleWindow.isShowingDebugConsole(); - } - - @JSBody(params = { "str" }, script = "window.minecraftServer = str;") - public static native void setMCServerWindowGlobal(String str); -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URI; +import java.net.URISyntaxException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.browser.Storage; +import org.teavm.jso.browser.TimerHandler; +import org.teavm.jso.browser.Window; +import org.teavm.jso.canvas.CanvasRenderingContext2D; +import org.teavm.jso.dom.css.CSSStyleDeclaration; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.html.HTMLCanvasElement; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLInputElement; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.DebugConsoleWindow; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLHandle; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformApplication { + + public static void openLink(String url) { + if(url.indexOf(':') == -1) { + url = "http://" + url; + } + URI parsedURL; + try { + parsedURL = new URI(url); + }catch(URISyntaxException ex) { + PlatformRuntime.logger.error("Refusing to open invalid URL: {}", url); + return; + } + try { + Window.current().open(parsedURL.toString(), "_blank", "noopener,noreferrer"); + }catch(Throwable t) { + PlatformRuntime.logger.error("Exception opening link!"); + } + } + + public static void setClipboard(String text) { + try { + setClipboard0(text); + }catch(Throwable t) { + PlatformRuntime.logger.error("Exception setting clipboard data"); + try { + Window.prompt("Here is the text you're trying to copy:", text); + }catch(Throwable t2) { + } + } + } + + public static String getClipboard() { + try { + return getClipboard0(); + }catch(Throwable t) { + PlatformRuntime.logger.error("Exception getting clipboard data"); + try { + String ret = Window.prompt("Please enter the text to paste:"); + return ret != null ? ret : ""; + }catch(Throwable t2) { + return ""; + } + } + } + + @JSFunctor + private static interface StupidFunctionResolveString extends JSObject { + void resolveStr(String s); + } + + @Async + private static native String getClipboard0(); + + private static void getClipboard0(final AsyncCallback cb) { + final long start = PlatformRuntime.steadyTimeMillis(); + getClipboard1(new StupidFunctionResolveString() { + @Override + public void resolveStr(String s) { + if(PlatformRuntime.steadyTimeMillis() - start > 500l) { + PlatformInput.unpressCTRL = true; + } + cb.complete(s); + } + }); + } + + @JSBody(params = { "cb" }, script = "if(!navigator.clipboard) { cb(prompt(\"Please enter the text to paste:\") || \"\"); } else if (!navigator.clipboard.readText) cb(\"\"); else navigator.clipboard.readText().then(function(s) { cb(s); }, function(s) { cb(\"\"); });") + private static native void getClipboard1(StupidFunctionResolveString cb); + + @JSBody(params = { "str" }, script = "if(navigator.clipboard) clipboard.writeText(str);") + private static native void setClipboard0(String str); + + public static void setLocalStorage(String name, byte[] data) { + setLocalStorage(name, data, true); + } + + public static void setLocalStorage(String name, byte[] data, boolean hooks) { + IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter(); + String eagName = adapter.getLocalStorageNamespace() + "." + name; + String b64 = data != null ? Base64.encodeBase64String(data) : null; + try { + Storage s = Window.current().getLocalStorage(); + if(s != null) { + if(b64 != null) { + s.setItem(eagName, b64); + }else { + s.removeItem(eagName); + } + } + }catch(Throwable t) { + } + if(hooks) { + adapter.getHooks().callLocalStorageSavedHook(name, b64); + } + } + + public static byte[] getLocalStorage(String name) { + return getLocalStorage(name, true); + } + + public static byte[] getLocalStorage(String name, boolean hooks) { + IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter(); + String eagName = adapter.getLocalStorageNamespace() + "." + name; + byte[] hooked = null; + if(hooks) { + String hookedStr = adapter.getHooks().callLocalStorageLoadHook(eagName); + if(hookedStr != null) { + try { + hooked = Base64.decodeBase64(hookedStr); + }catch(Throwable t) { + PlatformRuntime.logger.error("Invalid Base64 recieved from local storage hook!"); + hooked = null; + } + } + } + if(hooked == null) { + try { + Storage s = Window.current().getLocalStorage(); + if(s != null) { + String str = s.getItem(eagName); + if(str != null) { + return Base64.decodeBase64(str); + }else { + return null; + } + }else { + return null; + } + }catch(Throwable t) { + return null; + } + }else { + return hooked; + } + } + + private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss"); + + public static String saveScreenshot() { + PlatformOpenGL._wglBindFramebuffer(0x8D40, null); + int w = PlatformInput.getWindowWidth(); + int h = PlatformInput.getWindowHeight(); + ByteBuffer buf = PlatformRuntime.allocateByteBuffer(w * h * 4); + PlatformOpenGL._wglReadPixels(0, 0, w, h, 6408, 5121, buf); + for(int i = 3, l = buf.remaining(); i < l; i += 4) { + buf.put(i, (byte)0xFF); + } + String name = "screenshot_" + dateFormatSS.format(new Date()).toString() + ".png"; + HTMLCanvasElement copyCanvas = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); + copyCanvas.setWidth(w); + copyCanvas.setHeight(h); + CanvasRenderingContext2D ctx = (CanvasRenderingContext2D) copyCanvas.getContext("2d", PlatformAssets.youEagler()); + putImageData(ctx, EaglerArrayBufferAllocator.getDataView8(buf).getBuffer(), w, h); + PlatformRuntime.freeByteBuffer(buf); + downloadScreenshot(copyCanvas, name, PlatformRuntime.parent); + return name; + } + + @JSBody(params = { "ctx", "buffer", "w", "h" }, script = "var imgData = ctx.createImageData(w, h); var ww = w * 4; for(var i = 0; i < h; ++i) { imgData.data.set(new Uint8ClampedArray(buffer, (h - i - 1) * ww, ww), i * ww); } ctx.putImageData(imgData, 0, 0);") + private static native JSObject putImageData(CanvasRenderingContext2D ctx, ArrayBuffer buffer, int w, int h); + + @JSBody(params = { "cvs", "name", "parentElement" }, script = + "var vigg = function(el, url){" + + "el.style.position = \"absolute\";" + + "el.style.left = \"0px\";" + + "el.style.top = \"0px\";" + + "el.style.zIndex = \"-100\";" + + "el.style.color = \"transparent\";" + + "el.innerText = \"Download Screenshot\";" + + "el.href = url;" + + "el.target = \"_blank\";" + + "el.download = name;" + + "parentElement.appendChild(el);" + + "setTimeout(function() { el.click();" + + "setTimeout(function() { parentElement.removeChild(el); }, 50);" + + "}, 50);" + + "}; setTimeout(function() { vigg(document.createElement(\"a\"), cvs.toDataURL(\"image/png\")); }, 50);") + private static native void downloadScreenshot(HTMLCanvasElement cvs, String name, HTMLElement parentElement); + + public static void showPopup(final String msg) { + Window.setTimeout(new TimerHandler() { + @Override + public void onTimer() { + Window.alert(msg); + } + }, 1); + } + + @JSFunctor + private static interface FileChooserCallback extends JSObject { + void accept(String name, ArrayBuffer buffer); + } + + private static class FileChooserCallbackImpl implements FileChooserCallback { + + private static final FileChooserCallbackImpl instance = new FileChooserCallbackImpl(); + + @Override + public void accept(String name, ArrayBuffer buffer) { + fileChooserHasResult = true; + if(name == null) { + fileChooserResultObject = null; + }else { + fileChooserResultObject = new FileChooserResult(name, TeaVMUtils.wrapByteArrayBuffer(buffer)); + } + } + + } + + private static final int FILE_CHOOSER_IMPL_CORE = 0; + private static final int FILE_CHOOSER_IMPL_LEGACY = 1; + private static int fileChooserImpl = -1; + private static boolean fileChooserHasResult = false; + private static FileChooserResult fileChooserResultObject = null; + private static HTMLInputElement fileChooserElement = null; + private static HTMLElement fileChooserMobileElement = null; + + @JSBody(params = { "inputElement", "callback" }, script = + "if(inputElement.files.length > 0) {" + + "var eag = function(value){" + + "value.arrayBuffer().then(function(arr){ callback(value.name, arr); })" + + ".catch(function(){ callback(null, null); });" + + "}; eag(inputElement.files[0]); } else callback(null, null);") + private static native void getFileChooserResultNew(HTMLInputElement inputElement, FileChooserCallback callback); + + @JSBody(params = { "inputElement", "callback" }, script = + "if(inputElement.files.length > 0) {" + + "var eag = function(value, reader){" + + "reader.addEventListener(\"loadend\",function(evt){ callback(value.name, reader.result); });" + + "reader.addEventListener(\"error\",function(evt){ callback(null, null); });" + + "reader.readAsArrayBuffer(value);" + + "}; eag(inputElement.files[0], new FileReader()); } else callback(null, null);") + private static native void getFileChooserResultLegacy(HTMLInputElement inputElement, FileChooserCallback callback); + + private static void getFileChooserResult(HTMLInputElement inputElement, FileChooserCallback callback) { + if(fileChooserImpl == -1) { + fileChooserImpl = getFileChooserImpl(); + if(fileChooserImpl == FILE_CHOOSER_IMPL_LEGACY) { + PlatformRuntime.logger.info("Note: using legacy FileReader implementation because File.prototype.arrayBuffer() is not supported"); + } + } + switch(fileChooserImpl) { + case FILE_CHOOSER_IMPL_CORE: + getFileChooserResultNew(inputElement, callback); + break; + case FILE_CHOOSER_IMPL_LEGACY: + getFileChooserResultLegacy(inputElement, callback); + break; + default: + throw new UnsupportedOperationException(); + } + } + + @JSBody(params = { }, script = "return (typeof File.prototype.arrayBuffer === \"function\") ? 0 : 1;") + private static native int getFileChooserImpl(); + + @JSBody(params = { "inputElement", "value" }, script = "inputElement.accept = value;") + private static native void setAcceptSelection(HTMLInputElement inputElement, String value); + + @JSBody(params = { "inputElement", "enable" }, script = "inputElement.multiple = enable;") + private static native void setMultipleSelection(HTMLInputElement inputElement, boolean enable); + + public static void displayFileChooser(String mime, String ext) { + clearFileChooserResult(); + final HTMLDocument doc = PlatformRuntime.doc != null ? PlatformRuntime.doc : Window.current().getDocument(); + if(PlatformInput.isLikelyMobileBrowser) { + final HTMLElement element = fileChooserMobileElement = doc.createElement("div"); + element.getClassList().add("_eaglercraftX_mobile_file_chooser_popup"); + CSSStyleDeclaration decl = element.getStyle(); + decl.setProperty("position", "absolute"); + decl.setProperty("background-color", "white"); + decl.setProperty("font-family", "sans-serif"); + decl.setProperty("top", "10%"); + decl.setProperty("left", "10%"); + decl.setProperty("right", "10%"); + decl.setProperty("border", "5px double black"); + decl.setProperty("padding", "15px"); + decl.setProperty("text-align", "left"); + decl.setProperty("font-size", "20px"); + decl.setProperty("user-select", "none"); + decl.setProperty("z-index", "150"); + final HTMLElement fileChooserTitle = doc.createElement("h3"); + fileChooserTitle.appendChild(doc.createTextNode("File Chooser")); + element.appendChild(fileChooserTitle); + final HTMLElement inputElementContainer = doc.createElement("p"); + final HTMLInputElement inputElement = fileChooserElement = (HTMLInputElement) doc.createElement("input"); + inputElement.setType("file"); + if(mime == null) { + setAcceptSelection(inputElement, "." + ext); + }else { + setAcceptSelection(inputElement, mime); + } + setMultipleSelection(inputElement, false); + inputElementContainer.appendChild(inputElement); + element.appendChild(inputElementContainer); + final HTMLElement fileChooserButtons = doc.createElement("p"); + final HTMLElement fileChooserButtonCancel = doc.createElement("button"); + fileChooserButtonCancel.getClassList().add("_eaglercraftX_mobile_file_chooser_btn_cancel"); + fileChooserButtonCancel.getStyle().setProperty("font-size", "1.0em"); + fileChooserButtonCancel.addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(fileChooserMobileElement == element) { + PlatformRuntime.parent.removeChild(element); + fileChooserMobileElement = null; + fileChooserElement = null; + } + } + }); + fileChooserButtonCancel.appendChild(doc.createTextNode("Cancel")); + fileChooserButtons.appendChild(fileChooserButtonCancel); + fileChooserButtons.appendChild(doc.createTextNode(" ")); + final HTMLElement fileChooserButtonDone = doc.createElement("button"); + fileChooserButtonDone.getClassList().add("_eaglercraftX_mobile_file_chooser_btn_done"); + fileChooserButtonDone.getStyle().setProperty("font-size", "1.0em"); + fileChooserButtonDone.getStyle().setProperty("font-weight", "bold"); + fileChooserButtonDone.addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(fileChooserMobileElement == element) { + getFileChooserResult(inputElement, FileChooserCallbackImpl.instance); + PlatformRuntime.parent.removeChild(element); + fileChooserMobileElement = null; + fileChooserElement = null; + } + } + }); + fileChooserButtonDone.appendChild(doc.createTextNode("Done")); + fileChooserButtons.appendChild(fileChooserButtonDone); + element.appendChild(fileChooserButtons); + PlatformRuntime.parent.appendChild(element); + }else { + final HTMLInputElement inputElement = fileChooserElement = (HTMLInputElement) doc.createElement("input"); + inputElement.setType("file"); + CSSStyleDeclaration decl = inputElement.getStyle(); + decl.setProperty("position", "absolute"); + decl.setProperty("left", "0px"); + decl.setProperty("top", "0px"); + decl.setProperty("z-index", "-100"); + if(mime == null) { + setAcceptSelection(inputElement, "." + ext); + }else { + setAcceptSelection(inputElement, mime); + } + setMultipleSelection(inputElement, false); + inputElement.addEventListener("change", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(fileChooserElement == inputElement) { + getFileChooserResult(inputElement, FileChooserCallbackImpl.instance); + PlatformRuntime.parent.removeChild(inputElement); + fileChooserElement = null; + } + } + }); + PlatformRuntime.parent.appendChild(inputElement); + Window.setTimeout(new TimerHandler() { + @Override + public void onTimer() { + inputElement.click(); + } + }, 50); + } + } + + public static boolean fileChooserHasResult() { + return fileChooserHasResult; + } + + public static FileChooserResult getFileChooserResult() { + fileChooserHasResult = false; + FileChooserResult res = fileChooserResultObject; + fileChooserResultObject = null; + return res; + } + + private static final String faviconURLString = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAR/SURBVEhLtZXZK3ZRFMYPcqXc+gv413DHxVuGIpIhkciQWaRccCNjSCkligwXSOZ5nmfv9zvn2e8+58V753sudmuvvdZ61l5r7XOc8H+GS/D19aUNkPz5+aktQH5/f//4+LBKZKuRkpUtQjCUYG5gD2T38vLy/PwsDfL9/f3Dw8PT05M0b29vnKLhCKCBT4L4gvBLBIei4//4+Hh1dUVEQutUuLu7E83FxQUGnKLBWKfQaA3S+AREVxaEOD8/Pzk50XpzcyMDcH19zdZG3N3d3dzc3Nvb01aX5pQUpQGGQJxcQpfNysoKhUIdHR1o1tbWbInYAgxIPDMzMy8vLzc3FxqOdMoRqwJK8G8ALUYIhHMiSEhIwI6CyIb0qQzC4eGhsXCc1tZWnZIEKzdQJQSXgKxfX18RCM3Z5eWlcfVAxKOjo+Pj49PTU88lTOk2NjbMsePc3t6SAfcgFdszOyMuAdeBg0CQi2lhYUHOeOLDCisN8FzcPFZXV3t7ezHY3t5GQ+6it+2xMASsKhEEWKsmRLRBBUpPvpJ/TpFKFBwKYAiITmicsbYhdHfJAltqhUCVsCQhwslmeXmZxiBQT9c0Ar9E2O3v72sYSE0N1yQArkKy0kBMXLqlZqIZHR3t6empqqqSDcBdhXEJSJ/bUc3q6uq+vj629GB9fR1WsLW1NTs7u7S0RN2locMjIyOEm5ubQ7+4uJienk4/+vv77Y1hwhLBEKhwWHitdVFfX9/Y2Gg2HuLi4owUAysrK8yCG97rh0+ApP5Q2ZycHFlPTExUVFRIBvn5+WhKSkp2dnaMKhptbW2426GgQ/rwuAQCZ1hwFayLiork9hMFBQV1dXVmE0BLS4vqw3QFB8kn4IAxoGPkYpxi4FeDmpqas7Mz4pClAgqGwD48rjY2NmacYqC0tJQ1KSlJWyE5OZkpUKkBAxZVIntAoZh04+Q48fHxPNGBgYHExMT29naj9cBodnZ2mo3jlJWVMeW2OGQck4B1amqqoaGhqamJjx2lGxwcpL0mUgR8fJhsWqJtSkoKU2SbHHUDpkhPBujd8xuQG6PJRM/Pz09PT7O1NNnZ2Tw3fgZkXVhYKCUlUhBATP+hCVyKZGky17RV0g04laayslJ6hlVeFHB4eFhKaogGd0LxtmTgE+hbhKDnPjMzgw8E3qGL2tpaBWpubjYqj2BoaEj6rq4uNATRZ0ZwCbiL6gXEzINk5vCBQJ9rMD4+rkA8QNK036uDg4Py8vLu7m680KjIBNR3zBDoWQM1g98snyB+VSoRW8C/UwR81/SvhgNj9JOTkwwVERUdRBEI0BAdLRVERkhLS8vIyEDQlrsTPTU1lVFhKxARvZgUlFLbegCf4BvIsbi4mIg4E5EogIHhiKCMtU0WUFiVy06j5fAJIDdSBDQw+PegDfBRcbOPwH4F9LuFWIIQdQNKwWqzIE0aoFUaBsw+SQuFw0uNtC9A+F4i3QNrbg3IDn+SAsHh+wYiEpeyBEMLv/cAO6KzAijxxB+Y4wisBhssJUhjEbPJf4Nw+B+JXqLW3bw+wQAAAABJRU5ErkJggg=="; + + public static String faviconURLTeaVM() { + return faviconURLString.substring(0); + } + + @JSBody(params = { "doc", "str" }, script = "doc.write(str);doc.close();") + private static native void documentWrite(HTMLDocument doc, String str); + + public static void openCreditsPopup(String text) { + Window currentWin = PlatformRuntime.win; + + int w = (int)(850 * PlatformInput.getDPI()); + int h = (int)(700 * PlatformInput.getDPI()); + + int x = (currentWin.getScreen().getWidth() - w) / 2; + int y = (currentWin.getScreen().getHeight() - h) / 2; + + Window newWin = Window.current().open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h + ",menubar=0,status=0,titlebar=0,toolbar=0"); + if(newWin == null || TeaVMUtils.isNotTruthy(newWin)) { + Window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!"); + return; + } + + newWin.focus(); + documentWrite(newWin.getDocument(), "" + + "EaglercraftX 1.8 Credits" + + "" + + "
" + text + "
"); + } + + public static void clearFileChooserResult() { + fileChooserHasResult = false; + fileChooserResultObject = null; + if(fileChooserMobileElement != null) { + PlatformRuntime.parent.removeChild(fileChooserMobileElement); + fileChooserMobileElement = null; + fileChooserElement = null; + }else if(fileChooserElement != null) { + PlatformRuntime.parent.removeChild(fileChooserElement); + fileChooserElement = null; + } + } + + @JSFunctor + private static interface DownloadBytesBlobURLRevoke extends JSObject { + void call(); + } + + @JSBody(params = { "name", "url", "revokeFunc", "parentElement" }, script = + "var vigg = function(el){" + + "el.style.position = \"absolute\";" + + "el.style.left = \"0px\";" + + "el.style.top = \"0px\";" + + "el.style.zIndex = \"-100\";" + + "el.style.color = \"transparent\";" + + "el.innerText = \"Download File\";" + + "el.href = url;" + + "el.target = \"_blank\";" + + "el.download = name;" + + "parentElement.appendChild(el);" + + "setTimeout(function() { el.click();" + + "setTimeout(function() { parentElement.removeChild(el); }, 50);" + + "setTimeout(function() { revokeFunc(); }, 60000);" + + "}, 50);" + + "}; vigg(document.createElement(\"a\"));") + private static native void downloadBytesImpl(String str, String url, DownloadBytesBlobURLRevoke revokeFunc, HTMLElement parentElement); + + public static void downloadFileWithName(String str, byte[] dat) { + TeaVMBlobURLHandle blobHandle = TeaVMBlobURLManager.registerNewURLByte(dat, "application/octet-stream"); + downloadBytesImpl(str, blobHandle.toExternalForm(), blobHandle::release, PlatformRuntime.parent); + } + + public static void downloadFileWithNameTeaVM(String str, ArrayBuffer dat) { + TeaVMBlobURLHandle blobHandle = TeaVMBlobURLManager.registerNewURLArrayBuffer(dat, "application/octet-stream"); + downloadBytesImpl(str, blobHandle.toExternalForm(), blobHandle::release, PlatformRuntime.parent); + } + + public static void showDebugConsole() { + DebugConsoleWindow.showDebugConsole(); + } + + public static void addLogMessage(String text, boolean err) { + DebugConsoleWindow.addLogMessage(text, err); + } + + public static boolean isShowingDebugConsole() { + return DebugConsoleWindow.isShowingDebugConsole(); + } + + @JSBody(params = { "str" }, script = "window.minecraftServer = str;") + public static native void setMCServerWindowGlobal(String str); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java index cef5208..96b6774 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java @@ -1,150 +1,174 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.teavm.interop.Async; -import org.teavm.interop.AsyncCallback; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.browser.Window; -import org.teavm.jso.canvas.CanvasRenderingContext2D; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.html.HTMLCanvasElement; -import org.teavm.jso.dom.html.HTMLImageElement; -import org.teavm.jso.dom.xml.Document; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.Int32Array; -import org.teavm.jso.typedarrays.Uint8ClampedArray; - -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; -import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; - -/** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformAssets { - - private static final byte[] MISSING_FILE = new byte[0]; - - static final Map assets = new HashMap(); - - public static final byte[] getResourceBytes(String path) { - if (path.startsWith("/")) { - path = path.substring(1); - } - byte[] data = assets.get(path); - if (data == null && path.startsWith("assets/minecraft/lang/") && !path.endsWith(".mcmeta")) { - ArrayBuffer file = PlatformRuntime.downloadRemoteURI( - ClientMain.configLocalesFolder + "/" + path.substring(22)); - if (file != null && file.getByteLength() > 0) { - data = TeaVMUtils.wrapByteArrayBuffer(file); - assets.put(path, data); - return data; - } else { - assets.put(path, MISSING_FILE); - return null; - } - } else { - return data == MISSING_FILE ? null : data; - } - } - - public static final ImageData loadImageFile(InputStream data) { - byte[] b = EaglerInputStream.inputStreamToBytesQuiet(data); - if (b != null) { - return loadImageFile(b); - } else { - return null; - } - } - - private static HTMLCanvasElement imageLoadCanvas = null; - private static CanvasRenderingContext2D imageLoadContext = null; - - public static ImageData loadImageFile(byte[] data) { - return loadImageFile(TeaVMUtils.unwrapArrayBuffer(data)); - } - - @JSBody(params = {}, script = "return { willReadFrequently: true };") - public static native JSObject youEagler(); - - @JSBody(params = { "ctx" }, script = "ctx.imageSmoothingEnabled = false;") - private static native void disableImageSmoothing(CanvasRenderingContext2D ctx); - - @Async - private static native ImageData loadImageFile(ArrayBuffer data); - - private static void loadImageFile(ArrayBuffer data, final AsyncCallback ret) { - final Document doc = Window.current().getDocument(); - final HTMLImageElement toLoad = (HTMLImageElement) doc.createElement("img"); - toLoad.addEventListener("load", new EventListener() { - @Override - public void handleEvent(Event evt) { - if (imageLoadCanvas == null) { - imageLoadCanvas = (HTMLCanvasElement) doc.createElement("canvas"); - } - if (imageLoadCanvas.getWidth() < toLoad.getWidth()) { - imageLoadCanvas.setWidth(toLoad.getWidth()); - } - if (imageLoadCanvas.getHeight() < toLoad.getHeight()) { - imageLoadCanvas.setHeight(toLoad.getHeight()); - } - if (imageLoadContext == null) { - imageLoadContext = (CanvasRenderingContext2D) imageLoadCanvas.getContext("2d", youEagler()); - disableImageSmoothing(imageLoadContext); - } - imageLoadContext.clearRect(0, 0, toLoad.getWidth(), toLoad.getHeight()); - imageLoadContext.drawImage(toLoad, 0, 0, toLoad.getWidth(), toLoad.getHeight()); - org.teavm.jso.canvas.ImageData pxlsDat = imageLoadContext.getImageData(0, 0, toLoad.getWidth(), - toLoad.getHeight()); - Uint8ClampedArray pxls = pxlsDat.getData(); - int totalPixels = pxlsDat.getWidth() * pxlsDat.getHeight(); - TeaVMUtils.freeDataURL(toLoad.getSrc()); - if (pxls.getByteLength() < totalPixels << 2) { - ret.complete(null); - return; - } - ret.complete(new ImageData(pxlsDat.getWidth(), pxlsDat.getHeight(), - TeaVMUtils.wrapIntArrayBuffer(pxls.getBuffer()), true)); - } - }); - toLoad.addEventListener("error", new EventListener() { - @Override - public void handleEvent(Event evt) { - TeaVMUtils.freeDataURL(toLoad.getSrc()); - ret.complete(null); - } - }); - String src = TeaVMUtils.getDataURL(data, "image/png"); - if (src != null) { - toLoad.setSrc(src); - } else { - ret.complete(null); - } - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.browser.Window; +import org.teavm.jso.canvas.CanvasRenderingContext2D; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.html.HTMLCanvasElement; +import org.teavm.jso.dom.html.HTMLImageElement; +import org.teavm.jso.dom.xml.Document; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.Uint8ClampedArray; + +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLHandle; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +/** + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformAssets { + + private static final byte[] MISSING_FILE = new byte[0]; + + static Map assets = new HashMap<>(); + + public static boolean getResourceExists(String path) { + if(path.startsWith("/")) { + path = path.substring(1); + } + byte[] ret = assets.get(path); + if(ret != null && ret != MISSING_FILE) { + return true; + }else { + if(path.startsWith("assets/minecraft/lang/") && !path.endsWith(".mcmeta")) { + ArrayBuffer file = PlatformRuntime.downloadRemoteURI( + ClientMain.configLocalesFolder + "/" + path.substring(22)); + if(file != null) { + assets.put(path, TeaVMUtils.wrapByteArrayBuffer(file)); + return true; + }else { + assets.put(path, MISSING_FILE); + return false; + } + }else { + return false; + } + } + } + + public static byte[] getResourceBytes(String path) { + if(path.startsWith("/")) { + path = path.substring(1); + } + byte[] data = assets.get(path); + if(data == null && path.startsWith("assets/minecraft/lang/") && !path.endsWith(".mcmeta")) { + ArrayBuffer file = PlatformRuntime.downloadRemoteURI( + ClientMain.configLocalesFolder + "/" + path.substring(22)); + if(file != null) { + data = TeaVMUtils.wrapByteArrayBuffer(file); + assets.put(path, data); + return data; + }else { + assets.put(path, MISSING_FILE); + return null; + } + }else { + return data == MISSING_FILE ? null : data; + } + } + + public static ImageData loadImageFile(InputStream data) { + return loadImageFile(data, "image/png"); + } + + public static ImageData loadImageFile(InputStream data, String mime) { + byte[] b = EaglerInputStream.inputStreamToBytesQuiet(data); + if(b != null) { + return loadImageFile(b, mime); + }else { + return null; + } + } + + private static HTMLCanvasElement imageLoadCanvas = null; + private static CanvasRenderingContext2D imageLoadContext = null; + + public static ImageData loadImageFile(byte[] data) { + return loadImageFile(data, "image/png"); + } + + @JSBody(params = { }, script = "return { willReadFrequently: true };") + static native JSObject youEagler(); + + @JSBody(params = { "ctx" }, script = "ctx.imageSmoothingEnabled = false;") + private static native void disableImageSmoothing(CanvasRenderingContext2D ctx); + + @Async + public static native ImageData loadImageFile(byte[] data, String mime); + + private static void loadImageFile(byte[] data, String mime, final AsyncCallback ret) { + final Document doc = Window.current().getDocument(); + final HTMLImageElement toLoad = (HTMLImageElement) doc.createElement("img"); + final TeaVMBlobURLHandle[] src = new TeaVMBlobURLHandle[1]; + toLoad.addEventListener("load", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(imageLoadCanvas == null) { + imageLoadCanvas = (HTMLCanvasElement) doc.createElement("canvas"); + } + if(imageLoadCanvas.getWidth() < toLoad.getWidth()) { + imageLoadCanvas.setWidth(toLoad.getWidth()); + } + if(imageLoadCanvas.getHeight() < toLoad.getHeight()) { + imageLoadCanvas.setHeight(toLoad.getHeight()); + } + if(imageLoadContext == null) { + imageLoadContext = (CanvasRenderingContext2D) imageLoadCanvas.getContext("2d", youEagler()); + disableImageSmoothing(imageLoadContext); + } + imageLoadContext.clearRect(0, 0, toLoad.getWidth(), toLoad.getHeight()); + imageLoadContext.drawImage(toLoad, 0, 0, toLoad.getWidth(), toLoad.getHeight()); + org.teavm.jso.canvas.ImageData pxlsDat = imageLoadContext.getImageData(0, 0, toLoad.getWidth(), toLoad.getHeight()); + Uint8ClampedArray pxls = pxlsDat.getData(); + int totalPixels = pxlsDat.getWidth() * pxlsDat.getHeight(); + TeaVMBlobURLManager.releaseURL(src[0]); + if(pxls.getByteLength() < totalPixels << 2) { + ret.complete(null); + return; + } + ret.complete(new ImageData(pxlsDat.getWidth(), pxlsDat.getHeight(), TeaVMUtils.wrapIntArrayBuffer(pxls.getBuffer()), true)); + } + }); + toLoad.addEventListener("error", new EventListener() { + @Override + public void handleEvent(Event evt) { + TeaVMBlobURLManager.releaseURL(src[0]); + ret.complete(null); + } + }); + src[0] = TeaVMBlobURLManager.registerNewURLByte(data, mime); + if(src[0] != null) { + toLoad.setSrc(src[0].toExternalForm()); + }else { + ret.complete(null); + } + } + + public static void freeAssetRepoTeaVM() { + assets = new HashMap<>(); + } +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java index c2a1a56..fa1c9a6 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java @@ -1,389 +1,590 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.audio.SoundCategory; -import org.teavm.interop.Async; -import org.teavm.interop.AsyncCallback; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.Uint8Array; -import org.teavm.jso.webaudio.AudioBuffer; -import org.teavm.jso.webaudio.AudioBufferSourceNode; -import org.teavm.jso.webaudio.AudioContext; -import org.teavm.jso.webaudio.AudioListener; -import org.teavm.jso.webaudio.DecodeErrorCallback; -import org.teavm.jso.webaudio.DecodeSuccessCallback; -import org.teavm.jso.webaudio.GainNode; -import org.teavm.jso.webaudio.MediaEvent; -import org.teavm.jso.webaudio.MediaStream; -import org.teavm.jso.webaudio.MediaStreamAudioDestinationNode; -import org.teavm.jso.webaudio.PannerNode; - -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.minecraft.util.MathHelper; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformAudio { - - static final Logger logger = LogManager.getLogger("BrowserAudio"); - - static AudioContext audioctx = null; - static MediaStreamAudioDestinationNode recDest = null; - private static final Map soundCache = new HashMap(); - - private static long cacheFreeTimer = 0l; - - protected static class BrowserAudioResource implements IAudioResource { - - protected AudioBuffer buffer; - protected long cacheHit = 0l; - - protected BrowserAudioResource(AudioBuffer buffer) { - this.buffer = buffer; - } - - } - - protected static class BrowserAudioHandle implements IAudioHandle, EventListener { - - protected final BrowserAudioResource resource; - protected AudioBufferSourceNode source; - protected final PannerNode panner; - protected final GainNode gain; - protected float pitch; - protected boolean isPaused = false; - protected boolean isEnded = false; - - public BrowserAudioHandle(BrowserAudioResource resource, AudioBufferSourceNode source, PannerNode panner, - GainNode gain, float pitch) { - this.resource = resource; - this.source = source; - this.panner = panner; - this.gain = gain; - this.pitch = pitch; - source.setOnEnded(this); - } - - @Override - public void pause(boolean setPaused) { - if (setPaused) { - if (!isPaused) { - isPaused = true; - source.getPlaybackRate().setValue(0.0f); - } - } else { - if (isPaused) { - isPaused = false; - source.getPlaybackRate().setValue(pitch); - } - } - } - - @Override - public void restart() { - if (isEnded) { - isEnded = false; - AudioBufferSourceNode src = audioctx.createBufferSource(); - resource.cacheHit = System.currentTimeMillis(); - src.setBuffer(resource.buffer); - src.getPlaybackRate().setValue(pitch); - source.disconnect(); - src.connect(panner == null ? gain : panner); - source = src; - source.start(); - } else { - source.getPlaybackRate().setValue(pitch); - source.start(0.0); - } - } - - @Override - public void move(float x, float y, float z) { - if (panner != null) { - panner.setPosition(x, y, z); - } - } - - @Override - public void pitch(float f) { - pitch = f; - if (!isPaused) { - source.getPlaybackRate().setValue(pitch); - } - } - - @Override - public void gain(float f) { - if (panner != null) { - float v1 = f * 16.0f; - if (v1 < 16.0f) - v1 = 16.0f; - panner.setMaxDistance(v1); - } - float v2 = f; - if (v2 > 1.0f) - v2 = 1.0f; - gain.getGain().setValue(v2); - } - - @Override - public void end() { - if (!isEnded) { - isEnded = true; - source.stop(); - } - } - - @Override - public boolean shouldFree() { - return isEnded; - } - - @Override - public void handleEvent(MediaEvent evt) { - isEnded = true; - } - - } - - static void initialize() { - - try { - audioctx = AudioContext.create(); - recDest = audioctx.createMediaStreamDestination(); - } catch (Throwable t) { - throw new PlatformRuntime.RuntimeInitializationFailureException("Could not initialize audio context!", t); - } - - PlatformInput.clearEvenBuffers(); - - } - - private static GainNode micGain; - - public static void setMicVol(float vol) { - if (micGain == null) - return; - micGain.getGain().setValue(vol); - } - - protected static void initRecDest() { - AudioBufferSourceNode absn = audioctx.createBufferSource(); - AudioBuffer ab = audioctx.createBuffer(1, 1, 48000); - ab.copyToChannel(new float[] { 0 }, 0); - absn.setBuffer(ab); - absn.setLoop(true); - absn.start(); - absn.connect(recDest); - MediaStream mic = PlatformRuntime.getMic(); - if (mic != null) { - micGain = audioctx.createGain(); - micGain.getGain().setValue(Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.VOICE)); - audioctx.createMediaStreamSource(mic).connect(micGain); - micGain.connect(recDest); - } - } - - protected static MediaStream getRecStream() { - return recDest.getStream(); - } - - public static IAudioResource loadAudioData(String filename, boolean holdInCache) { - BrowserAudioResource buffer; - synchronized (soundCache) { - buffer = soundCache.get(filename); - } - if (buffer == null) { - byte[] file = PlatformAssets.getResourceBytes(filename); - if (file == null) - return null; - buffer = new BrowserAudioResource(decodeAudioAsync(TeaVMUtils.unwrapArrayBuffer(file), filename)); - if (holdInCache) { - synchronized (soundCache) { - soundCache.put(filename, buffer); - } - } - } - if (buffer.buffer != null) { - buffer.cacheHit = System.currentTimeMillis(); - return buffer; - } else { - return null; - } - } - - public static interface IAudioCacheLoader { - byte[] loadFile(String filename); - } - - public static IAudioResource loadAudioDataNew(String filename, boolean holdInCache, IAudioCacheLoader loader) { - BrowserAudioResource buffer; - synchronized (soundCache) { - buffer = soundCache.get(filename); - } - if (buffer == null) { - byte[] file = loader.loadFile(filename); - if (file == null) - return null; - Uint8Array buf = Uint8Array.create(file.length); - buf.set(file); - buffer = new BrowserAudioResource(decodeAudioAsync(buf.getBuffer(), filename)); - if (holdInCache) { - synchronized (soundCache) { - soundCache.put(filename, buffer); - } - } - } - if (buffer.buffer != null) { - buffer.cacheHit = System.currentTimeMillis(); - return buffer; - } else { - return null; - } - } - - @Async - public static native AudioBuffer decodeAudioAsync(ArrayBuffer buffer, String errorFileName); - - private static void decodeAudioAsync(ArrayBuffer buffer, final String errorFileName, - final AsyncCallback cb) { - audioctx.decodeAudioData(buffer, new DecodeSuccessCallback() { - @Override - public void onSuccess(AudioBuffer decodedData) { - cb.complete(decodedData); - } - }, new DecodeErrorCallback() { - @Override - public void onError(JSObject error) { - logger.error("Could not load audio: {}", errorFileName); - cb.complete(null); - } - }); - } - - public static void clearAudioCache() { - long millis = System.currentTimeMillis(); - if (millis - cacheFreeTimer > 30000l) { - cacheFreeTimer = millis; - synchronized (soundCache) { - Iterator itr = soundCache.values().iterator(); - while (itr.hasNext()) { - if (millis - itr.next().cacheHit > 600000l) { // 10 minutes - itr.remove(); - } - } - } - } - } - - public static void flushAudioCache() { - synchronized (soundCache) { - soundCache.clear(); - } - } - - public static boolean available() { - return true; // this is not used - } - - public static IAudioHandle beginPlayback(IAudioResource track, float x, float y, float z, - float volume, float pitch) { - BrowserAudioResource internalTrack = (BrowserAudioResource) track; - internalTrack.cacheHit = System.currentTimeMillis(); - - AudioBufferSourceNode src = audioctx.createBufferSource(); - src.setBuffer(internalTrack.buffer); - src.getPlaybackRate().setValue(pitch); - - PannerNode panner = audioctx.createPanner(); - panner.setPosition(x, y, z); - float v1 = volume * 16.0f; - if (v1 < 16.0f) - v1 = 16.0f; - panner.setMaxDistance(v1); - panner.setRolloffFactor(1.0f); - panner.setDistanceModel("linear"); - panner.setPanningModel("HRTF"); - panner.setConeInnerAngle(360.0f); - panner.setConeOuterAngle(0.0f); - panner.setConeOuterGain(0.0f); - panner.setOrientation(0.0f, 1.0f, 0.0f); - - GainNode gain = audioctx.createGain(); - float v2 = volume; - if (v2 > 1.0f) - v2 = 1.0f; - gain.getGain().setValue(v2); - - src.connect(panner); - panner.connect(gain); - gain.connect(audioctx.getDestination()); - gain.connect(recDest); - - src.start(); - - return new BrowserAudioHandle(internalTrack, src, panner, gain, pitch); - } - - public static IAudioHandle beginPlaybackStatic(IAudioResource track, float volume, float pitch) { - BrowserAudioResource internalTrack = (BrowserAudioResource) track; - internalTrack.cacheHit = System.currentTimeMillis(); - - AudioBufferSourceNode src = audioctx.createBufferSource(); - src.setBuffer(internalTrack.buffer); - src.getPlaybackRate().setValue(pitch); - - GainNode gain = audioctx.createGain(); - float v2 = volume; - if (v2 > 1.0f) - v2 = 1.0f; - gain.getGain().setValue(v2); - - src.connect(gain); - gain.connect(audioctx.getDestination()); - gain.connect(recDest); - - src.start(); - - return new BrowserAudioHandle(internalTrack, src, null, gain, pitch); - } - - public static void setListener(float x, float y, float z, float pitchDegrees, float yawDegrees) { - float var2 = MathHelper.cos(-yawDegrees * 0.017453292F); - float var3 = MathHelper.sin(-yawDegrees * 0.017453292F); - float var4 = -MathHelper.cos(pitchDegrees * 0.017453292F); - float var5 = MathHelper.sin(pitchDegrees * 0.017453292F); - AudioListener l = audioctx.getListener(); - l.setPosition(x, y, z); - l.setOrientation(-var3 * var4, -var5, -var2 * var4, 0.0f, 1.0f, 0.0f); - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.Int8Array; +import org.teavm.jso.webaudio.AudioBuffer; +import org.teavm.jso.webaudio.AudioBufferSourceNode; +import org.teavm.jso.webaudio.AudioContext; +import org.teavm.jso.webaudio.AudioListener; +import org.teavm.jso.webaudio.DecodeErrorCallback; +import org.teavm.jso.webaudio.DecodeSuccessCallback; +import org.teavm.jso.webaudio.GainNode; +import org.teavm.jso.webaudio.MediaEvent; +import org.teavm.jso.webaudio.MediaStream; +import org.teavm.jso.webaudio.MediaStreamAudioDestinationNode; +import org.teavm.jso.webaudio.PannerNode; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.JOrbisAudioBufferDecoder; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.util.MathHelper; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformAudio { + + static final Logger logger = LogManager.getLogger("BrowserAudio"); + + static AudioContext audioctx = null; + static MediaStreamAudioDestinationNode recDestNode = null; + static MediaStream recDestMediaStream = null; + static AudioBuffer silence = null; + static AudioBufferSourceNode recDestSilenceNode = null; + static GainNode micRecGain = null; + static GainNode gameRecGain = null; + private static final Map soundCache = new HashMap<>(); + private static final List activeSounds = new LinkedList<>(); + + private static long cacheFreeTimer = 0l; + private static long activeFreeTimer = 0l; + private static boolean oggSupport = false; + private static boolean loadViaAudioBufferSupport = false; + private static boolean loadViaWAV32FSupport = false; + private static boolean loadViaWAV16Support = false; + + protected static class BrowserAudioResource implements IAudioResource { + + protected AudioBuffer buffer; + protected long cacheHit = 0l; + + protected BrowserAudioResource(AudioBuffer buffer) { + this.buffer = buffer; + } + + } + + protected static class BrowserAudioHandle implements IAudioHandle, EventListener { + + protected final BrowserAudioResource resource; + protected AudioBufferSourceNode source; + protected final PannerNode panner; + protected final GainNode gain; + protected float pitch; + protected boolean isPaused = false; + protected boolean isEnded = false; + + public BrowserAudioHandle(BrowserAudioResource resource, AudioBufferSourceNode source, PannerNode panner, + GainNode gain, float pitch) { + this.resource = resource; + this.source = source; + this.panner = panner; + this.gain = gain; + this.pitch = pitch; + source.setOnEnded(this); + } + + @Override + public void pause(boolean setPaused) { + if(setPaused) { + if(!isPaused) { + isPaused = true; + source.getPlaybackRate().setValue(0.0f); + } + }else { + if(isPaused) { + isPaused = false; + source.getPlaybackRate().setValue(pitch); + } + } + } + + @Override + public void restart() { + if(isEnded) { + isEnded = false; + AudioBufferSourceNode src = audioctx.createBufferSource(); + resource.cacheHit = PlatformRuntime.steadyTimeMillis(); + src.setBuffer(resource.buffer); + src.getPlaybackRate().setValue(pitch); + source.disconnect(); + src.connect(panner == null ? gain : panner); + source = src; + source.start(); + }else { + source.getPlaybackRate().setValue(pitch); + source.start(0.0); + } + } + + @Override + public void move(float x, float y, float z) { + if(panner != null) { + panner.setPosition(x, y, z); + } + } + + @Override + public void pitch(float f) { + pitch = f; + if(!isPaused) { + source.getPlaybackRate().setValue(pitch); + } + } + + @Override + public void gain(float f) { + if(panner != null) { + float v1 = f * 16.0f; + if(v1 < 16.0f) v1 = 16.0f; + panner.setMaxDistance(v1); + } + float v2 = f; + if(v2 > 1.0f) v2 = 1.0f; + gain.getGain().setValue(v2); + } + + @Override + public void end() { + if(!isEnded) { + isEnded = true; + source.stop(); + } + } + + @Override + public boolean shouldFree() { + return isEnded; + } + + @Override + public void handleEvent(MediaEvent evt) { + isEnded = true; + } + + } + + static void initialize() { + oggSupport = false; + loadViaAudioBufferSupport = false; + loadViaWAV32FSupport = false; + loadViaWAV16Support = false; + + try { + audioctx = AudioContext.create(); + }catch(Throwable t) { + audioctx = null; + logger.error("Could not initialize audio context!"); + logger.error(t); + return; + } + + detectOGGSupport(); + + if(!oggSupport) { + loadViaAudioBufferSupport = detectLoadViaAudioBufferSupport(audioctx); + if(!loadViaAudioBufferSupport) { + logger.warn("Missing AudioContext buffer from Float32Array support, attempting to use WAV files as a container to load raw PCM data"); + detectWAVFallbackSupport(); + if(!loadViaWAV32FSupport && !loadViaWAV16Support) { + try { + audioctx.close(); + }catch(Throwable t) { + } + audioctx = null; + logger.error("Audio context is missing some required features!"); + } + } + } + + PlatformInput.clearEvenBuffers(); + + } + + @JSBody(params = { "ctx" }, script = "var tmpBuf = ctx.createBuffer(2, 16, 16000); return (typeof tmpBuf.copyToChannel === \"function\");") + private static native boolean detectLoadViaAudioBufferSupport(AudioContext ctx); + + private static void detectOGGSupport() { + byte[] fileData = EagRuntime.getRequiredResourceBytes("/assets/eagler/audioctx_test_ogg.dat"); + + if(((TeaVMClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isUseJOrbisAudioDecoderTeaVM()) { + logger.info("Note: Using embedded JOrbis OGG decoder"); + oggSupport = false; + }else { + try { + Int8Array arr = Int8Array.create(fileData.length); + arr.set(TeaVMUtils.unwrapByteArray(fileData), 0); + AudioBuffer buffer = decodeAudioBrowserAsync(arr.getBuffer(), null); + if(buffer == null || buffer.getLength() == 0) { + throw new RuntimeException(); + } + oggSupport = true; + }catch(Throwable t) { + oggSupport = false; + logger.error("OGG file support detected as false! Using embedded JOrbis OGG decoder"); + } + } + } + + private static void detectWAVFallbackSupport() { + byte[] fileData = EagRuntime.getRequiredResourceBytes("/assets/eagler/audioctx_test_wav32f.dat"); + + try { + Int8Array arr = Int8Array.create(fileData.length); + arr.set(TeaVMUtils.unwrapByteArray(fileData), 0); + AudioBuffer buffer = decodeAudioBrowserAsync(arr.getBuffer(), null); + if(buffer == null || buffer.getLength() == 0) { + throw new RuntimeException(); + } + loadViaWAV32FSupport = true; + return; + }catch(Throwable t) { + loadViaWAV32FSupport = false; + logger.error("Could not load a 32-bit floating point WAV file, trying to use 16-bit integers"); + } + + fileData = EagRuntime.getRequiredResourceBytes("/assets/eagler/audioctx_test_wav16.dat"); + + try { + Int8Array arr = Int8Array.create(fileData.length); + arr.set(TeaVMUtils.unwrapByteArray(fileData), 0); + AudioBuffer buffer = decodeAudioBrowserAsync(arr.getBuffer(), null); + if(buffer == null || buffer.getLength() == 0) { + throw new RuntimeException(); + } + loadViaWAV16Support = true; + return; + }catch(Throwable t) { + loadViaWAV16Support = false; + logger.error("Could not load a 16-bit integer WAV file, this browser is not supported"); + } + } + + static MediaStream initRecordingStream(float gameVol, float micVol) { + if(recDestMediaStream != null) { + return recDestMediaStream; + } + try { + if(silence == null) { + silence = audioctx.createBuffer(1, 1, 48000); + silence.copyToChannel(new float[] { 0 }, 0); + } + recDestNode = audioctx.createMediaStreamDestination(); + recDestSilenceNode = audioctx.createBufferSource(); + recDestSilenceNode.setBuffer(silence); + recDestSilenceNode.setLoop(true); + recDestSilenceNode.start(); + recDestSilenceNode.connect(recDestNode); + if(micVol > 0.0f) { + MediaStream mic = PlatformScreenRecord.getMic(); + if (mic != null) { + micRecGain = audioctx.createGain(); + micRecGain.getGain().setValue(micVol); + audioctx.createMediaStreamSource(mic).connect(micRecGain); + micRecGain.connect(recDestNode); + } + } + gameRecGain = audioctx.createGain(); + gameRecGain.getGain().setValue(gameVol); + synchronized(activeSounds) { + for(BrowserAudioHandle handle : activeSounds) { + if(handle.panner != null) { + handle.panner.connect(gameRecGain); + }else { + handle.gain.connect(gameRecGain); + } + } + } + PlatformVoiceClient.addRecordingDest(gameRecGain); + gameRecGain.connect(recDestNode); + recDestMediaStream = recDestNode.getStream(); + return recDestMediaStream; + }catch(Throwable t) { + destroyRecordingStream(); + throw t; + } + } + + static void destroyRecordingStream() { + if(recDestSilenceNode != null) { + try { + recDestSilenceNode.disconnect(); + }catch(Throwable t) { + } + recDestSilenceNode = null; + } + if(micRecGain != null) { + try { + micRecGain.disconnect(); + }catch(Throwable t) { + } + micRecGain = null; + } + if(gameRecGain != null) { + try { + gameRecGain.disconnect(); + }catch(Throwable t) { + } + synchronized(activeSounds) { + for(BrowserAudioHandle handle : activeSounds) { + try { + if(handle.panner != null) { + handle.panner.disconnect(gameRecGain); + }else { + handle.gain.disconnect(gameRecGain); + } + }catch(Throwable t) { + } + } + } + PlatformVoiceClient.removeRecordingDest(gameRecGain); + gameRecGain = null; + } + recDestNode = null; + recDestMediaStream = null; + } + + public static IAudioResource loadAudioData(String filename, boolean holdInCache) { + BrowserAudioResource buffer; + synchronized(soundCache) { + buffer = soundCache.get(filename); + } + if(buffer == null) { + byte[] file = PlatformAssets.getResourceBytes(filename); + if(file == null) return null; + buffer = new BrowserAudioResource(decodeAudioData(file, filename)); + if(holdInCache) { + synchronized(soundCache) { + soundCache.put(filename, buffer); + } + } + } + if(buffer.buffer != null) { + buffer.cacheHit = PlatformRuntime.steadyTimeMillis(); + return buffer; + }else { + return null; + } + } + + public static interface IAudioCacheLoader { + byte[] loadFile(String filename); + } + + public static IAudioResource loadAudioDataNew(String filename, boolean holdInCache, IAudioCacheLoader loader) { + BrowserAudioResource buffer; + synchronized(soundCache) { + buffer = soundCache.get(filename); + } + if(buffer == null) { + byte[] file = loader.loadFile(filename); + if(file == null) return null; + buffer = new BrowserAudioResource(decodeAudioData(file, filename)); + if(holdInCache) { + synchronized(soundCache) { + soundCache.put(filename, buffer); + } + } + } + if(buffer.buffer != null) { + buffer.cacheHit = PlatformRuntime.steadyTimeMillis(); + return buffer; + }else { + return null; + } + } + + private static AudioBuffer decodeAudioData(byte[] data, String errorFileName) { + if(data == null) { + return null; + } + if(oggSupport) { + // browsers complain if we don't copy the array + Int8Array arr = Int8Array.create(data.length); + arr.set(TeaVMUtils.unwrapByteArray(data), 0); + return decodeAudioBrowserAsync(arr.getBuffer(), errorFileName); + }else { + if(data.length > 4 && data[0] == (byte)0x4F && data[1] == (byte)0x67 && data[2] == (byte)0x67 && data[3] == (byte)0x53) { + return JOrbisAudioBufferDecoder.decodeAudioJOrbis(audioctx, data, errorFileName, + loadViaAudioBufferSupport ? JOrbisAudioBufferDecoder.LOAD_VIA_AUDIOBUFFER + : (loadViaWAV32FSupport ? JOrbisAudioBufferDecoder.LOAD_VIA_WAV32F + : JOrbisAudioBufferDecoder.LOAD_VIA_WAV16)); + } else { + Int8Array arr = Int8Array.create(data.length); + arr.set(TeaVMUtils.unwrapByteArray(data), 0); + return decodeAudioBrowserAsync(arr.getBuffer(), errorFileName); + } + } + } + + @Async + public static native AudioBuffer decodeAudioBrowserAsync(ArrayBuffer buffer, String errorFileName); + + private static void decodeAudioBrowserAsync(ArrayBuffer buffer, final String errorFileName, final AsyncCallback cb) { + audioctx.decodeAudioData(buffer, new DecodeSuccessCallback() { + @Override + public void onSuccess(AudioBuffer decodedData) { + cb.complete(decodedData); + } + }, new DecodeErrorCallback() { + @Override + public void onError(JSObject error) { + if(errorFileName != null) { + logger.error("Could not load audio: {}", errorFileName); + } + cb.complete(null); + } + }); + } + + public static void clearAudioCache() { + long millis = PlatformRuntime.steadyTimeMillis(); + if(millis - cacheFreeTimer > 30000l) { + cacheFreeTimer = millis; + synchronized(soundCache) { + Iterator itr = soundCache.values().iterator(); + while(itr.hasNext()) { + if(millis - itr.next().cacheHit > 600000l) { // 10 minutes + itr.remove(); + } + } + } + } + if(millis - activeFreeTimer > 700l) { + activeFreeTimer = millis; + synchronized(activeSounds) { + Iterator itr = activeSounds.iterator(); + while(itr.hasNext()) { + if(itr.next().shouldFree()) { + itr.remove(); + } + } + } + } + } + + public static void flushAudioCache() { + synchronized(soundCache) { + soundCache.clear(); + } + synchronized(activeSounds) { + activeSounds.clear(); + } + } + + public static boolean available() { + return audioctx != null; + } + + public static IAudioHandle beginPlayback(IAudioResource track, float x, float y, float z, + float volume, float pitch) { + BrowserAudioResource internalTrack = (BrowserAudioResource) track; + internalTrack.cacheHit = PlatformRuntime.steadyTimeMillis(); + + AudioBufferSourceNode src = audioctx.createBufferSource(); + src.setBuffer(internalTrack.buffer); + src.getPlaybackRate().setValue(pitch); + + PannerNode panner = audioctx.createPanner(); + panner.setPosition(x, y, z); + float v1 = volume * 16.0f; + if(v1 < 16.0f) v1 = 16.0f; + panner.setMaxDistance(v1); + panner.setRolloffFactor(1.0f); + panner.setDistanceModel("linear"); + panner.setPanningModel("HRTF"); + panner.setConeInnerAngle(360.0f); + panner.setConeOuterAngle(0.0f); + panner.setConeOuterGain(0.0f); + panner.setOrientation(0.0f, 1.0f, 0.0f); + + GainNode gain = audioctx.createGain(); + float v2 = volume; + if(v2 > 1.0f) v2 = 1.0f; + gain.getGain().setValue(v2); + + src.connect(panner); + panner.connect(gain); + gain.connect(audioctx.getDestination()); + if(gameRecGain != null) { + gain.connect(gameRecGain); + } + + src.start(); + + BrowserAudioHandle ret = new BrowserAudioHandle(internalTrack, src, panner, gain, pitch); + synchronized(activeSounds) { + activeSounds.add(ret); + } + return ret; + } + + public static IAudioHandle beginPlaybackStatic(IAudioResource track, float volume, float pitch) { + BrowserAudioResource internalTrack = (BrowserAudioResource) track; + internalTrack.cacheHit = PlatformRuntime.steadyTimeMillis(); + + AudioBufferSourceNode src = audioctx.createBufferSource(); + src.setBuffer(internalTrack.buffer); + src.getPlaybackRate().setValue(pitch); + + GainNode gain = audioctx.createGain(); + float v2 = volume; + if(v2 > 1.0f) v2 = 1.0f; + gain.getGain().setValue(v2); + + src.connect(gain); + gain.connect(audioctx.getDestination()); + if(gameRecGain != null) { + gain.connect(gameRecGain); + } + + src.start(); + + BrowserAudioHandle ret = new BrowserAudioHandle(internalTrack, src, null, gain, pitch); + synchronized(activeSounds) { + activeSounds.add(ret); + } + return ret; + } + + public static void setListener(float x, float y, float z, float pitchDegrees, float yawDegrees) { + float var2 = MathHelper.cos(-yawDegrees * 0.017453292F); + float var3 = MathHelper.sin(-yawDegrees * 0.017453292F); + float var4 = -MathHelper.cos(pitchDegrees * 0.017453292F); + float var5 = MathHelper.sin(pitchDegrees * 0.017453292F); + AudioListener l = audioctx.getListener(); + l.setPosition(x, y, z); + l.setOrientation(-var3 * var4, -var5, -var2 * var4, 0.0f, 1.0f, 0.0f); + } + + static void destroy() { + soundCache.clear(); + if(audioctx != null) { + audioctx.close(); + audioctx = null; + recDestNode = null; + recDestMediaStream = null; + silence = null; + recDestSilenceNode = null; + micRecGain = null; + gameRecGain = null; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java index ab2f93b..3c39179 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java @@ -1,370 +1,49 @@ package net.lax1dude.eaglercraft.v1_8.internal; -import org.teavm.interop.Async; -import org.teavm.interop.AsyncCallback; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.indexeddb.EventHandler; -import org.teavm.jso.indexeddb.IDBCountRequest; -import org.teavm.jso.indexeddb.IDBCursor; -import org.teavm.jso.indexeddb.IDBCursorRequest; -import org.teavm.jso.indexeddb.IDBDatabase; -import org.teavm.jso.indexeddb.IDBFactory; -import org.teavm.jso.indexeddb.IDBGetRequest; -import org.teavm.jso.indexeddb.IDBObjectStoreParameters; -import org.teavm.jso.indexeddb.IDBOpenDBRequest; -import org.teavm.jso.indexeddb.IDBRequest; -import org.teavm.jso.indexeddb.IDBTransaction; -import org.teavm.jso.indexeddb.IDBVersionChangeEvent; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.Int8Array; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.BooleanResult; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.IndexedDBFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class PlatformFilesystem { - private static String filesystemDB = null; - private static IDBDatabase database = null; + private static final Logger logger = LogManager.getLogger("PlatformFilesystem"); - public static void initialize(String dbName) { - filesystemDB = "_net_lax1dude_eaglercraft_v1_8_internal_PlatformFilesystem_1_8_8_" + dbName; - DatabaseOpen dbOpen = AsyncHandlers.openDB(filesystemDB); - - if (dbOpen.failedLocked) { - throw new FilesystemDatabaseLockedException(dbOpen.failedError); + public static IEaglerFilesystem initializePersist(String dbName) { + try { + return IndexedDBFilesystem.createFilesystem(dbName); + }catch(Throwable t) { + logger.error("Could not open IndexedDB filesystem: {}", dbName); + logger.error(t); + return null; } - - if (dbOpen.failedInit) { - throw new FilesystemDatabaseInitializationException(dbOpen.failedError); - } - - if (dbOpen.database == null) { - throw new NullPointerException("IDBDatabase is null!"); - } - - database = dbOpen.database; } - public static class FilesystemDatabaseLockedException extends RuntimeException { + public static class FilesystemDatabaseLockedException extends EaglerFileSystemException { public FilesystemDatabaseLockedException(String message) { super(message); } } - public static class FilesystemDatabaseInitializationException extends RuntimeException { + public static class FilesystemDatabaseInitializationException extends EaglerFileSystemException { public FilesystemDatabaseInitializationException(String message) { super(message); } } - public static boolean eaglerDelete(String pathName) { - return AsyncHandlers.deleteFile(database, pathName).bool; - } - - public static ByteBuffer eaglerRead(String pathName) { - ArrayBuffer ar = AsyncHandlers.readWholeFile(database, pathName); - if (ar == null) { - return null; - } - return EaglerArrayBufferAllocator.wrapByteBufferTeaVM(Int8Array.create(ar)); - } - - public static void eaglerWrite(String pathName, ByteBuffer data) { - if (!AsyncHandlers.writeWholeFile(database, pathName, - EaglerArrayBufferAllocator.getDataView8Unsigned(data).getBuffer()).bool) { - throw new RuntimeException( - "Failed to write " + data.remaining() + " byte file to indexeddb table: " + pathName); - } - } - - public static boolean eaglerExists(String pathName) { - return AsyncHandlers.fileExists(database, pathName).bool; - } - - public static boolean eaglerMove(String pathNameOld, String pathNameNew) { - ArrayBuffer old = AsyncHandlers.readWholeFile(database, pathNameOld); - return old != null && AsyncHandlers.writeWholeFile(database, pathNameNew, old).bool - && AsyncHandlers.deleteFile(database, pathNameOld).bool; - } - - public static int eaglerCopy(String pathNameOld, String pathNameNew) { - ArrayBuffer old = AsyncHandlers.readWholeFile(database, pathNameOld); - if (old != null && AsyncHandlers.writeWholeFile(database, pathNameNew, old).bool) { - return old.getByteLength(); - } else { - return -1; - } - } - - public static int eaglerSize(String pathName) { - ArrayBuffer old = AsyncHandlers.readWholeFile(database, pathName); - return old == null ? -1 : old.getByteLength(); - } - - private static class VFSFilenameIteratorNonRecursive implements VFSFilenameIterator { - - private final VFSFilenameIterator child; - private final int pathCount; - - private VFSFilenameIteratorNonRecursive(VFSFilenameIterator child, int pathCount) { - this.child = child; - this.pathCount = pathCount; - } - - @Override - public void next(String entry) { - if (countSlashes(entry) == pathCount) { - child.next(entry); - } - } - - } - - private static int countSlashes(String str) { - int j = 0; - for (int i = 0, l = str.length(); i < l; ++i) { - if (str.charAt(i) == '/') { - ++j; - } - } - return j; - } - - public static void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { - if (recursive) { - AsyncHandlers.iterateFiles(database, pathName, false, itr); - } else { - AsyncHandlers.iterateFiles(database, pathName, false, - new VFSFilenameIteratorNonRecursive(itr, countSlashes(pathName) + 1)); - } - } - - protected static class DatabaseOpen { - - protected final boolean failedInit; - protected final boolean failedLocked; - protected final String failedError; - - protected final IDBDatabase database; - - protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) { - failedInit = init; - failedLocked = locked; - failedError = error; - database = db; - } - - } - - @JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;") - protected static native IDBFactory createIDBFactory(); - - protected static class AsyncHandlers { - - @Async - protected static native DatabaseOpen openDB(String name); - - private static void openDB(String name, final AsyncCallback cb) { - IDBFactory i = createIDBFactory(); - if (i == null) { - cb.complete(new DatabaseOpen(false, false, "window.indexedDB was null or undefined", null)); - return; - } - final IDBOpenDBRequest f = i.open(name, 1); - f.setOnBlocked(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(new DatabaseOpen(false, true, null, null)); - } - }); - f.setOnSuccess(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(new DatabaseOpen(false, false, null, f.getResult())); - } - }); - f.setOnError(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(new DatabaseOpen(false, false, "open error", null)); - } - }); - f.setOnUpgradeNeeded(new EventListener() { - @Override - public void handleEvent(IDBVersionChangeEvent evt) { - f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path")); - } - }); - } - - @Async - protected static native BooleanResult deleteFile(IDBDatabase db, String name); - - private static void deleteFile(IDBDatabase db, String name, final AsyncCallback cb) { - IDBTransaction tx = db.transaction("filesystem", "readwrite"); - final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name)); - - r.setOnSuccess(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(BooleanResult.TRUE); - } - }); - r.setOnError(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(BooleanResult.FALSE); - } - }); - } - - @JSBody(params = { - "obj" }, script = "return (typeof obj === \"undefined\") ? null : ((typeof obj.data === \"undefined\") ? null : obj.data);") - protected static native ArrayBuffer readRow(JSObject obj); - - @JSBody(params = { "obj" }, script = "return [obj];") - private static native JSObject makeTheFuckingKeyWork(String k); - - @Async - protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name); - - private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback cb) { - IDBTransaction tx = db.transaction("filesystem", "readonly"); - final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name)); - r.setOnSuccess(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(readRow(r.getResult())); - } - }); - r.setOnError(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(null); - } - }); - - } - - @JSBody(params = { - "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));") - private static native String readKey(JSObject k); - - @JSBody(params = { - "k" }, script = "return ((typeof k) === \"undefined\") ? null : (((typeof k.path) === \"undefined\") ? null : (((typeof k.path) === \"string\") ? k[0] : null));") - private static native String readRowKey(JSObject r); - - @Async - protected static native Integer iterateFiles(IDBDatabase db, final String prefix, boolean rw, - final VFSFilenameIterator itr); - - private static void iterateFiles(IDBDatabase db, final String prefix, boolean rw, final VFSFilenameIterator itr, - final AsyncCallback cb) { - IDBTransaction tx = db.transaction("filesystem", rw ? "readwrite" : "readonly"); - final IDBCursorRequest r = tx.objectStore("filesystem").openCursor(); - final int[] res = new int[1]; - r.setOnSuccess(new EventHandler() { - @Override - public void handleEvent() { - IDBCursor c = r.getResult(); - if (c == null || c.getKey() == null || c.getValue() == null) { - cb.complete(res[0]); - return; - } - String k = readKey(c.getKey()); - if (k != null) { - if (k.startsWith(prefix)) { - int ci = res[0]++; - try { - itr.next(k); - } catch (VFSIterator2.BreakLoop ex) { - cb.complete(res[0]); - return; - } - } - } - c.doContinue(); - } - }); - r.setOnError(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(res[0] > 0 ? res[0] : -1); - } - }); - } - - @Async - protected static native BooleanResult fileExists(IDBDatabase db, String name); - - private static void fileExists(IDBDatabase db, String name, final AsyncCallback cb) { - IDBTransaction tx = db.transaction("filesystem", "readonly"); - final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name)); - r.setOnSuccess(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(BooleanResult._new(r.getResult() > 0)); - } - }); - r.setOnError(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(BooleanResult.FALSE); - } - }); - } - - @JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };") - protected static native JSObject writeRow(String name, ArrayBuffer data); - - @Async - protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data); - - private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, - final AsyncCallback cb) { - IDBTransaction tx = db.transaction("filesystem", "readwrite"); - final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data)); - - r.setOnSuccess(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(BooleanResult.TRUE); - } - }); - r.setOnError(new EventHandler() { - @Override - public void handleEvent() { - cb.complete(BooleanResult.FALSE); - } - }); - } - - } } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index c4da90b..7633de9 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -1,710 +1,2383 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import java.util.LinkedList; -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; -import org.teavm.interop.Async; -import org.teavm.interop.AsyncCallback; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.browser.TimerHandler; -import org.teavm.jso.browser.Window; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.events.KeyboardEvent; -import org.teavm.jso.dom.events.MouseEvent; -import org.teavm.jso.dom.events.WheelEvent; -import org.teavm.jso.dom.html.HTMLCanvasElement; -import org.teavm.jso.dom.html.HTMLElement; -import org.teavm.jso.webgl.WebGLFramebuffer; -import org.teavm.jso.webgl.WebGLRenderbuffer; - -import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext; - -import static net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext.*; -import static org.teavm.jso.webgl.WebGLRenderingContext.COLOR_ATTACHMENT0; -import static org.teavm.jso.webgl.WebGLRenderingContext.COLOR_BUFFER_BIT; -import static org.teavm.jso.webgl.WebGLRenderingContext.DEPTH_ATTACHMENT; -import static org.teavm.jso.webgl.WebGLRenderingContext.FRAMEBUFFER; -import static org.teavm.jso.webgl.WebGLRenderingContext.NEAREST; -import static org.teavm.jso.webgl.WebGLRenderingContext.RENDERBUFFER; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -// HoosierTransfer mod here - -public class PlatformInput { - - private static Window win = null; - private static HTMLCanvasElement canvas = null; - static WebGL2RenderingContext context = null; - - static WebGLFramebuffer mainFramebuffer = null; - static WebGLRenderbuffer mainColorRenderbuffer = null; - static WebGLRenderbuffer mainDepthRenderbuffer = null; - private static int framebufferWidth = -1; - private static int framebufferHeight = -1; - - private static EventListener contextmenu = null; - private static EventListener mousedown = null; - private static EventListener mouseup = null; - private static EventListener mousemove = null; - private static EventListener mouseenter = null; - private static EventListener mouseleave = null; - private static EventListener keydown = null; - private static EventListener keyup = null; - private static EventListener keypress = null; - private static EventListener wheel = null; - private static EventListener pointerlock = null; - - private static List mouseEvents = new LinkedList(); - private static List keyEvents = new LinkedList(); - - private static int mouseX = 0; - private static int mouseY = 0; - private static double mouseDX = 0.0D; - private static double mouseDY = 0.0D; - private static double mouseDWheel = 0.0D; - private static int width = 0; - private static int height = 0; - private static boolean enableRepeatEvents = true; - private static boolean isWindowFocused = true; - private static boolean isMouseOverWindow = true; - static boolean unpressCTRL = false; - - private static int windowWidth = -1; - private static int windowHeight = -1; - private static int lastWasResizedWindowWidth = -2; - private static int lastWasResizedWindowHeight = -2; - - private static MouseEvent currentEvent = null; - private static KeyboardEvent currentEventK = null; - private static boolean[] buttonStates = new boolean[8]; - private static boolean[] keyStates = new boolean[256]; - - private static int functionKeyModifier = KeyboardConstants.KEY_F; - - private static long mouseUngrabTimer = 0l; - private static long mouseGrabTimer = 0l; - private static int mouseUngrabTimeout = -1; - private static boolean pointerLockFlag = false; - - private static JSObject fullscreenQuery = null; - - public static boolean keyboardLockSupported = false; - public static boolean lockKeys = false; - - private static boolean vsync = true; - private static boolean vsyncSupport = false; - - @JSBody(params = {}, script = "window.onbeforeunload = () => {return false;};") - private static native void onBeforeCloseRegister(); - - static void initHooks(Window window, HTMLCanvasElement canvaz) { - win = window; - canvas = canvaz; - canvas.getStyle().setProperty("cursor", "default"); - win.addEventListener("contextmenu", contextmenu = new EventListener() { - @Override - public void handleEvent(MouseEvent evt) { - evt.preventDefault(); - evt.stopPropagation(); - } - }); - canvas.addEventListener("mousedown", mousedown = new EventListener() { - @Override - public void handleEvent(MouseEvent evt) { - evt.preventDefault(); - evt.stopPropagation(); - int b = evt.getButton(); - buttonStates[b == 1 ? 2 : (b == 2 ? 1 : b)] = true; - mouseEvents.add(evt); - } - }); - canvas.addEventListener("mouseup", mouseup = new EventListener() { - @Override - public void handleEvent(MouseEvent evt) { - evt.preventDefault(); - evt.stopPropagation(); - int b = evt.getButton(); - buttonStates[b == 1 ? 2 : (b == 2 ? 1 : b)] = false; - mouseEvents.add(evt); - } - }); - canvas.addEventListener("mousemove", mousemove = new EventListener() { - @Override - public void handleEvent(MouseEvent evt) { - evt.preventDefault(); - evt.stopPropagation(); - mouseX = (int) (getOffsetX(evt) * win.getDevicePixelRatio()); - mouseY = (int) ((canvas.getClientHeight() - getOffsetY(evt)) * win.getDevicePixelRatio()); - mouseDX += evt.getMovementX(); - mouseDY += -evt.getMovementY(); - if (hasBeenActive()) { - mouseEvents.add(evt); - } - } - }); - canvas.addEventListener("mouseenter", mouseenter = new EventListener() { - @Override - public void handleEvent(MouseEvent evt) { - isMouseOverWindow = true; - } - }); - canvas.addEventListener("mouseleave", mouseleave = new EventListener() { - @Override - public void handleEvent(MouseEvent evt) { - isMouseOverWindow = false; - } - }); - win.addEventListener("keydown", keydown = new EventListener() { - @Override - public void handleEvent(KeyboardEvent evt) { - int w = getWhich(evt); - if (w == 122) { // F11 - toggleFullscreen(); - } - evt.preventDefault(); - evt.stopPropagation(); - if (!enableRepeatEvents && evt.isRepeat()) - return; - int ww = processFunctionKeys(w); - keyStates[KeyboardConstants.getEaglerKeyFromBrowser(ww, ww == w ? evt.getLocation() : 0)] = true; - keyEvents.add(evt); - } - }); - win.addEventListener("keyup", keyup = new EventListener() { - @Override - public void handleEvent(KeyboardEvent evt) { - int w = getWhich(evt); - evt.preventDefault(); - evt.stopPropagation(); - if (!enableRepeatEvents && evt.isRepeat()) - return; - int ww = processFunctionKeys(w); - int eagKey = KeyboardConstants.getEaglerKeyFromBrowser(ww, ww == w ? evt.getLocation() : 0); - keyStates[eagKey] = false; - if (eagKey == functionKeyModifier) { - for (int key = KeyboardConstants.KEY_F1; key <= KeyboardConstants.KEY_F10; ++key) { - keyStates[key] = false; - } - } - keyEvents.add(evt); - } - }); - win.addEventListener("keypress", keypress = new EventListener() { - @Override - public void handleEvent(KeyboardEvent evt) { - evt.preventDefault(); - evt.stopPropagation(); - if (enableRepeatEvents && evt.isRepeat()) - keyEvents.add(evt); - } - }); - canvas.addEventListener("wheel", wheel = new EventListener() { - @Override - public void handleEvent(WheelEvent evt) { - evt.preventDefault(); - evt.stopPropagation(); - mouseEvents.add(evt); - mouseDWheel += evt.getDeltaY(); - } - }); - win.addEventListener("blur", new EventListener() { - @Override - public void handleEvent(WheelEvent evt) { - isWindowFocused = false; - for (int i = 0; i < buttonStates.length; ++i) { - buttonStates[i] = false; - } - for (int i = 0; i < keyStates.length; ++i) { - keyStates[i] = false; - } - } - }); - win.addEventListener("focus", new EventListener() { - @Override - public void handleEvent(WheelEvent evt) { - isWindowFocused = true; - } - }); - win.getDocument().addEventListener("pointerlockchange", pointerlock = new EventListener() { - @Override - public void handleEvent(WheelEvent evt) { - Window.setTimeout(new TimerHandler() { - @Override - public void onTimer() { - boolean grab = isPointerLocked(); - if (!grab) { - if (pointerLockFlag) { - mouseUngrabTimer = System.currentTimeMillis(); - } - } - pointerLockFlag = grab; - } - }, 60); - mouseDX = 0.0D; - mouseDY = 0.0D; - } - }); - try { - onBeforeCloseRegister(); - } catch (Throwable t) { - } - - try { - asyncRequestAnimationFrame(); - vsyncSupport = true; - } catch (Throwable t) { - PlatformRuntime.logger.error("VSync is not supported on this browser!"); - } - - fullscreenQuery = fullscreenMediaQuery(); - if (keyboardLockSupported = checkKeyboardLockSupported()) { - TeaVMUtils.addEventListener(fullscreenQuery, "change", new EventListener() { - @Override - public void handleEvent(Event evt) { - if (!mediaQueryMatches(evt)) { - unlockKeys(); - lockKeys = false; - } - } - }); - } - } - - @JSBody(params = {}, script = "if(window.navigator.userActivation){return window.navigator.userActivation.hasBeenActive;}else{return false;}") - public static native boolean hasBeenActive(); - - @JSBody(params = { "m" }, script = "return m.offsetX;") - private static native int getOffsetX(MouseEvent m); - - @JSBody(params = { "m" }, script = "return m.offsetY;") - private static native int getOffsetY(MouseEvent m); - - @JSBody(params = { "e" }, script = "return e.which;") - private static native int getWhich(KeyboardEvent e); - - public static int getWindowWidth() { - return windowWidth; - } - - public static int getWindowHeight() { - return windowHeight; - } - - public static boolean getWindowFocused() { - return isWindowFocused || isPointerLocked(); - } - - public static boolean isCloseRequested() { - return false; - } - - public static void setVSync(boolean enable) { - vsync = enable; - } - - @JSBody(params = { "doc" }, script = "return (doc.visibilityState === \"visible\");") - private static native boolean getVisibilityState(JSObject doc); - - public static void update() { - double r = win.getDevicePixelRatio(); - int w = (int) (PlatformRuntime.parent.getClientWidth()); - int h = (int) (PlatformRuntime.parent.getClientHeight()); - int w2 = windowWidth = (int) (w * r); - int h2 = windowHeight = (int) (h * r); - if (canvas.getWidth() != w2) { - canvas.setWidth(w2); - } - if (canvas.getHeight() != h2) { - canvas.setHeight(h2); - } - flipBuffer(); - if (PlatformRuntime.recording) { - long t = System.currentTimeMillis(); - if (t - PlatformRuntime.lastFrame > (1000 / 30)) { - PlatformRuntime.recFrame(); - PlatformRuntime.lastFrame = t; - } - } - if (getVisibilityState(win.getDocument())) { - if (vsyncSupport && vsync) { - asyncRequestAnimationFrame(); - } else { - EagUtils.sleep(0l); - } - } else { - EagUtils.sleep(50l); - } - } - - @Async - private static native void asyncRequestAnimationFrame(); - - private static void asyncRequestAnimationFrame(AsyncCallback cb) { - final boolean[] hasCompleted = new boolean[1]; - final int[] timeout = new int[] { -1 }; - Window.requestAnimationFrame((d) -> { - if (!hasCompleted[0]) { - hasCompleted[0] = true; - Window.clearTimeout(timeout[0]); - cb.complete(null); - } - }); - timeout[0] = Window.setTimeout(() -> { - if (!hasCompleted[0]) { - hasCompleted[0] = true; - cb.complete(null); - } - }, 50); - } - - public static boolean isVSyncSupported() { - return vsyncSupport; - } - - static void initFramebuffer(WebGL2RenderingContext ctx, WebGLFramebuffer fbo, int sw, int sh) { - context = ctx; - mainFramebuffer = fbo; - - framebufferWidth = windowWidth = (int) (sw); - framebufferHeight = windowHeight = (int) (sh / 2); - - ctx.bindFramebuffer(FRAMEBUFFER, fbo); - - mainColorRenderbuffer = ctx.createRenderbuffer(); - mainDepthRenderbuffer = ctx.createRenderbuffer(); - - ctx.bindRenderbuffer(RENDERBUFFER, mainColorRenderbuffer); - ctx.renderbufferStorage(RENDERBUFFER, RGBA8, sw, sh); - ctx.framebufferRenderbuffer(FRAMEBUFFER, COLOR_ATTACHMENT0, RENDERBUFFER, mainColorRenderbuffer); - - ctx.bindRenderbuffer(RENDERBUFFER, mainDepthRenderbuffer); - ctx.renderbufferStorage(RENDERBUFFER, DEPTH_COMPONENT32F, sw, sh); - ctx.framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, mainDepthRenderbuffer); - - ctx.drawBuffers(new int[] { COLOR_ATTACHMENT0 }); - } - - private static void flipBuffer() { - - context.bindFramebuffer(READ_FRAMEBUFFER, mainFramebuffer); - context.bindFramebuffer(DRAW_FRAMEBUFFER, null); - context.blitFramebuffer(0, 0, framebufferWidth, framebufferHeight, 0, 0, windowWidth, windowHeight, - COLOR_BUFFER_BIT, NEAREST); - - context.bindFramebuffer(FRAMEBUFFER, mainFramebuffer); - - if (windowWidth != framebufferWidth || windowHeight != framebufferHeight) { - framebufferWidth = windowWidth; - framebufferHeight = windowHeight; - - context.bindRenderbuffer(RENDERBUFFER, mainColorRenderbuffer); - context.renderbufferStorage(RENDERBUFFER, RGBA8, framebufferWidth, framebufferHeight); - - context.bindRenderbuffer(RENDERBUFFER, mainDepthRenderbuffer); - context.renderbufferStorage(RENDERBUFFER, DEPTH_COMPONENT32F, framebufferWidth, framebufferHeight); - } - - } - - public static boolean wasResized() { - if (windowWidth != lastWasResizedWindowWidth || windowHeight != lastWasResizedWindowHeight) { - lastWasResizedWindowWidth = windowWidth; - lastWasResizedWindowHeight = windowHeight; - return true; - } else { - return false; - } - } - - public static boolean keyboardNext() { - if (unpressCTRL) { // un-press ctrl after copy/paste permission - keyEvents.clear(); - currentEventK = null; - keyStates[29] = false; - keyStates[157] = false; - keyStates[28] = false; - keyStates[219] = false; - keyStates[220] = false; - unpressCTRL = false; - return false; - } - currentEventK = null; - return !keyEvents.isEmpty() && (currentEventK = keyEvents.remove(0)) != null; - } - - public static boolean keyboardGetEventKeyState() { - return currentEventK == null ? false : !currentEventK.getType().equals("keyup"); - } - - public static int keyboardGetEventKey() { - int w = processFunctionKeys(getWhich(currentEventK)); - return currentEventK == null ? -1 : KeyboardConstants.getEaglerKeyFromBrowser(w, currentEventK.getLocation()); - } - - public static char keyboardGetEventCharacter() { - if (currentEventK == null) - return '\0'; - String s = currentEventK.getKey(); - return currentEventK == null ? ' ' : (char) (s.length() > 1 ? '\0' : s.charAt(0)); - } - - public static boolean keyboardIsKeyDown(int key) { - if (unpressCTRL) { // un-press ctrl after copy/paste permission - keyStates[28] = false; - keyStates[29] = false; - keyStates[157] = false; - keyStates[219] = false; - keyStates[220] = false; - } - return key < 0 || key >= keyStates.length ? false : keyStates[key]; - } - - public static boolean keyboardIsRepeatEvent() { - return currentEventK == null ? false : currentEventK.isRepeat(); - } - - public static void keyboardEnableRepeatEvents(boolean b) { - enableRepeatEvents = b; - } - - public static boolean mouseNext() { - currentEvent = null; - return !mouseEvents.isEmpty() && (currentEvent = mouseEvents.remove(0)) != null; - } - - public static boolean mouseGetEventButtonState() { - return currentEvent == null ? false : currentEvent.getType().equals(MouseEvent.MOUSEDOWN); - } - - public static int mouseGetEventButton() { - if (currentEvent == null || currentEvent.getType().equals(MouseEvent.MOUSEMOVE)) - return -1; - int b = currentEvent.getButton(); - return b == 1 ? 2 : (b == 2 ? 1 : b); - } - - public static int mouseGetEventX() { - return currentEvent == null ? -1 : (int) (currentEvent.getClientX() * win.getDevicePixelRatio()); - } - - public static int mouseGetEventY() { - return currentEvent == null ? -1 - : (int) ((canvas.getClientHeight() - currentEvent.getClientY()) * win.getDevicePixelRatio()); - } - - public static int mouseGetEventDWheel() { - return ("wheel".equals(currentEvent.getType())) ? (((WheelEvent) currentEvent).getDeltaY() == 0.0D ? 0 - : (((WheelEvent) currentEvent).getDeltaY() > 0.0D ? -1 : 1)) : 0; - } - - public static int mouseGetX() { - return mouseX; - } - - public static int mouseGetY() { - return mouseY; - } - - public static boolean mouseIsButtonDown(int i) { - return buttonStates[i]; - } - - public static int mouseGetDWheel() { - int ret = (int) mouseDWheel; - mouseDWheel = 0.0D; - return ret; - } - - public static void mouseSetGrabbed(boolean grab) { - long t = System.currentTimeMillis(); - pointerLockFlag = grab; - mouseGrabTimer = t; - if (grab) { - canvas.requestPointerLock(); - if (mouseUngrabTimeout != -1) - Window.clearTimeout(mouseUngrabTimeout); - mouseUngrabTimeout = -1; - if (t - mouseUngrabTimer < 3000l) { - mouseUngrabTimeout = Window.setTimeout(new TimerHandler() { - @Override - public void onTimer() { - canvas.requestPointerLock(); - } - }, 3100 - (int) (t - mouseUngrabTimer)); - } - } else { - if (mouseUngrabTimeout != -1) - Window.clearTimeout(mouseUngrabTimeout); - mouseUngrabTimeout = -1; - Window.current().getDocument().exitPointerLock(); - } - mouseDX = 0.0D; - mouseDY = 0.0D; - } - - public static boolean isMouseGrabbed() { - return pointerLockFlag; - } - - @JSBody(params = {}, script = "return document.pointerLockElement != null;") - public static native boolean isPointerLocked(); - - public static int mouseGetDX() { - int ret = (int) mouseDX; - mouseDX = 0.0D; - return ret; - } - - public static int mouseGetDY() { - int ret = (int) mouseDY; - mouseDY = 0.0D; - return ret; - } - - public static void mouseSetCursorPosition(int x, int y) { - // obsolete - } - - public static boolean mouseIsInsideWindow() { - return isMouseOverWindow; - } - - public static boolean contextLost() { - return PlatformRuntime.webgl.isContextLost(); - } - - private static int processFunctionKeys(int key) { - if (keyboardIsKeyDown(functionKeyModifier)) { - if (key >= 49 && key <= 57) { - key = key - 49 + 112; - } - } - return key; - } - - public static void setFunctionKeyModifier(int key) { - functionKeyModifier = key; - } - - public static void removeEventHandlers() { - win.removeEventListener("contextmenu", contextmenu); - canvas.removeEventListener("mousedown", mousedown); - canvas.removeEventListener("mouseup", mouseup); - canvas.removeEventListener("mousemove", mousemove); - canvas.removeEventListener("mouseenter", mouseenter); - canvas.removeEventListener("mouseleave", mouseleave); - win.removeEventListener("keydown", keydown); - win.removeEventListener("keyup", keyup); - win.removeEventListener("keypress", keypress); - canvas.removeEventListener("wheel", wheel); - win.getDocument().removeEventListener("pointerlockchange", pointerlock); - if (mouseUngrabTimeout != -1) { - Window.clearTimeout(mouseUngrabTimeout); - mouseUngrabTimeout = -1; - } - try { - win.getDocument().exitPointerLock(); - }catch(Throwable t) { - } - } - - public static void pressAnyKeyScreen() { - if (mouseEvents.isEmpty() && keyEvents.isEmpty() && !hasBeenActive()) { - EarlyLoadScreen.paintEnable(); - - while (mouseEvents.isEmpty() && keyEvents.isEmpty()) { - EagUtils.sleep(100l); - } - } - } - - public static void clearEvenBuffers() { - mouseEvents.clear(); - keyEvents.clear(); - } - - @JSBody(params = {}, script = "return window.matchMedia(\"(display-mode: fullscreen)\");") - private static native JSObject fullscreenMediaQuery(); - - @JSBody(params = { "mediaQuery" }, script = "return mediaQuery.matches;") - private static native boolean mediaQueryMatches(JSObject mediaQuery); - - public static void toggleFullscreen() { - if (isFullscreen()) { - if (keyboardLockSupported) { - unlockKeys(); - lockKeys = false; - } - exitFullscreen(); - } else { - if (keyboardLockSupported) { - lockKeys(); - lockKeys = true; - } - requestFullscreen(canvas); - } - } - - public static boolean isFullscreen() { - return mediaQueryMatches(fullscreenQuery); - } - - @JSBody(params = {}, script = "window.navigator.keyboard.lock();") - private static native void lockKeys(); - - @JSBody(params = {}, script = "window.navigator.keyboard.unlock();") - private static native void unlockKeys(); - - @JSBody(params = {}, script = "return !!(window.navigator.keyboard && window.navigator.keyboard.lock);") - private static native boolean checkKeyboardLockSupported(); - - @JSBody(params = {}, script = "document.exitFullscreen();") - private static native void exitFullscreen(); - - @JSBody(params = { "element" }, script = "element.requestFullscreen();") - private static native void requestFullscreen(HTMLElement element); - - public static void showCursor(EnumCursorType cursor) { - switch (cursor) { - case DEFAULT: - default: - canvas.getStyle().setProperty("cursor", "default"); - break; - case HAND: - canvas.getStyle().setProperty("cursor", "pointer"); - break; - case TEXT: - canvas.getStyle().setProperty("cursor", "text"); - break; - } - } - -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.Touch; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TouchEvent; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.VisualViewport; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.browser.Navigator; +import org.teavm.jso.browser.TimerHandler; +import org.teavm.jso.browser.Window; +import org.teavm.jso.core.JSNumber; +import org.teavm.jso.dom.css.CSSStyleDeclaration; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.events.KeyboardEvent; +import org.teavm.jso.dom.events.MouseEvent; +import org.teavm.jso.dom.events.WheelEvent; +import org.teavm.jso.dom.html.HTMLCanvasElement; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLFormElement; +import org.teavm.jso.dom.html.HTMLInputElement; +import org.teavm.jso.dom.html.TextRectangle; +import org.teavm.jso.gamepad.Gamepad; +import org.teavm.jso.gamepad.GamepadButton; +import org.teavm.jso.gamepad.GamepadEvent; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.InputEvent; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.LegacyKeycodeTranslator; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.OffsetTouch; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.SortedTouchEvent; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLBackBuffer; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformInput { + + private static Window win = null; + private static HTMLElement parent = null; + private static HTMLCanvasElement canvas = null; + private static HTMLElement touchKeyboardOpenZone = null; + private static int touchOpenZoneX = 0; + private static int touchOpenZoneY = 0; + private static int touchOpenZoneW = 0; + private static int touchOpenZoneH = 0; + private static HTMLFormElement touchKeyboardForm = null; + private static HTMLInputElement touchKeyboardField = null; + private static boolean shownTouchKeyboardEventWarning = false; + private static boolean shownLegacyTouchKeyboardWarning = false; + private static boolean showniOSReturnTouchKeyboardWarning = false; + private static double lastTouchKeyboardEvtA = 0.0; + private static double lastTouchKeyboardEvtB = 0.0; + private static double lastTouchKeyboardEvtC = 0.0; + + private static EventListener contextmenu = null; + private static EventListener mousedown = null; + private static EventListener mouseup = null; + private static EventListener mousemove = null; + private static EventListener mouseenter = null; + private static EventListener mouseleave = null; + private static EventListener touchstart = null; + private static EventListener touchend = null; + private static EventListener touchmove = null; + private static EventListener touchcancel = null; + private static EventListener gamepadconnected = null; + private static EventListener gamepaddisconnected = null; + private static EventListener keydown = null; + private static EventListener keyup = null; + private static EventListener touchKeyboardOpenZone_touchstart = null; + private static EventListener touchKeyboardOpenZone_touchend = null; + private static EventListener touchKeyboardOpenZone_touchmove = null; + private static EventListener wheel = null; + private static EventListener focus = null; + private static EventListener blur = null; + private static EventListener pointerlock = null; + + private static Map keyCodeTranslatorMap = null; + + public static Map getKeyCodeTranslatorMapTeaVM() { + return keyCodeTranslatorMap; + } + + private static final List pastedStrings = new LinkedList<>(); + + private static final int EVENT_KEY_DOWN = 0; + private static final int EVENT_KEY_UP = 1; + private static final int EVENT_KEY_REPEAT = 2; + + private static class VKeyEvent { + + private final int keyCode; + private final int location; + private final int eagKey; + private final char keyChar; + private final int type; + + private VKeyEvent(int keyCode, int location, int eagKey, char keyChar, int type) { + this.keyCode = keyCode; + this.location = location; + this.eagKey = eagKey; + this.keyChar = keyChar; + this.type = type; + } + + } + + private static final int EVENT_MOUSE_DOWN = 0; + private static final int EVENT_MOUSE_UP = 1; + private static final int EVENT_MOUSE_MOVE = 2; + private static final int EVENT_MOUSE_WHEEL = 3; + + private static class VMouseEvent { + + private final int posX; + private final int posY; + private final int button; + private final float wheel; + private final int type; + + private VMouseEvent(int posX, int posY, int button, float wheel, int type) { + this.posX = posX; + this.posY = posY; + this.button = button; + this.wheel = wheel; + this.type = type; + } + + } + + private static final List mouseEvents = new LinkedList<>(); + private static final List keyEvents = new LinkedList<>(); + private static final List touchEvents = new LinkedList<>(); + + private static boolean hasShownPressAnyKey = false; + private static boolean isOnMobilePressAnyKey = false; + + private static interface MobilePressAnyKeyHandler { + void call(boolean enterBootMenu); + } + + private static HTMLElement mobilePressAnyKeyScreen = null; + private static MobilePressAnyKeyHandler mobilePressAnyKey = null; + static boolean isLikelyMobileBrowser = false; + + private static int mouseX = 0; + private static int mouseY = 0; + private static double mouseDX = 0.0D; + private static double mouseDY = 0.0D; + private static double mouseDWheel = 0.0D; + private static boolean enableRepeatEvents = true; + private static boolean isWindowFocused = true; + private static boolean isMouseOverWindow = true; + static boolean unpressCTRL = false; + + private static SortedTouchEvent currentTouchState = null; + private static SortedTouchEvent currentTouchEvent = null; + + public static int touchOffsetXTeaVM = 0; + public static int touchOffsetYTeaVM = 0; + + private static boolean gamepadSupported = false; + private static final List gamepadList = new ArrayList<>(); + private static Gamepad selectedGamepad = null; + private static String selectedGamepadName = null; + private static double gamepadTimestamp = -1.0; + private static final boolean[] gamepadButtonStates = new boolean[24]; + private static final float[] gamepadAxisStates = new float[4]; + + private static int windowWidth = -1; + private static int windowHeight = -1; + private static float windowDPI = 1.0f; + private static int visualViewportX = -1; + private static int visualViewportY = -1; + private static int visualViewportW = -1; + private static int visualViewportH = -1; + private static int lastWasResizedWindowWidth = -2; + private static int lastWasResizedWindowHeight = -2; + private static float lastWasResizedWindowDPI = -2.0f; + private static int lastWasResizedVisualViewportX = -2; + private static int lastWasResizedVisualViewportY = -2; + private static int lastWasResizedVisualViewportW = -2; + private static int lastWasResizedVisualViewportH = -2; + + private static VMouseEvent currentEvent = null; + private static VKeyEvent currentEventK = null; + private static boolean[] buttonStates = new boolean[8]; + private static boolean[] keyStates = new boolean[256]; + + private static int functionKeyModifier = KeyboardConstants.KEY_F; + + // Can't support webkit vendor prefix since there's no document.pointerLockElement + private static final int POINTER_LOCK_NONE = 0; + private static final int POINTER_LOCK_CORE = 1; + private static final int POINTER_LOCK_MOZ = 2; + private static int pointerLockSupported = POINTER_LOCK_NONE; + private static long mouseUngrabTimer = 0l; + private static long mouseGrabTimer = 0l; + private static int mouseUngrabTimeout = -1; + private static boolean pointerLockFlag = false; + + private static final int FULLSCREEN_NONE = 0; + private static final int FULLSCREEN_CORE = 1; + private static final int FULLSCREEN_WEBKIT = 2; + private static final int FULLSCREEN_MOZ = 3; + private static int fullscreenSupported = FULLSCREEN_NONE; + + private static JSObject fullscreenQuery = null; + + public static boolean keyboardLockSupported = false; + public static boolean lockKeys = false; + + static boolean vsync = true; + static boolean vsyncSupport = false; + + private static long vsyncWaiting = -1l; + private static AsyncCallback vsyncAsyncCallback = null; + private static int vsyncTimeout = -1; + + // hack to fix occasional freeze on iOS + private static int vsyncSaveLockInterval = -1; + + @JSFunctor + private static interface UnloadCallback extends JSObject { + void call(); + } + + @JSBody(params = { "win", "cb" }, script = "win.__curEaglerX188UnloadListenerCB = cb; if((typeof win.__isEaglerX188UnloadListenerSet === \"string\") && win.__isEaglerX188UnloadListenerSet === \"yes\") return; win.onbeforeunload = function(evt) { if(win.__curEaglerX188UnloadListenerCB) win.__curEaglerX188UnloadListenerCB(); return false; }; win.__isEaglerX188UnloadListenerSet = \"yes\";") + private static native void onBeforeCloseRegister(Window win, UnloadCallback cb); + + static void initHooks(Window window, HTMLElement parent_, HTMLCanvasElement canvaz) { + win = window; + parent = parent_; + canvas = canvaz; + canvas.getStyle().setProperty("cursor", "default"); + updateTouchOffset(); + lastWasResizedWindowWidth = -2; + lastWasResizedWindowHeight = -2; + lastWasResizedWindowDPI = -2.0f; + lastWasResizedVisualViewportX = -2; + lastWasResizedVisualViewportY = -2; + lastWasResizedVisualViewportW = -2; + lastWasResizedVisualViewportH = -2; + hasShownPressAnyKey = false; + touchOpenZoneX = 0; + touchOpenZoneY = 0; + touchOpenZoneW = 0; + touchOpenZoneH = 0; + touchKeyboardForm = null; + touchKeyboardField = null; + shownLegacyTouchKeyboardWarning = false; + shownTouchKeyboardEventWarning = false; + showniOSReturnTouchKeyboardWarning = false; + lastTouchKeyboardEvtA = 0.0; + lastTouchKeyboardEvtB = 0.0; + lastTouchKeyboardEvtC = 0.0; + touchKeyboardOpenZone = win.getDocument().createElement("div"); + touchKeyboardOpenZone.getClassList().add("_eaglercraftX_keyboard_open_zone"); + CSSStyleDeclaration decl = touchKeyboardOpenZone.getStyle(); + decl.setProperty("display", "none"); + decl.setProperty("position", "absolute"); + decl.setProperty("background-color", "transparent"); + decl.setProperty("top", "0px"); + decl.setProperty("left", "0px"); + decl.setProperty("width", "0px"); + decl.setProperty("height", "0px"); + decl.setProperty("z-index", "100"); + decl.setProperty("touch-action", "pan-x pan-y"); + decl.setProperty("-webkit-touch-callout", "none"); + decl.setProperty("-webkit-tap-highlight-color", "rgba(255, 255, 255, 0)"); + parent.appendChild(touchKeyboardOpenZone); + + PlatformRuntime.logger.info("Loading keyboard layout data"); + + LegacyKeycodeTranslator keycodeTranslator = new LegacyKeycodeTranslator(); + if(checkKeyboardLayoutSupported()) { + try { + iterateKeyboardLayout(keycodeTranslator::addBrowserLayoutMapping); + }catch(Throwable t) { + PlatformRuntime.logger.error("Caught exception querying keyboard layout from browser, using the default layout instead"); + PlatformRuntime.logger.error(t); + } + int cnt = keycodeTranslator.getRemappedKeyCount(); + if(cnt > 0) { + PlatformRuntime.logger.info("KeyboardLayoutMap remapped {} keys from their default codes", cnt); + } + } + keyCodeTranslatorMap = keycodeTranslator.buildLayoutTable(); + + parent.addEventListener("contextmenu", contextmenu = new EventListener() { + @Override + public void handleEvent(MouseEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + } + }); + canvas.addEventListener("mousedown", mousedown = new EventListener() { + @Override + public void handleEvent(MouseEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + if(tryGrabCursorHook()) return; + int b = evt.getButton(); + b = b == 1 ? 2 : (b == 2 ? 1 : b); + buttonStates[b] = true; + int eventX = (int)(getOffsetX(evt) * windowDPI); + int eventY = windowHeight - (int)(getOffsetY(evt) * windowDPI) - 1; + synchronized(mouseEvents) { + mouseEvents.add(new VMouseEvent(eventX, eventY, b, 0.0f, EVENT_MOUSE_DOWN)); + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + } + }); + canvas.addEventListener("mouseup", mouseup = new EventListener() { + @Override + public void handleEvent(MouseEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + int b = evt.getButton(); + b = b == 1 ? 2 : (b == 2 ? 1 : b); + buttonStates[b] = false; + int eventX = (int)(getOffsetX(evt) * windowDPI); + int eventY = windowHeight - (int)(getOffsetY(evt) * windowDPI) - 1; + synchronized(mouseEvents) { + mouseEvents.add(new VMouseEvent(eventX, eventY, b, 0.0f, EVENT_MOUSE_UP)); + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + } + }); + canvas.addEventListener("mousemove", mousemove = new EventListener() { + @Override + public void handleEvent(MouseEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + mouseX = (int)(getOffsetX(evt) * windowDPI); + mouseY = windowHeight - (int)(getOffsetY(evt) * windowDPI) - 1; + mouseDX += evt.getMovementX(); + mouseDY += -evt.getMovementY(); + if(hasShownPressAnyKey) { + int eventX = (int)(getOffsetX(evt) * windowDPI); + int eventY = windowHeight - (int)(getOffsetY(evt) * windowDPI) - 1; + synchronized(mouseEvents) { + mouseEvents.add(new VMouseEvent(eventX, eventY, -1, 0.0f, EVENT_MOUSE_MOVE)); + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + } + } + }); + canvas.addEventListener("mouseenter", mouseenter = new EventListener() { + @Override + public void handleEvent(MouseEvent evt) { + isMouseOverWindow = true; + } + }); + canvas.addEventListener("mouseleave", mouseleave = new EventListener() { + @Override + public void handleEvent(MouseEvent evt) { + isMouseOverWindow = false; + } + }); + canvas.addEventListener("touchstart", touchstart = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + SortedTouchEvent sorted = new SortedTouchEvent(evt, touchUIDMapperCreate); + currentTouchState = sorted; + List lst = sorted.getEventTouches(); + synchronized(touchEvents) { + touchEvents.add(sorted); + if(touchEvents.size() > 64) { + touchEvents.remove(0); + } + } + touchCloseDeviceKeyboard0(false); + } + }); + canvas.addEventListener("touchend", touchend = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + SortedTouchEvent sorted = new SortedTouchEvent(evt, touchUIDMapper); + currentTouchState = sorted; + List lst = sorted.getEventTouches(); + int len = lst.size(); + for (int i = 0; i < len; ++i) { + touchIDtoUID.remove(lst.get(i).touch.getIdentifier()); + } + synchronized(touchEvents) { + touchEvents.add(sorted); + if(touchEvents.size() > 64) { + touchEvents.remove(0); + } + } + } + }); + canvas.addEventListener("touchmove", touchmove = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + SortedTouchEvent sorted = new SortedTouchEvent(evt, touchUIDMapperCreate); + currentTouchState = sorted; + if(hasShownPressAnyKey) { + synchronized(touchEvents) { + touchEvents.add(sorted); + if(touchEvents.size() > 64) { + touchEvents.remove(0); + } + } + } + } + }); + canvas.addEventListener("touchcancel", touchcancel = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + SortedTouchEvent sorted = new SortedTouchEvent(evt, touchUIDMapper); + currentTouchState = sorted; + List lst = sorted.getEventTouches(); + int len = lst.size(); + for (int i = 0; i < len; ++i) { + touchIDtoUID.remove(lst.get(i).touch.getIdentifier()); + } + if(hasShownPressAnyKey) { + synchronized(touchEvents) { + touchEvents.add(sorted); + if(touchEvents.size() > 64) { + touchEvents.remove(0); + } + } + } + } + }); + win.addEventListener("keydown", keydown = new EventListener() { + @Override + public void handleEvent(KeyboardEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + if(!enableRepeatEvents && evt.isRepeat()) return; + LegacyKeycodeTranslator.LegacyKeycode keyCode = null; + if(keyCodeTranslatorMap != null && hasCodeVar(evt)) { + keyCode = keyCodeTranslatorMap.get(evt.getCode()); + } + int w; + int loc; + if(keyCode != null) { + w = keyCode.keyCode; + loc = keyCode.location; + }else { + w = getWhich(evt); + loc = getLocationSafe(evt); + } + if (w == 122 && !evt.isRepeat()) { // F11 + toggleFullscreen(); + } + int ww = processFunctionKeys(w); + int eag = KeyboardConstants.getEaglerKeyFromBrowser(ww, ww == w ? loc : 0); + if(isOnMobilePressAnyKey && mobilePressAnyKey != null) { + if(eag == KeyboardConstants.KEY_DELETE) { + mobilePressAnyKey.call(true); + return; + } + } + if(eag != 0) { + keyStates[eag] = true; + } + String s = getCharOrNull(evt); + int l = s.length(); + char c; + if(l == 1) { + c = s.charAt(0); + }else if(l == 0) { + c = keyToAsciiLegacy(w, evt.isShiftKey()); + }else if(s.equals("Unidentified")) { + return; + }else { + c = '\0'; + } + synchronized(keyEvents) { + keyEvents.add(new VKeyEvent(ww, loc, eag, c, EVENT_KEY_DOWN)); + if(keyEvents.size() > 64) { + keyEvents.remove(0); + } + } + JSObject obj = evt.getTimeStamp(); + if(TeaVMUtils.isTruthy(obj)) { + lastTouchKeyboardEvtA = ((JSNumber)obj).doubleValue(); + } + } + }); + win.addEventListener("keyup", keyup = new EventListener() { + @Override + public void handleEvent(KeyboardEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + LegacyKeycodeTranslator.LegacyKeycode keyCode = null; + if(keyCodeTranslatorMap != null && hasCodeVar(evt)) { + keyCode = keyCodeTranslatorMap.get(evt.getCode()); + } + int w; + int loc; + if(keyCode != null) { + w = keyCode.keyCode; + loc = keyCode.location; + }else { + w = getWhich(evt); + loc = getLocationSafe(evt); + } + int ww = processFunctionKeys(w); + int eag = KeyboardConstants.getEaglerKeyFromBrowser(ww, ww == w ? loc : 0); + if(eag != 0) { + keyStates[eag] = false; + if(eag == functionKeyModifier) { + for(int key = KeyboardConstants.KEY_F1; key <= KeyboardConstants.KEY_F10; ++key) { + keyStates[key] = false; + } + } + } + String s = getCharOrNull(evt); + int l = s.length(); + char c; + if(l == 1) { + c = s.charAt(0); + }else if(l == 0) { + c = keyToAsciiLegacy(w, evt.isShiftKey()); + }else if(s.equals("Unidentified")) { + return; + }else { + c = '\0'; + } + synchronized(keyEvents) { + keyEvents.add(new VKeyEvent(ww, loc, eag, c, EVENT_KEY_UP)); + if(keyEvents.size() > 64) { + keyEvents.remove(0); + } + } + } + }); + touchKeyboardOpenZone.addEventListener("touchstart", touchKeyboardOpenZone_touchstart = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + } + }); + touchKeyboardOpenZone.addEventListener("touchend", touchKeyboardOpenZone_touchend = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + touchOpenDeviceKeyboard(); + } + }); + touchKeyboardOpenZone.addEventListener("touchmove", touchKeyboardOpenZone_touchmove = new EventListener() { + @Override + public void handleEvent(TouchEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + } + }); + canvas.addEventListener("wheel", wheel = new EventListener() { + @Override + public void handleEvent(WheelEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + double delta = evt.getDeltaY(); + mouseDWheel += delta; + if(hasShownPressAnyKey) { + int eventX = (int)(getOffsetX(evt) * windowDPI); + int eventY = windowHeight - (int)(getOffsetY(evt) * windowDPI) - 1; + synchronized(mouseEvents) { + mouseEvents.add(new VMouseEvent(eventX, eventY, -1, (float)delta, EVENT_MOUSE_WHEEL)); + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + } + } + }); + win.addEventListener("blur", blur = new EventListener() { + @Override + public void handleEvent(Event evt) { + isWindowFocused = false; + for(int i = 0; i < buttonStates.length; ++i) { + buttonStates[i] = false; + } + for(int i = 0; i < keyStates.length; ++i) { + keyStates[i] = false; + } + } + }); + win.addEventListener("focus", focus = new EventListener() { + @Override + public void handleEvent(Event evt) { + isWindowFocused = true; + } + }); + + try { + pointerLockSupported = getSupportedPointerLock(win.getDocument()); + }catch(Throwable t) { + pointerLockSupported = POINTER_LOCK_NONE; + } + if(pointerLockSupported != POINTER_LOCK_NONE) { + win.getDocument().addEventListener(pointerLockSupported == POINTER_LOCK_MOZ ? "mozpointerlockchange" : "pointerlockchange", pointerlock = new EventListener() { + @Override + public void handleEvent(Event evt) { + Window.setTimeout(new TimerHandler() { + @Override + public void onTimer() { + boolean grab = isPointerLocked(); + if(!grab) { + if(pointerLockFlag) { + mouseUngrabTimer = PlatformRuntime.steadyTimeMillis(); + } + } + pointerLockFlag = grab; + } + }, 60); + mouseDX = 0.0D; + mouseDY = 0.0D; + } + }); + if(pointerLockSupported == POINTER_LOCK_MOZ) { + PlatformRuntime.logger.info("Using moz- vendor prefix for pointer lock"); + } + }else { + PlatformRuntime.logger.error("Pointer lock is not supported on this browser"); + } + + if(pointerLockSupported != POINTER_LOCK_NONE) { + String ua = PlatformRuntime.getUserAgentString(); + if(ua != null) { + ua = ua.toLowerCase(); + isLikelyMobileBrowser = ua.contains("mobi") || ua.contains("tablet"); + }else { + isLikelyMobileBrowser = false; + } + }else { + isLikelyMobileBrowser = true; + } + + try { + fullscreenSupported = getSupportedFullScreen(win.getDocument()); + }catch(Throwable t) { + fullscreenSupported = FULLSCREEN_NONE; + } + if(fullscreenSupported != FULLSCREEN_NONE) { + fullscreenQuery = fullscreenMediaQuery(); + if(fullscreenSupported == FULLSCREEN_CORE && (keyboardLockSupported = checkKeyboardLockSupported())) { + TeaVMUtils.addEventListener(fullscreenQuery, "change", new EventListener() { + @Override + public void handleEvent(Event evt) { + if (!mediaQueryMatches(evt)) { + unlockKeys(); + lockKeys = false; + } + } + }); + } + if(pointerLockSupported == FULLSCREEN_WEBKIT) { + PlatformRuntime.logger.info("Using webkit- vendor prefix for fullscreen"); + }else if(pointerLockSupported == FULLSCREEN_MOZ) { + PlatformRuntime.logger.info("Using moz- vendor prefix for fullscreen"); + } + }else { + PlatformRuntime.logger.error("Fullscreen is not supported on this browser"); + } + + try { + onBeforeCloseRegister(window, () -> PlatformRuntime.beforeUnload()); + }catch(Throwable t) { + } + + vsyncWaiting = -1l; + vsyncAsyncCallback = null; + vsyncTimeout = -1; + vsyncSupport = false; + + try { + asyncRequestAnimationFrame(); + vsyncSupport = true; + }catch(Throwable t) { + PlatformRuntime.logger.error("VSync is not supported on this browser!"); + } + + if(vsyncSupport) { + if(vsyncSaveLockInterval != -1) { + try { + Window.clearInterval(vsyncSaveLockInterval); + }catch(Throwable t) { + } + vsyncSaveLockInterval = -1; + } + // fix for iOS freezing randomly...? + vsyncSaveLockInterval = Window.setInterval(() -> { + if(vsyncWaiting != -1l) { + long steadyTime = PlatformRuntime.steadyTimeMillis(); + if(steadyTime - vsyncWaiting > 1000) { + PlatformRuntime.logger.error("VSync lockup detected! Attempting to recover..."); + vsyncWaiting = -1l; + if(vsyncTimeout != -1) { + try { + Window.clearTimeout(vsyncTimeout); + }catch(Throwable t) { + } + vsyncTimeout = -1; + } + if(vsyncAsyncCallback != null) { + AsyncCallback cb = vsyncAsyncCallback; + vsyncAsyncCallback = null; + cb.complete(null); + }else { + PlatformRuntime.logger.error("Async callback is null!"); + } + } + } + }, 1000); + } + + try { + gamepadSupported = gamepadSupported(); + if(gamepadSupported) { + win.addEventListener("gamepadconnected", gamepadconnected = new EventListener() { + @Override + public void handleEvent(GamepadEvent evt) { + enumerateGamepads(); + } + }); + win.addEventListener("gamepaddisconnected", gamepaddisconnected = new EventListener() { + @Override + public void handleEvent(GamepadEvent evt) { + if(evt.getGamepad() == selectedGamepad) { + selectedGamepad = null; + } + enumerateGamepads(); + } + }); + } + }catch(Throwable t) { + gamepadSupported = false; + PlatformRuntime.logger.error("Gamepad detected as unsupported!"); + } + + enumerateGamepads(); + } + + @JSFunctor + private static interface KeyboardLayoutIterator extends JSObject { + void call(String key, String val); + } + + @JSFunctor + private static interface KeyboardLayoutDone extends JSObject { + void call(); + } + + @JSBody(params = { "cb", "cbDone" }, script = "return navigator.keyboard.getLayoutMap()" + + ".then(function(layoutMap) { if(layoutMap && layoutMap.forEach) layoutMap.forEach(cb); cbDone(); })" + + ".catch(function() { cbDone(); });") + private static native void iterateKeyboardLayout0(KeyboardLayoutIterator cb, KeyboardLayoutDone cbDone); + + @Async + private static native void iterateKeyboardLayout(KeyboardLayoutIterator cb); + + private static void iterateKeyboardLayout(KeyboardLayoutIterator cb, final AsyncCallback complete) { + iterateKeyboardLayout0(cb, () -> complete.complete(null)); + } + + @JSBody(params = { }, script = "return !!(navigator.keyboard && navigator.keyboard.getLayoutMap);") + private static native boolean checkKeyboardLayoutSupported(); + + @JSBody(params = { "doc" }, script = "return (typeof doc.exitPointerLock === \"function\") ? 1" + + ": ((typeof doc.mozExitPointerLock === \"function\") ? 2 : -1);") + private static native int getSupportedPointerLock(HTMLDocument doc); + + @JSBody(params = { "doc" }, script = "return (typeof doc.exitFullscreen === \"function\") ? 1" + + ": ((typeof doc.webkitExitFullscreen === \"function\") ? 2" + + ": ((typeof doc.mozExitFullscreen === \"function\") ? 3 : -1));") + private static native int getSupportedFullScreen(HTMLDocument doc); + + @JSBody(params = { "evt" }, script = "return (typeof evt.key === \"string\");") + private static native boolean hasKeyVar(KeyboardEvent evt); + + @JSBody(params = { "evt" }, script = "return (typeof evt.code === \"string\");") + private static native boolean hasCodeVar(KeyboardEvent evt); + + @JSBody(params = { "evt" }, script = "return evt.keyIdentifier;") + private static native String getKeyIdentifier(KeyboardEvent evt); + + @JSBody(params = { "fallback" }, script = "if(window.navigator.userActivation){return window.navigator.userActivation.hasBeenActive;}else{return fallback;}") + public static native boolean hasBeenActiveTeaVM(boolean fallback); + + @JSBody(params = { "m" }, script = "return m.offsetX;") + private static native int getOffsetX(MouseEvent m); + + @JSBody(params = { "m" }, script = "return m.offsetY;") + private static native int getOffsetY(MouseEvent m); + + @JSBody(params = { "e" }, script = "return (typeof e.which === \"number\") ? e.which : ((typeof e.keyCode === \"number\") ? e.keyCode : 0);") + private static native int getWhich(KeyboardEvent e); + + @JSBody(params = { "e" }, script = "return (typeof e.location === \"number\") ? e.location : 0;") + private static native int getLocationSafe(KeyboardEvent e); + + @JSBody(params = { "el", "i", "j" }, script = "el.setSelectionRange(el, i, j)") + private static native boolean setSelectionRange(HTMLElement el, int i, int j); + + public static int getWindowWidth() { + return windowWidth; + } + + public static int getWindowHeight() { + return windowHeight; + } + + public static int getVisualViewportX() { + return visualViewportX; + } + + public static int getVisualViewportY() { + return visualViewportY; + } + + public static int getVisualViewportW() { + return visualViewportW; + } + + public static int getVisualViewportH() { + return visualViewportH; + } + + public static boolean getWindowFocused() { + return isWindowFocused || isPointerLocked(); + } + + public static boolean isCloseRequested() { + return false; + } + + public static void setVSync(boolean enable) { + vsync = enable; + } + + @JSBody(params = { "doc" }, script = "return (typeof doc.visibilityState !== \"string\") || (doc.visibilityState === \"visible\");") + private static native boolean getVisibilityState(JSObject doc); + + @JSBody(params = { "win" }, script = "return (typeof win.devicePixelRatio === \"number\") ? win.devicePixelRatio : 1.0;") + static native double getDevicePixelRatio(Window win); + + public static void update() { + double r = getDevicePixelRatio(win); + if(r < 0.01) r = 1.0; + windowDPI = (float)r; + updateTouchOffset(); + int w = parent.getClientWidth(); + int h = parent.getClientHeight(); + int w2 = windowWidth = (int)(w * r); + int h2 = windowHeight = (int)(h * r); + if(PlatformRuntime.useVisualViewport) { + VisualViewport vv = PlatformRuntime.getVisualViewport(); + double scale = vv.getScale(); + visualViewportX = (int)(vv.getPageLeft() * r * scale); + visualViewportY = (int)(vv.getPageTop() * r * scale); + visualViewportW = (int)(vv.getWidth() * r * scale); + visualViewportH = (int)(vv.getHeight() * r * scale); + if(visualViewportW < 1) visualViewportW = 1; + if(visualViewportH < 1) visualViewportH = 1; + if(visualViewportX < 0) { + visualViewportW += visualViewportX; + visualViewportX = 0; + }else if(visualViewportX >= windowWidth) { + visualViewportX = windowWidth - 1; + } + if(visualViewportY < 0) { + visualViewportH += visualViewportY; + visualViewportY = 0; + }else if(visualViewportY >= windowHeight) { + visualViewportY = windowHeight - 1; + } + if((visualViewportX + visualViewportW) > windowWidth) { + visualViewportW = windowWidth - visualViewportX; + } + if((visualViewportY + visualViewportH) > windowHeight) { + visualViewportH = windowHeight - visualViewportY; + } + }else { + visualViewportX = 0; + visualViewportY = 0; + visualViewportW = w2; + visualViewportH = h2; + } + if(canvas.getWidth() != w2) { + canvas.setWidth(w2); + } + if(canvas.getHeight() != h2) { + canvas.setHeight(h2); + } + WebGLBackBuffer.flipBuffer(w2, h2); + PlatformScreenRecord.captureFrameHook(); + if(getVisibilityState(win.getDocument())) { + if(vsyncSupport && vsync) { + asyncRequestAnimationFrame(); + }else { + PlatformRuntime.swapDelayTeaVM(); + } + }else { + EagUtils.sleep(50l); + } + } + + @Async + private static native void asyncRequestAnimationFrame(); + + private static void asyncRequestAnimationFrame(AsyncCallback cb) { + if(vsyncWaiting != -1l) { + cb.error(new IllegalStateException("Already waiting for vsync!")); + return; + } + vsyncWaiting = PlatformRuntime.steadyTimeMillis(); + vsyncAsyncCallback = cb; + final boolean[] hasTimedOut = new boolean[] { false }; + final int[] timeout = new int[] { -1 }; + Window.requestAnimationFrame((d) -> { + if(!hasTimedOut[0]) { + hasTimedOut[0] = true; + if(vsyncWaiting != -1l) { + vsyncWaiting = -1l; + if(vsyncTimeout != -1 && vsyncTimeout == timeout[0]) { + try { + Window.clearTimeout(vsyncTimeout); + }catch(Throwable t) { + } + vsyncTimeout = -1; + } + vsyncAsyncCallback = null; + cb.complete(null); + } + } + }); + vsyncTimeout = timeout[0] = Window.setTimeout(() -> { + if(!hasTimedOut[0]) { + hasTimedOut[0] = true; + if(vsyncWaiting != -1l) { + vsyncTimeout = -1; + vsyncWaiting = -1l; + vsyncAsyncCallback = null; + cb.complete(null); + } + } + }, 50); + } + + public static boolean isVSyncSupported() { + return vsyncSupport; + } + + public static boolean wasResized() { + if (windowWidth != lastWasResizedWindowWidth || windowHeight != lastWasResizedWindowHeight + || windowDPI != lastWasResizedWindowDPI) { + lastWasResizedWindowWidth = windowWidth; + lastWasResizedWindowHeight = windowHeight; + lastWasResizedWindowDPI = windowDPI; + return true; + }else { + return false; + } + } + + public static boolean wasVisualViewportResized() { + if (visualViewportX != lastWasResizedVisualViewportX || visualViewportY != lastWasResizedVisualViewportY + || visualViewportW != lastWasResizedVisualViewportW + || visualViewportH != lastWasResizedVisualViewportH) { + lastWasResizedVisualViewportX = visualViewportX; + lastWasResizedVisualViewportY = visualViewportY; + lastWasResizedVisualViewportW = visualViewportW; + lastWasResizedVisualViewportH = visualViewportH; + return true; + }else { + return false; + } + } + + public static boolean keyboardNext() { + synchronized(keyEvents) { + if(unpressCTRL) { //un-press ctrl after copy/paste permission + keyEvents.clear(); + currentEventK = null; + keyStates[29] = false; + keyStates[157] = false; + keyStates[28] = false; + keyStates[219] = false; + keyStates[220] = false; + unpressCTRL = false; + return false; + } + currentEventK = null; + return !keyEvents.isEmpty() && (currentEventK = keyEvents.remove(0)) != null; + } + } + + public static void keyboardFireEvent(EnumFireKeyboardEvent eventType, int eagKey, char keyChar) { + synchronized(keyEvents) { + switch(eventType) { + case KEY_DOWN: + keyEvents.add(new VKeyEvent(-1, 0, eagKey, keyChar, EVENT_KEY_DOWN)); + break; + case KEY_UP: + keyEvents.add(new VKeyEvent(-1, 0, eagKey, '\0', EVENT_KEY_UP)); + break; + case KEY_REPEAT: + keyEvents.add(new VKeyEvent(-1, 0, eagKey, keyChar, EVENT_KEY_REPEAT)); + break; + default: + throw new UnsupportedOperationException(); + } + if(keyEvents.size() > 64) { + keyEvents.remove(0); + } + } + } + + public static boolean keyboardGetEventKeyState() { + return currentEventK == null ? false : (currentEventK.type != EVENT_KEY_UP); + } + + public static int keyboardGetEventKey() { + return currentEventK == null ? -1 : currentEventK.eagKey; + } + + @JSBody(params = { "evt" }, script = "return (typeof evt.key === \"string\") ? evt.key : \"\";") + private static native String getCharOrNull(KeyboardEvent evt); + + private static char keyToAsciiLegacy(int whichIn, boolean shiftUp) { + switch(whichIn) { + case 188: whichIn = 44; break; + case 109: whichIn = 45; break; + case 190: whichIn = 46; break; + case 191: whichIn = 47; break; + case 192: whichIn = 96; break; + case 220: whichIn = 92; break; + case 222: whichIn = 39; break; + case 221: whichIn = 93; break; + case 219: whichIn = 91; break; + case 173: whichIn = 45; break; + case 187: whichIn = 61; break; + case 186: whichIn = 59; break; + case 189: whichIn = 45; break; + default: break; + } + if(shiftUp) { + switch(whichIn) { + case 96: return '~'; + case 49: return '!'; + case 50: return '@'; + case 51: return '#'; + case 52: return '$'; + case 53: return '%'; + case 54: return '^'; + case 55: return '&'; + case 56: return '*'; + case 57: return '('; + case 48: return ')'; + case 45: return '_'; + case 61: return '+'; + case 91: return '{'; + case 93: return '}'; + case 92: return '|'; + case 59: return ':'; + case 39: return '\"'; + case 44: return '<'; + case 46: return '>'; + case 47: return '?'; + default: return (char)whichIn; + } + }else { + if(whichIn >= 65 && whichIn <= 90) { + return (char)(whichIn + 32); + }else { + return (char)whichIn; + } + } + } + + private static int asciiUpperToKeyLegacy(char charIn) { + switch(charIn) { + case '\n': return 17; + case '~': return 192; + case '!': return 49; + case '@': return 50; + case '#': return 51; + case '$': return 52; + case '%': return 53; + case '^': return 54; + case '&': return 55; + case '*': return 56; + case '(': return 57; + case ')': return 48; + case '_': return 173; + case '+': return 187; + case '{': return 219; + case '}': return 221; + case '|': return 220; + case ':': return 186; + case '\"': return 222; + case '<': return 188; + case '>': return 190; + case '?': return 191; + case '.': return 190; + case '\'': return 222; + case ';': return 186; + case '[': return 219; + case ']': return 221; + case ',': return 188; + case '/': return 191; + case '\\': return 220; + case '-': return 189; + case '`': return 192; + default: return (int)charIn; + } + } + + public static char keyboardGetEventCharacter() { + return currentEventK == null ? '\0' : currentEventK.keyChar; + } + + public static boolean keyboardIsKeyDown(int key) { + if(unpressCTRL) { //un-press ctrl after copy/paste permission + keyStates[28] = false; + keyStates[29] = false; + keyStates[157] = false; + keyStates[219] = false; + keyStates[220] = false; + } + return key < 0 || key >= keyStates.length ? false : keyStates[key]; + } + + public static boolean keyboardIsRepeatEvent() { + return currentEventK == null ? false : (currentEventK.type == EVENT_KEY_REPEAT); + } + + public static void keyboardEnableRepeatEvents(boolean b) { + enableRepeatEvents = b; + } + + public static boolean mouseNext() { + currentEvent = null; + synchronized(mouseEvents) { + return !mouseEvents.isEmpty() && (currentEvent = mouseEvents.remove(0)) != null; + } + } + + public static void mouseFireMoveEvent(EnumFireMouseEvent eventType, int posX, int posY) { + if(eventType == EnumFireMouseEvent.MOUSE_MOVE) { + synchronized(mouseEvents) { + mouseEvents.add(new VMouseEvent(posX, posY, -1, 0.0f, EVENT_MOUSE_MOVE)); + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + }else { + throw new UnsupportedOperationException(); + } + } + + public static void mouseFireButtonEvent(EnumFireMouseEvent eventType, int posX, int posY, int button) { + synchronized(mouseEvents) { + switch(eventType) { + case MOUSE_DOWN: + mouseEvents.add(new VMouseEvent(posX, posY, button, 0.0f, EVENT_MOUSE_DOWN)); + break; + case MOUSE_UP: + mouseEvents.add(new VMouseEvent(posX, posY, button, 0.0f, EVENT_MOUSE_UP)); + break; + default: + throw new UnsupportedOperationException(); + } + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + } + + public static void mouseFireWheelEvent(EnumFireMouseEvent eventType, int posX, int posY, float wheel) { + if(eventType == EnumFireMouseEvent.MOUSE_WHEEL) { + synchronized(mouseEvents) { + mouseEvents.add(new VMouseEvent(posX, posY, -1, wheel, EVENT_MOUSE_WHEEL)); + if(mouseEvents.size() > 64) { + mouseEvents.remove(0); + } + } + }else { + throw new UnsupportedOperationException(); + } + } + + public static boolean mouseGetEventButtonState() { + return currentEvent == null ? false : (currentEvent.type == EVENT_MOUSE_DOWN); + } + + public static int mouseGetEventButton() { + if(currentEvent == null || (currentEvent.type == EVENT_MOUSE_MOVE)) return -1; + return currentEvent.button; + } + + public static int mouseGetEventX() { + return currentEvent == null ? -1 : currentEvent.posX; + } + + public static int mouseGetEventY() { + return currentEvent == null ? -1 : currentEvent.posY; + } + + public static int mouseGetEventDWheel() { + return (currentEvent.type == EVENT_MOUSE_WHEEL) ? (currentEvent.wheel == 0.0f ? 0 : (currentEvent.wheel > 0.0f ? -1 : 1)) : 0; + } + + public static int mouseGetX() { + return mouseX; + } + + public static int mouseGetY() { + return mouseY; + } + + public static boolean mouseIsButtonDown(int i) { + return buttonStates[i]; + } + + public static int mouseGetDWheel() { + int ret = (int)mouseDWheel; + mouseDWheel = 0.0D; + return ret; + } + + public static void mouseSetGrabbed(boolean grab) { + if(pointerLockSupported == POINTER_LOCK_NONE) { + return; + } + long t = PlatformRuntime.steadyTimeMillis(); + pointerLockFlag = grab; + mouseGrabTimer = t; + if(grab) { + callRequestPointerLock(canvas); + if(mouseUngrabTimeout != -1) Window.clearTimeout(mouseUngrabTimeout); + mouseUngrabTimeout = -1; + if(t - mouseUngrabTimer < 3000l) { + mouseUngrabTimeout = Window.setTimeout(new TimerHandler() { + @Override + public void onTimer() { + callRequestPointerLock(canvas); + } + }, 3100 - (int)(t - mouseUngrabTimer)); + } + }else { + if(mouseUngrabTimeout != -1) Window.clearTimeout(mouseUngrabTimeout); + mouseUngrabTimeout = -1; + callExitPointerLock(win.getDocument()); + } + mouseDX = 0.0D; + mouseDY = 0.0D; + } + + private static boolean tryGrabCursorHook() { + if(pointerLockSupported == POINTER_LOCK_NONE) { + return false; + } + if(pointerLockFlag && !isPointerLocked()) { + mouseSetGrabbed(true); + return true; + } + return false; + } + + private static void callRequestPointerLock(HTMLElement el) { + switch(pointerLockSupported) { + case POINTER_LOCK_CORE: + try { + el.requestPointerLock(); + }catch(Throwable t) { + } + break; + case POINTER_LOCK_MOZ: + try { + mozRequestPointerLock(el); + }catch(Throwable t) { + } + break; + default: + PlatformRuntime.logger.warn("Failed to request pointer lock, it is not supported!"); + break; + } + } + + @JSBody(params = { "el" }, script = "el.mozRequestPointerLock();") + private static native void mozRequestPointerLock(HTMLElement el); + + private static void callExitPointerLock(HTMLDocument doc) { + switch(pointerLockSupported) { + case POINTER_LOCK_CORE: + try { + doc.exitPointerLock(); + }catch(Throwable t) { + } + break; + case POINTER_LOCK_MOZ: + try { + mozExitPointerLock(doc); + }catch(Throwable t) { + } + break; + default: + PlatformRuntime.logger.warn("Failed to exit pointer lock, it is not supported!"); + break; + } + } + + @JSBody(params = { "doc" }, script = "doc.mozExitPointerLock();") + private static native void mozExitPointerLock(HTMLDocument el); + + public static boolean mouseGrabSupported() { + return pointerLockSupported != POINTER_LOCK_NONE; + } + + public static boolean isMouseGrabbed() { + return pointerLockFlag; + } + + public static boolean isPointerLocked() { + switch(pointerLockSupported) { + case POINTER_LOCK_CORE: + return isPointerLocked0(win.getDocument(), canvas); + case POINTER_LOCK_MOZ: + return isMozPointerLocked0(win.getDocument(), canvas); + default: + return false; + } + } + + @JSBody(params = { "doc", "canvasEl" }, script = "return doc.pointerLockElement === canvasEl;") + private static native boolean isPointerLocked0(HTMLDocument doc, HTMLCanvasElement canvasEl); + + @JSBody(params = { "doc", "canvasEl" }, script = "return doc.mozPointerLockElement === canvasEl;") + private static native boolean isMozPointerLocked0(HTMLDocument doc, HTMLCanvasElement canvasEl); + + public static int mouseGetDX() { + int ret = (int)mouseDX; + mouseDX = 0.0D; + return ret; + } + + public static int mouseGetDY() { + int ret = (int)mouseDY; + mouseDY = 0.0D; + return ret; + } + + public static void mouseSetCursorPosition(int x, int y) { + // obsolete + } + + public static boolean mouseIsInsideWindow() { + return isMouseOverWindow; + } + + public static boolean contextLost() { + return PlatformRuntime.webgl.isContextLost(); + } + + private static int processFunctionKeys(int key) { + if(keyboardIsKeyDown(functionKeyModifier)) { + if(key >= 49 && key <= 57) { + key = key - 49 + 112; + } + } + return key; + } + + public static void setFunctionKeyModifier(int key) { + functionKeyModifier = key; + } + + public static void removeEventHandlers() { + if(contextmenu != null) { + parent.removeEventListener("contextmenu", contextmenu); + contextmenu = null; + } + if(mousedown != null) { + canvas.removeEventListener("mousedown", mousedown); + mousedown = null; + } + if(mouseup != null) { + canvas.removeEventListener("mouseup", mouseup); + mouseup = null; + } + if(mousemove != null) { + canvas.removeEventListener("mousemove", mousemove); + mousemove = null; + } + if(mouseenter != null) { + canvas.removeEventListener("mouseenter", mouseenter); + mouseenter = null; + } + if(mouseleave != null) { + canvas.removeEventListener("mouseleave", mouseleave); + mouseleave = null; + } + if(touchstart != null) { + canvas.removeEventListener("touchstart", touchstart); + touchstart = null; + } + if(touchmove != null) { + canvas.removeEventListener("touchmove", touchmove); + touchmove = null; + } + if(touchend != null) { + canvas.removeEventListener("touchend", touchend); + touchend = null; + } + if(touchcancel != null) { + canvas.removeEventListener("touchcancel", touchcancel); + touchcancel = null; + } + if(gamepadconnected != null) { + win.removeEventListener("gamepadconnected", gamepadconnected); + gamepadconnected = null; + } + if(gamepaddisconnected != null) { + win.removeEventListener("gamepaddisconnected", gamepaddisconnected); + gamepaddisconnected = null; + } + if(keydown != null) { + win.removeEventListener("keydown", keydown); + keydown = null; + } + if(keyup != null) { + win.removeEventListener("keyup", keyup); + keyup = null; + } + if(focus != null) { + win.removeEventListener("focus", focus); + focus = null; + } + if(blur != null) { + win.removeEventListener("blur", blur); + blur = null; + } + if(wheel != null) { + canvas.removeEventListener("wheel", wheel); + wheel = null; + } + if(pointerlock != null) { + win.getDocument().removeEventListener("pointerlockchange", pointerlock); + pointerlock = null; + } + if(mouseUngrabTimeout != -1) { + Window.clearTimeout(mouseUngrabTimeout); + mouseUngrabTimeout = -1; + } + if(vsyncSaveLockInterval != -1) { + try { + Window.clearInterval(vsyncSaveLockInterval); + }catch(Throwable t) { + } + vsyncSaveLockInterval = -1; + } + if(touchKeyboardField != null) { + touchKeyboardField.blur(); + if(parent != null) { + parent.removeChild(touchKeyboardField); + } + touchKeyboardField = null; + } + if(touchKeyboardOpenZone != null) { + if(touchKeyboardOpenZone_touchstart != null) { + touchKeyboardOpenZone.removeEventListener("touchstart", touchKeyboardOpenZone_touchstart); + touchKeyboardOpenZone_touchstart = null; + } + if(touchKeyboardOpenZone_touchend != null) { + touchKeyboardOpenZone.removeEventListener("touchend", touchKeyboardOpenZone_touchend); + touchKeyboardOpenZone_touchend = null; + } + if(touchKeyboardOpenZone_touchmove != null) { + touchKeyboardOpenZone.removeEventListener("touchmove", touchKeyboardOpenZone_touchmove); + touchKeyboardOpenZone_touchmove = null; + } + if(parent != null) { + parent.removeChild(touchKeyboardOpenZone); + } + touchKeyboardOpenZone = null; + } + try { + callExitPointerLock(win.getDocument()); + }catch(Throwable t) { + } + ClientMain.removeErrorHandler(win); + } + + public static void pressAnyKeyScreen() { + IClientConfigAdapter cfgAdapter = PlatformRuntime.getClientConfigAdapter(); + boolean allowBootMenu = cfgAdapter.isAllowBootMenu(); + if(isLikelyMobileBrowser) { + EarlyLoadScreen.paintEnable(PlatformOpenGL.checkVAOCapable(), allowBootMenu); + try { + isOnMobilePressAnyKey = true; + setupAnyKeyScreenMobile(allowBootMenu); + if(pressAnyKeyScreenMobile() && allowBootMenu) { + PlatformRuntime.enterBootMenu(); + } + }finally { + isOnMobilePressAnyKey = false; + } + }else { + if(mouseEvents.isEmpty() && keyEvents.isEmpty() && !hasBeenActiveTeaVM(false)) { + EarlyLoadScreen.paintEnable(PlatformOpenGL.checkVAOCapable(), allowBootMenu); + + while(mouseEvents.isEmpty() && keyEvents.isEmpty() && touchEvents.isEmpty()) { + EagUtils.sleep(100l); + } + } + } + hasShownPressAnyKey = true; + } + + private static void setupAnyKeyScreenMobile(boolean allowBootMenu) { + if(mobilePressAnyKeyScreen != null) { + parent.removeChild(mobilePressAnyKeyScreen); + } + mobilePressAnyKeyScreen = win.getDocument().createElement("div"); + mobilePressAnyKeyScreen.getClassList().add("_eaglercraftX_mobile_press_any_key"); + mobilePressAnyKeyScreen.setAttribute("style", "position:absolute;background-color:white;font-family:sans-serif;top:10%;left:10%;right:10%;bottom:10%;border:5px double black;padding:calc(5px + 7vh) 15px;text-align:center;font-size:20px;user-select:none;z-index:10;"); + mobilePressAnyKeyScreen.setInnerHTML("

Mobile Browser Detected

" + + "

You must manually select an option below to continue

" + + "

" + + (allowBootMenu ? "

" : "") + + "

(Tablets and phones with large screens work best)

"); + mobilePressAnyKeyScreen.querySelector("._eaglercraftX_mobile_launch_client").addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(isOnMobilePressAnyKey && mobilePressAnyKey != null) { + mobilePressAnyKey.call(false); + } + } + }); + if(allowBootMenu) { + mobilePressAnyKeyScreen.querySelector("._eaglercraftX_mobile_enter_boot_menu").addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(isOnMobilePressAnyKey && mobilePressAnyKey != null) { + mobilePressAnyKey.call(true); + } + } + }); + } + parent.appendChild(mobilePressAnyKeyScreen); + } + + @Async + private static native boolean pressAnyKeyScreenMobile(); + + private static void pressAnyKeyScreenMobile(final AsyncCallback complete) { + mobilePressAnyKey = new MobilePressAnyKeyHandler() { + @Override + public void call(boolean enterBootMenu) { + mobilePressAnyKey = null; + if(mobilePressAnyKeyScreen != null && parent != null) { + parent.removeChild(mobilePressAnyKeyScreen); + } + mobilePressAnyKeyScreen = null; + complete.complete(enterBootMenu); + } + }; + PlatformRuntime.logger.info("Waiting for user to select option on mobile press any key screen"); + } + + public static void clearEvenBuffers() { + mouseEvents.clear(); + keyEvents.clear(); + touchEvents.clear(); + net.lax1dude.eaglercraft.v1_8.Gamepad.clearEventBuffer(); + } + + @JSBody(params = {}, script = "return window.matchMedia(\"(display-mode: fullscreen)\");") + private static native JSObject fullscreenMediaQuery(); + + @JSBody(params = { "mediaQuery" }, script = "return mediaQuery.matches;") + private static native boolean mediaQueryMatches(JSObject mediaQuery); + + public static boolean supportsFullscreen() { + return fullscreenSupported != FULLSCREEN_NONE; + } + + public static void toggleFullscreen() { + if(fullscreenSupported == FULLSCREEN_NONE) return; + if (isFullscreen()) { + if (keyboardLockSupported) { + unlockKeys(); + lockKeys = false; + } + callExitFullscreen(win.getDocument()); + } else { + if (keyboardLockSupported) { + lockKeys(); + lockKeys = true; + } + callRequestFullscreen(canvas); + } + } + + public static boolean isFullscreen() { + return fullscreenSupported != FULLSCREEN_NONE && mediaQueryMatches(fullscreenQuery); + } + + @JSBody(params = { }, script = "navigator.keyboard.lock();") + private static native void lockKeys(); + + @JSBody(params = { }, script = "navigator.keyboard.unlock();") + private static native void unlockKeys(); + + @JSBody(params = { }, script = "return !!(navigator.keyboard && navigator.keyboard.lock);") + private static native boolean checkKeyboardLockSupported(); + + private static void callRequestFullscreen(HTMLElement el) { + switch(fullscreenSupported) { + case FULLSCREEN_CORE: + try { + requestFullscreen(el); + }catch(Throwable t) { + } + break; + case FULLSCREEN_WEBKIT: + try { + webkitRequestFullscreen(el); + }catch(Throwable t) { + } + break; + case FULLSCREEN_MOZ: + try { + mozRequestFullscreen(el); + }catch(Throwable t) { + } + break; + default: + PlatformRuntime.logger.warn("Failed to request fullscreen, it is not supported!"); + break; + } + } + + @JSBody(params = { "el" }, script = "el.requestFullscreen();") + private static native void requestFullscreen(HTMLElement element); + + @JSBody(params = { "el" }, script = "el.webkitRequestFullscreen();") + private static native void webkitRequestFullscreen(HTMLElement element); + + @JSBody(params = { "el" }, script = "el.mozRequestFullScreen();") + private static native void mozRequestFullscreen(HTMLElement element); + + private static void callExitFullscreen(HTMLDocument doc) { + switch(fullscreenSupported) { + case FULLSCREEN_CORE: + try { + exitFullscreen(doc); + }catch(Throwable t) { + } + break; + case FULLSCREEN_WEBKIT: + try { + webkitExitFullscreen(doc); + }catch(Throwable t) { + } + break; + case FULLSCREEN_MOZ: + try { + mozCancelFullscreen(doc); + }catch(Throwable t) { + } + break; + default: + PlatformRuntime.logger.warn("Failed to exit fullscreen, it is not supported!"); + break; + } + } + + @JSBody(params = { "doc" }, script = "doc.exitFullscreen();") + private static native void exitFullscreen(HTMLDocument doc); + + @JSBody(params = { "doc" }, script = "doc.webkitExitFullscreen();") + private static native void webkitExitFullscreen(HTMLDocument doc); + + @JSBody(params = { "doc" }, script = "doc.mozCancelFullscreen();") + private static native void mozCancelFullscreen(HTMLDocument doc); + + public static void showCursor(EnumCursorType cursor) { + switch(cursor) { + case DEFAULT: + default: + canvas.getStyle().setProperty("cursor", "default"); + break; + case HAND: + canvas.getStyle().setProperty("cursor", "pointer"); + break; + case TEXT: + canvas.getStyle().setProperty("cursor", "text"); + break; + } + } + + public static boolean touchNext() { + currentTouchEvent = null; + return !touchEvents.isEmpty() && (currentTouchEvent = touchEvents.remove(0)) != null; + } + + public static EnumTouchEvent touchGetEventType() { + return currentTouchEvent != null ? currentTouchEvent.type : null; + } + + public static int touchGetEventTouchPointCount() { + return currentTouchEvent != null ? currentTouchEvent.getEventTouches().size() : 0; + } + + public static int touchGetEventTouchX(int pointId) { + return currentTouchEvent != null ? currentTouchEvent.getEventTouches().get(pointId).posX : 0; + } + + public static int touchGetEventTouchY(int pointId) { + return currentTouchEvent != null ? currentTouchEvent.getEventTouches().get(pointId).posY : 0; + } + + public static float touchGetEventTouchRadiusX(int pointId) { + return currentTouchEvent != null ? (float)currentTouchEvent.getEventTouches().get(pointId).touch.getRadiusXSafe(5.0 * windowDPI) : 1.0f; + } + + public static float touchGetEventTouchRadiusY(int pointId) { + return currentTouchEvent != null ? (float)currentTouchEvent.getEventTouches().get(pointId).touch.getRadiusYSafe(5.0 * windowDPI) : 1.0f; + } + + public static float touchGetEventTouchRadiusMixed(int pointId) { + if(currentTouchEvent != null) { + Touch t = currentTouchEvent.getEventTouches().get(pointId).touch; + double d = 5.0 * windowDPI; + return (float)(t.getRadiusXSafe(d) * 0.5 + t.getRadiusYSafe(d) * 0.5); + }else { + return 1.0f; + } + } + + public static float touchGetEventTouchForce(int pointId) { + return currentTouchEvent != null ? (float)currentTouchEvent.getEventTouches().get(pointId).touch.getForceSafe(0.5) : 0.0f; + } + + public static int touchGetEventTouchPointUID(int pointId) { + return currentTouchEvent != null ? currentTouchEvent.getEventTouches().get(pointId).eventUID : -1; + } + + public static int touchPointCount() { + return currentTouchState != null ? currentTouchState.getTargetTouchesSize() : 0; + } + + public static int touchPointX(int pointId) { + return currentTouchState != null ? currentTouchState.getTargetTouches().get(pointId).posX : -1; + } + + public static int touchPointY(int pointId) { + return currentTouchState != null ? currentTouchState.getTargetTouches().get(pointId).posY : -1; + } + + public static float touchRadiusX(int pointId) { + return currentTouchState != null ? (float)currentTouchState.getTargetTouches().get(pointId).touch.getRadiusXSafe(5.0 * windowDPI) : 1.0f; + } + + public static float touchRadiusY(int pointId) { + return currentTouchState != null ? (float)currentTouchState.getTargetTouches().get(pointId).touch.getRadiusYSafe(5.0 * windowDPI) : 1.0f; + } + + public static float touchRadiusMixed(int pointId) { + if(currentTouchState != null) { + Touch t = currentTouchState.getTargetTouches().get(pointId).touch; + return (float)(t.getRadiusX() * 0.5 + t.getRadiusY() * 0.5); + }else { + return 1.0f; + } + } + + public static float touchForce(int pointId) { + return currentTouchState != null ? (float)currentTouchState.getTargetTouches().get(pointId).touch.getForceSafe(0.5) : 0.0f; + } + + public static int touchPointUID(int pointId) { + return currentTouchState != null ? currentTouchState.getTargetTouches().get(pointId).eventUID : -1; + } + + private static final Map touchIDtoUID = new HashMap<>(); + private static int touchUIDnum = 0; + + private static final SortedTouchEvent.ITouchUIDMapper touchUIDMapperCreate = (idx) -> { + Integer ret = touchIDtoUID.get(idx); + if(ret != null) return ret.intValue(); + int r = touchUIDnum++; + touchIDtoUID.put(idx, r); + return r; + }; + + private static final SortedTouchEvent.ITouchUIDMapper touchUIDMapper = (idx) -> { + Integer ret = touchIDtoUID.get(idx); + return ret != null ? ret.intValue() : -1; + }; + + public static void touchBufferFlush() { + pointerLockSupported = 0; + pointerLockFlag = true; + currentTouchState = null; + touchEvents.clear(); + } + + // Note: this can't be called from the main loop, don't try + private static void touchOpenDeviceKeyboard() { + if(!touchIsDeviceKeyboardOpenMAYBE()) { + if(touchKeyboardField != null) { + touchKeyboardField.blur(); + touchKeyboardField.setValue(""); + EagUtils.sleep(10l); + if(touchKeyboardForm != null) { + touchKeyboardForm.removeChild(touchKeyboardField); + }else { + touchKeyboardField.delete(); + } + touchKeyboardField = null; + if(touchKeyboardForm != null) { + parent.removeChild(touchKeyboardForm); + touchKeyboardForm = null; + } + return; + } + if(touchKeyboardForm != null) { + parent.removeChild(touchKeyboardForm); + touchKeyboardForm = null; + } + touchKeyboardForm = (HTMLFormElement) win.getDocument().createElement("form"); + touchKeyboardForm.setAttribute("autocomplete", "off"); + touchKeyboardForm.getClassList().add("_eaglercraftX_text_input_wrapper"); + CSSStyleDeclaration decl = touchKeyboardForm.getStyle(); + decl.setProperty("position", "absolute"); + decl.setProperty("top", "0px"); + decl.setProperty("left", "0px"); + decl.setProperty("right", "0px"); + decl.setProperty("bottom", "0px"); + decl.setProperty("z-index", "-100"); + decl.setProperty("margin", "0px"); + decl.setProperty("padding", "0px"); + decl.setProperty("border", "none"); + touchKeyboardForm.addEventListener("submit", new EventListener() { + @Override + public void handleEvent(Event evt) { + evt.preventDefault(); + evt.stopPropagation(); + JSObject obj = evt.getTimeStamp(); + if(TeaVMUtils.isTruthy(obj)) { + double d = ((JSNumber)obj).doubleValue(); + if(lastTouchKeyboardEvtA != 0.0 && (d - lastTouchKeyboardEvtA) < 10.0) { + return; + } + if(lastTouchKeyboardEvtB != 0.0 && (d - lastTouchKeyboardEvtB) < 10.0) { + return; + } + if(lastTouchKeyboardEvtC != 0.0 && (d - lastTouchKeyboardEvtC) < 10.0) { + return; + } + if(!showniOSReturnTouchKeyboardWarning) { + PlatformRuntime.logger.info("Note: Generating return keystroke from submit event on form, this browser probably doesn't generate keydown/beforeinput/input when enter/return is pressed on the on-screen keyboard"); + showniOSReturnTouchKeyboardWarning = true; + } + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, KeyboardConstants.KEY_RETURN, '\n'); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, KeyboardConstants.KEY_RETURN, '\n'); + } + + } + }); + touchKeyboardField = (HTMLInputElement) win.getDocument().createElement("input"); + touchKeyboardField.setType("password"); + touchKeyboardField.setValue(" "); + touchKeyboardField.getClassList().add("_eaglercraftX_text_input_element"); + touchKeyboardField.setAttribute("autocomplete", "off"); + decl = touchKeyboardField.getStyle(); + decl.setProperty("position", "absolute"); + decl.setProperty("top", "0px"); + decl.setProperty("left", "0px"); + decl.setProperty("right", "0px"); + decl.setProperty("bottom", "0px"); + decl.setProperty("z-index", "-100"); + decl.setProperty("margin", "0px"); + decl.setProperty("padding", "0px"); + decl.setProperty("border", "none"); + decl.setProperty("-webkit-touch-callout", "default"); + touchKeyboardField.addEventListener("beforeinput", new EventListener() { + @Override + public void handleEvent(InputEvent evt) { + if(touchKeyboardField != evt.getTarget()) return; + if(!shownTouchKeyboardEventWarning) { + PlatformRuntime.logger.info("Note: Caught beforeinput event from on-screen keyboard, browser probably does not generate global keydown/keyup events on text fields, or does not respond to cancelling keydown"); + shownTouchKeyboardEventWarning = true; + } + JSObject obj = evt.getTimeStamp(); + if(TeaVMUtils.isTruthy(obj)) { + double d = ((JSNumber)obj).doubleValue(); + if(lastTouchKeyboardEvtA != 0.0 && (d - lastTouchKeyboardEvtA) < 10.0) { + return; + } + lastTouchKeyboardEvtB = d; + } + evt.preventDefault(); + evt.stopPropagation(); + switch(evt.getInputType()) { + case "insertParagraph": + case "insertLineBreak": + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, KeyboardConstants.KEY_RETURN, '\n'); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, KeyboardConstants.KEY_RETURN, '\n'); + break; + case "deleteWordBackward": + case "deleteSoftLineBackward": + case "deleteHardLineBackward": + case "deleteEntireSoftLine": + case "deleteContentBackward": + case "deleteContent": + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, KeyboardConstants.KEY_BACK, '\0'); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, KeyboardConstants.KEY_BACK, '\0'); + break; + case "deleteWordForward": + case "deleteSoftLineForward": + case "deleteHardLineForward": + case "deleteContentForward": + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, KeyboardConstants.KEY_DELETE, '\0'); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, KeyboardConstants.KEY_DELETE, '\0'); + break; + case "insertText": + case "insertCompositionText": + case "insertReplacementText": + String eventsToGenerate = evt.getData(); + for(int i = 0, l = eventsToGenerate.length(); i < l; ++i) { + char c = eventsToGenerate.charAt(i); + int eag = KeyboardConstants.getEaglerKeyFromBrowser(asciiUpperToKeyLegacy(Character.toUpperCase(c)), 0); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, eag, c); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, eag, c); + } + break; + case "insertFromPaste": + case "insertFromPasteAsQuotation": + case "insertFromDrop": + case "insertFromYank": + case "insertLink": + synchronized(pastedStrings) { + pastedStrings.add(evt.getData()); + if(pastedStrings.size() > 64) { + pastedStrings.remove(0); + } + } + break; + case "historyUndo": + case "historyRedo": + case "deleteByDrag": + case "deleteByCut": + break; + default: + PlatformRuntime.logger.info("Ingoring InputEvent.inputType \"{}\" from on-screen keyboard", evt.getInputType()); + break; + } + } + }); + touchKeyboardField.addEventListener("input", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(touchKeyboardField != evt.getTarget()) return; + JSObject obj = evt.getTimeStamp(); + if(!shownLegacyTouchKeyboardWarning) { + PlatformRuntime.logger.info("Note: Caught legacy input events from on-screen keyboard, browser could be outdated and doesn't support beforeinput event, or does not respond to cancelling beforeinput"); + shownLegacyTouchKeyboardWarning = true; + } + if(TeaVMUtils.isTruthy(obj)) { + double d = ((JSNumber)obj).doubleValue(); + if(lastTouchKeyboardEvtA != 0.0 && (d - lastTouchKeyboardEvtA) < 10.0) { + return; + } + if(lastTouchKeyboardEvtB != 0.0 && (d - lastTouchKeyboardEvtB) < 10.0) { + return; + } + lastTouchKeyboardEvtC = d; + } + String val = touchKeyboardField.getValue(); + int l = val.length(); + if(l == 0) { + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, KeyboardConstants.KEY_BACK, '\0'); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, KeyboardConstants.KEY_BACK, '\0'); + }else if(l == 1) { + char c = val.charAt(0); + int eag = KeyboardConstants.getEaglerKeyFromBrowser(asciiUpperToKeyLegacy(Character.toUpperCase(c)), 0); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, eag, c); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, eag, c); + }else { + val = val.trim(); + l = val.length(); + if(l == 0) { + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, KeyboardConstants.KEY_SPACE, ' '); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, KeyboardConstants.KEY_SPACE, ' '); + }else { + char c = val.charAt(l - 1); + int eag = KeyboardConstants.getEaglerKeyFromBrowser(asciiUpperToKeyLegacy(Character.toUpperCase(c)), 0); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_DOWN, eag, c); + keyboardFireEvent(EnumFireKeyboardEvent.KEY_UP, eag, c); + } + } + touchKeyboardField.setValue(" "); + setSelectionRange(touchKeyboardField, 1, 1); + } + }); + touchKeyboardField.addEventListener("focus", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(touchKeyboardField != evt.getTarget()) return; + touchKeyboardField.setValue(" "); + setSelectionRange(touchKeyboardField, 1, 1); + } + }); + touchKeyboardField.addEventListener("select", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(touchKeyboardField != evt.getTarget()) return; + evt.preventDefault(); + evt.stopPropagation(); + touchKeyboardField.setValue(" "); + setSelectionRange(touchKeyboardField, 1, 1); + } + }); + touchKeyboardForm.appendChild(touchKeyboardField); + parent.appendChild(touchKeyboardForm); + touchKeyboardField.setValue(" "); + touchKeyboardField.focus(); + touchKeyboardField.select(); + setSelectionRange(touchKeyboardField, 1, 1); + }else { + touchCloseDeviceKeyboard0(false); + } + } + + public static String touchGetPastedString() { + synchronized(pastedStrings) { + return pastedStrings.isEmpty() ? null : pastedStrings.remove(0); + } + } + + public static void touchSetOpenKeyboardZone(int x, int y, int w, int h) { + if(w != 0 && h != 0) { + int xx = (int)(x / windowDPI); + int yy = (int)((windowHeight - y - h) / windowDPI); + int ww = (int)(w / windowDPI); + int hh = (int)(h / windowDPI); + if(xx != touchOpenZoneX || yy != touchOpenZoneY || ww != touchOpenZoneW || hh != touchOpenZoneH) { + CSSStyleDeclaration decl = touchKeyboardOpenZone.getStyle(); + decl.setProperty("display", "block"); + decl.setProperty("left", "" + xx + "px"); + decl.setProperty("top", "" + yy + "px"); + decl.setProperty("width", "" + ww + "px"); + decl.setProperty("height", "" + hh + "px"); + touchOpenZoneX = xx; + touchOpenZoneY = yy; + touchOpenZoneW = ww; + touchOpenZoneH = hh; + } + }else { + if(touchOpenZoneW != 0 || touchOpenZoneH != 0) { + CSSStyleDeclaration decl = touchKeyboardOpenZone.getStyle(); + decl.setProperty("display", "none"); + decl.setProperty("top", "0px"); + decl.setProperty("left", "0px"); + decl.setProperty("width", "0px"); + decl.setProperty("height", "0px"); + } + touchOpenZoneX = 0; + touchOpenZoneY = 0; + touchOpenZoneW = 0; + touchOpenZoneH = 0; + } + } + + public static void touchCloseDeviceKeyboard() { + touchCloseDeviceKeyboard0(true); + } + + private static void touchCloseDeviceKeyboard0(boolean sync) { + if(touchKeyboardField != null) { + touchKeyboardField.blur(); + touchKeyboardField.setValue(""); + if(sync) { + EagUtils.sleep(10l); + if(touchKeyboardForm != null) { + touchKeyboardForm.removeChild(touchKeyboardField); + }else { + touchKeyboardField.delete(); + } + touchKeyboardField = null; + }else { + final HTMLInputElement el = touchKeyboardField; + final HTMLFormElement el2 = touchKeyboardForm; + Window.setTimeout(() -> { + if(el2 != null) { + el2.removeChild(el); + el2.delete(); + }else { + el.delete(); + } + }, 10); + touchKeyboardField = null; + touchKeyboardForm = null; + return; + } + } + if(touchKeyboardForm != null) { + if(parent != null) { + parent.removeChild(touchKeyboardForm); + }else { + touchKeyboardForm.delete(); + } + touchKeyboardForm = null; + } + } + + public static boolean touchIsDeviceKeyboardOpenMAYBE() { + return touchKeyboardField != null && isActiveElement(win.getDocument(), touchKeyboardField); + } + + @JSBody(params = { "doc", "el" }, script = "return doc.activeElement === el;") + private static native boolean isActiveElement(HTMLDocument doc, HTMLElement el); + + private static void enumerateGamepads() { + if(!gamepadSupported) return; + if(selectedGamepad != null) { + selectedGamepad = updateGamepad(selectedGamepad); + if(selectedGamepad == null || !TeaVMUtils.isTruthy(selectedGamepad) || !selectedGamepad.isConnected()) { + selectedGamepad = null; + } + } + List oldList = null; + if(!gamepadList.isEmpty()) { + oldList = new ArrayList<>(gamepadList); + gamepadList.clear(); + } + Gamepad[] gamepads = Navigator.getGamepads(); + if(gamepads != null && gamepads.length > 0) { + for(int i = 0; i < gamepads.length; ++i) { + Gamepad g = gamepads[i]; + if(TeaVMUtils.isTruthy(g) && g.isConnected() && "standard".equals(g.getMapping())) { + gamepadList.add(g); + } + } + } + if(selectedGamepad != null) { + selectedGamepadName = selectedGamepad.getId(); + } + if(oldList == null) { + if(!gamepadList.isEmpty()) { + for(int i = 0, l = gamepadList.size(); i < l; ++i) { + PlatformRuntime.logger.info("Found controller: {}", gamepadList.get(i).getId()); + } + } + }else { + if(gamepadList.isEmpty()) { + for(int i = 0, l = oldList.size(); i < l; ++i) { + PlatformRuntime.logger.info("Lost controller: {}", oldList.get(i).getId()); + } + }else { + Map oldDevCounts = new HashMap<>(); + for(Gamepad gg : oldList) { + String s = gg.getId(); + Integer i = oldDevCounts.get(s); + if(i != null) { + oldDevCounts.put(s, Integer.valueOf(i.intValue() + 1)); + }else { + oldDevCounts.put(s, Integer.valueOf(1)); + } + } + Map newDevCounts = new HashMap<>(); + for(Gamepad gg : gamepadList) { + String s = gg.getId(); + Integer i = newDevCounts.get(s); + if(i != null) { + newDevCounts.put(s, Integer.valueOf(i.intValue() + 1)); + }else { + newDevCounts.put(s, Integer.valueOf(1)); + } + } + for(Entry etr : oldDevCounts.entrySet()) { + Integer i = newDevCounts.get(etr.getKey()); + if(i == null) { + for(int j = 0, l = etr.getValue().intValue(); j < l; ++j) { + PlatformRuntime.logger.info("Lost controller: {}", etr.getKey()); + } + }else { + int j = i.intValue(); + int k = etr.getValue().intValue(); + if(k != j) { + if(k < j) { + for(int m = 0, l = j - k; m < l; ++m) { + PlatformRuntime.logger.info("Found controller: {}", etr.getKey()); + } + }else { + for(int m = 0, l = k - j; m < l; ++m) { + PlatformRuntime.logger.info("Lost controller: {}", etr.getKey()); + } + } + } + } + } + for(Entry etr : newDevCounts.entrySet()) { + Integer i = oldDevCounts.get(etr.getKey()); + if(i == null) { + for(int j = 0, l = etr.getValue().intValue(); j < l; ++j) { + PlatformRuntime.logger.info("Found controller: {}", etr.getKey()); + } + } + } + } + + } + } + + public static int gamepadGetValidDeviceCount() { + if(!gamepadSupported) return 0; + return gamepadList.size(); + } + + public static String gamepadGetDeviceName(int deviceId) { + if(gamepadSupported && deviceId >= 0 && deviceId < gamepadList.size()) { + return gamepadList.get(deviceId).getId(); + }else { + return "Unknown"; + } + } + + public static void gamepadSetSelectedDevice(int deviceId) { + if(!gamepadSupported) return; + gamepadReset(); + if(deviceId >= 0 && deviceId < gamepadList.size()) { + selectedGamepad = gamepadList.get(deviceId); + gamepadTimestamp = -1.0; + if(!TeaVMUtils.isTruthy(selectedGamepad) || !selectedGamepad.isConnected()) { + selectedGamepad = null; + } + }else { + selectedGamepad = null; + } + } + + private static void gamepadReset() { + for(int i = 0; i < gamepadButtonStates.length; ++i) { + gamepadButtonStates[i] = false; + } + for(int i = 0; i < gamepadAxisStates.length; ++i) { + gamepadAxisStates[i] = 0.0f; + } + } + + @JSBody(params = { }, script = "return (typeof navigator.getGamepads === \"function\");") + private static native boolean gamepadSupported(); + + @JSBody(params = { "gp" }, script = "return navigator.getGamepads()[gp.index];") + private static native Gamepad updateGamepad(Gamepad gp); + + public static void gamepadUpdate() { + if(!gamepadSupported) return; + if(selectedGamepad != null) { + selectedGamepad = updateGamepad(selectedGamepad); + if(selectedGamepad == null || !TeaVMUtils.isTruthy(selectedGamepad) || !selectedGamepad.isConnected()) { + gamepadReset(); + selectedGamepad = null; + return; + } + double ts = selectedGamepad.getTimestamp(); + if(ts != gamepadTimestamp) { + gamepadTimestamp = ts; + gamepadReset(); + GamepadButton[] btns = selectedGamepad.getButtons(); + for(int i = 0; i < btns.length; ++i) { + int j = GamepadConstants.getEaglerButtonFromBrowser(i); + if(j >= 0 && j < gamepadButtonStates.length) { + gamepadButtonStates[j] = btns[i].isPressed(); + } + } + double[] axes = selectedGamepad.getAxes(); + for(int i = 0; i < axes.length; ++i) { + if(i >= 4) { + break; + } + gamepadAxisStates[i] = (float)axes[i]; + } + } + }else { + gamepadReset(); + } + } + + public static boolean gamepadIsValid() { + if(!gamepadSupported) return false; + return selectedGamepad != null; + } + + public static String gamepadGetName() { + return gamepadSupported && selectedGamepad != null ? selectedGamepadName : "Unknown"; + } + + public static boolean gamepadGetButtonState(int button) { + return gamepadSupported && selectedGamepad != null && button >= 0 && button < gamepadButtonStates.length ? gamepadButtonStates[button] : false; + } + + public static float gamepadGetAxis(int axis) { + return gamepadSupported && selectedGamepad != null && axis >= 0 && axis < gamepadAxisStates.length ? gamepadAxisStates[axis] : 0.0f; + } + + public static float getDPI() { + return windowDPI; + } + + @JSBody(params = { "el" }, script = "var xx = 0; var yy = 0;" + + "while(el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {" + + "xx += el.offsetLeft - el.scrollLeft;" + + "yy += el.offsetTop - el.scrollTop;" + + "el = el.offsetParent;" + + "} return { left: xx, top: yy };") + private static native TextRectangle getPositionOf(HTMLElement el); + + private static void updateTouchOffset() { + try { + TextRectangle bounds = getPositionOf(canvas); + touchOffsetXTeaVM = bounds.getLeft(); + touchOffsetYTeaVM = bounds.getTop(); + }catch(Throwable t) { + touchOffsetXTeaVM = 0; + touchOffsetYTeaVM = 0; + } + } + + static void initWindowSize(int sw, int sh, float dpi) { + windowWidth = sw; + windowHeight = sh; + windowDPI = dpi; + visualViewportX = 0; + visualViewportY = 0; + visualViewportW = sw; + visualViewportH = sh; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java index 999a7c9..1ba443e 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java @@ -1,218 +1,40 @@ package net.lax1dude.eaglercraft.v1_8.internal; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import org.teavm.interop.Async; -import org.teavm.interop.AsyncCallback; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.events.MessageEvent; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.websocket.WebSocket; - -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMServerQuery; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMWebSocketClient; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; /** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ public class PlatformNetworking { - - private static WebSocket sock = null; - private static boolean sockIsConnecting = false; - private static boolean sockIsConnected = false; - private static boolean sockIsAlive = false; - private static boolean sockIsFailed = false; - private static LinkedList readPackets = new LinkedList(); - private static String currentSockURI = null; - private static EnumServerRateLimit serverRateLimit = null; - + private static final Logger logger = LogManager.getLogger("PlatformNetworking"); - - public static EnumEaglerConnectionState playConnectionState() { - return !sockIsConnected ? (sockIsFailed ? EnumEaglerConnectionState.FAILED : EnumEaglerConnectionState.CLOSED) - : (sockIsConnecting ? EnumEaglerConnectionState.CONNECTING : EnumEaglerConnectionState.CONNECTED); - } - - public static void startPlayConnection(String destination) { - sockIsFailed = !connectWebSocket(destination).booleanValue(); - } - - @JSBody(params = { "obj" }, script = "return typeof obj === \"string\";") - private static native boolean isString(JSObject obj); - - @Async - public static native Boolean connectWebSocket(String sockURI); - - private static void connectWebSocket(String sockURI, final AsyncCallback cb) { - sockIsConnecting = true; - sockIsConnected = false; - sockIsAlive = false; - currentSockURI = sockURI; + + public static IWebSocketClient openWebSocket(String socketURI) { try { - sock = WebSocket.create(sockURI); - } catch (Throwable t) { - sockIsFailed = true; - sockIsConnecting = false; - sockIsAlive = false; - cb.complete(Boolean.FALSE); - return; - } - final WebSocket oldSock = sock; - sock.setBinaryType("arraybuffer"); - TeaVMUtils.addEventListener(sock, "open", new EventListener() { - @Override - public void handleEvent(Event evt) { - if (oldSock != sock) - return; - sockIsConnecting = false; - sockIsAlive = false; - sockIsConnected = true; - synchronized (readPackets) { - readPackets.clear(); - } - cb.complete(Boolean.TRUE); - } - }); - TeaVMUtils.addEventListener(sock, "close", new EventListener() { - @Override - public void handleEvent(Event evt) { - if (oldSock != sock) - return; - sock = null; - boolean b = sockIsConnecting; - sockIsConnecting = false; - sockIsConnected = false; - sockIsAlive = false; - if (b) - cb.complete(Boolean.FALSE); - } - }); - TeaVMUtils.addEventListener(sock, "message", new EventListener() { - @Override - public void handleEvent(MessageEvent evt) { - if (oldSock != sock) - return; - sockIsAlive = true; - if (isString(evt.getData())) { - String str = evt.getDataAsString(); - if (str.equalsIgnoreCase("BLOCKED")) { - logger.error("Reached full IP ratelimit!"); - serverRateLimit = EnumServerRateLimit.BLOCKED; - } else if (str.equalsIgnoreCase("LOCKED")) { - logger.error("Reached full IP ratelimit lockout!"); - serverRateLimit = EnumServerRateLimit.LOCKED_OUT; - } - } else { - synchronized (readPackets) { - readPackets.add(TeaVMUtils.wrapByteArrayBuffer(evt.getDataAsArray())); - } - } - } - }); - TeaVMUtils.addEventListener(sock, "error", new EventListener() { - @Override - public void handleEvent(Event evt) { - if (oldSock != sock) - return; - if (sockIsConnecting) { - sockIsFailed = true; - sockIsConnecting = false; - sockIsAlive = false; - cb.complete(Boolean.FALSE); - } - } - }); - } - - public static void playDisconnect() { - if (sock != null) - sock.close(); - sockIsConnecting = false; - } - - public static byte[] readPlayPacket() { - synchronized (readPackets) { - if (!readPackets.isEmpty()) { - return readPackets.remove(0); - } else { - return null; - } - } - } - - public static List readAllPacket() { - synchronized (readPackets) { - if (!readPackets.isEmpty()) { - List ret = new ArrayList<>(readPackets); - readPackets.clear(); - return ret; - } else { - return null; - } - } - } - - public static int countAvailableReadData() { - int total = 0; - synchronized (readPackets) { - for (int i = 0, l = readPackets.size(); i < l; ++i) { - total += readPackets.get(i).length; - } - } - return total; - } - - @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") - protected static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); - - public static void writePlayPacket(byte[] pkt) { - if (sock != null && !sockIsConnecting) { - nativeBinarySend(sock, TeaVMUtils.unwrapArrayBuffer(pkt)); - } - } - - public static IServerQuery sendServerQuery(String uri, String accept) { - try { - return new TeaVMServerQuery(uri, accept); - } catch (Throwable t) { - logger.error("Could not send query to \"{}\"!", uri); + return new TeaVMWebSocketClient(socketURI); + }catch(Throwable t) { + logger.error("Could not open WebSocket to \"{}\"!", socketURI); logger.error(t); return null; } } - - public static EnumServerRateLimit getRateLimit() { - return serverRateLimit == null ? EnumServerRateLimit.OK : serverRateLimit; - } - - public static String getCurrentURI() { - return currentSockURI; + + public static IWebSocketClient openWebSocketUnsafe(String socketURI) { + return new TeaVMWebSocketClient(socketURI); } + } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java index 2493441..d1f9396 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java @@ -1,602 +1,865 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import org.teavm.jso.webgl.WebGLUniformLocation; - -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext; -import net.lax1dude.eaglercraft.v1_8.log4j.Level; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; - -/** - * Copyright (c) 2022-2023 lax1dude, hoosiertransfer, ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformOpenGL { - - private static final Logger logger = LogManager.getLogger("PlatformOpenGL"); - - static WebGL2RenderingContext ctx = null; - - static boolean hasDebugRenderInfoExt = false; - static boolean hasFramebufferHDR16FSupport = false; - static boolean hasFramebufferHDR32FSupport = false; - static boolean hasLinearHDR32FSupport = false; - - static void setCurrentContext(WebGL2RenderingContext context) { - ctx = context; - hasDebugRenderInfoExt = ctx.getExtension("WEBGL_debug_renderer_info") != null; - hasFramebufferHDR16FSupport = ctx.getExtension("EXT_color_buffer_half_float") != null; - hasFramebufferHDR32FSupport = ctx.getExtension("EXT_color_buffer_float") != null; - hasLinearHDR32FSupport = ctx.getExtension("OES_texture_float_linear") != null; - _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); - } - - public static final void _wglEnable(int glEnum) { - ctx.enable(glEnum); - } - - public static final void _wglDisable(int glEnum) { - ctx.disable(glEnum); - } - - public static final void _wglClearColor(float r, float g, float b, float a) { - ctx.clearColor(r, g, b, a); - } - - public static final void _wglClearDepth(float f) { - ctx.clearDepth(f); - } - - public static final void _wglClear(int bits) { - ctx.clear(bits); - } - - public static final void _wglDepthFunc(int glEnum) { - ctx.depthFunc(glEnum); - } - - public static final void _wglDepthMask(boolean mask) { - ctx.depthMask(mask); - } - - public static final void _wglCullFace(int glEnum) { - ctx.cullFace(glEnum); - } - - public static final void _wglViewport(int x, int y, int w, int h) { - ctx.viewport(x, y, w, h); - } - - public static final void _wglBlendFunc(int src, int dst) { - ctx.blendFunc(src, dst); - } - - public static final void _wglBlendFuncSeparate(int srcColor, int dstColor, - int srcAlpha, int dstAlpha) { - ctx.blendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha); - } - - public static final void _wglBlendEquation(int glEnum) { - ctx.blendEquation(glEnum); - } - - public static final void _wglBlendColor(float r, float g, float b, float a) { - ctx.blendColor(r, g, b, a); - } - - public static final void _wglColorMask(boolean r, boolean g, boolean b, boolean a) { - ctx.colorMask(r, g, b, a); - } - - public static final void _wglDrawBuffers(int buffer) { - ctx.drawBuffers(new int[] { buffer }); - } - - public static final void _wglDrawBuffers(int[] buffers) { - ctx.drawBuffers(buffers); - } - - public static final void _wglReadBuffer(int buffer) { - ctx.readBuffer(buffer); - } - - public static final void _wglPolygonOffset(float f1, float f2) { - ctx.polygonOffset(f1, f2); - } - - public static final void _wglLineWidth(float width) { - ctx.lineWidth(width); - } - - public static final IBufferGL _wglGenBuffers() { - return new OpenGLObjects.BufferGL(ctx.createBuffer()); - } - - public static final ITextureGL _wglGenTextures() { - return new OpenGLObjects.TextureGL(ctx.createTexture()); - } - - public static final IBufferArrayGL _wglGenVertexArrays() { - return new OpenGLObjects.BufferArrayGL(ctx.createVertexArray()); - } - - public static final IProgramGL _wglCreateProgram() { - return new OpenGLObjects.ProgramGL(ctx.createProgram()); - } - - public static final IShaderGL _wglCreateShader(int type) { - return new OpenGLObjects.ShaderGL(ctx.createShader(type)); - } - - public static final IFramebufferGL _wglCreateFramebuffer() { - return new OpenGLObjects.FramebufferGL(ctx.createFramebuffer()); - } - - public static final IRenderbufferGL _wglCreateRenderbuffer() { - return new OpenGLObjects.RenderbufferGL(ctx.createRenderbuffer()); - } - - public static final IQueryGL _wglGenQueries() { - return new OpenGLObjects.QueryGL(ctx.createQuery()); - } - - public static final void _wglDeleteBuffers(IBufferGL obj) { - ctx.deleteBuffer(obj == null ? null : ((OpenGLObjects.BufferGL)obj).ptr); - } - - public static final void _wglDeleteTextures(ITextureGL obj) { - ctx.deleteTexture(obj == null ? null : ((OpenGLObjects.TextureGL)obj).ptr); - } - - public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) { - ctx.deleteVertexArray(obj == null ? null : ((OpenGLObjects.BufferArrayGL)obj).ptr); - } - - public static final void _wglDeleteProgram(IProgramGL obj) { - ctx.deleteProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); - } - - public static final void _wglDeleteShader(IShaderGL obj) { - ctx.deleteShader(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr); - } - - public static final void _wglDeleteFramebuffer(IFramebufferGL obj) { - ctx.deleteFramebuffer(obj == null ? null : ((OpenGLObjects.FramebufferGL)obj).ptr); - } - - public static final void _wglDeleteRenderbuffer(IRenderbufferGL obj) { - ctx.deleteRenderbuffer(obj == null ? null : ((OpenGLObjects.RenderbufferGL)obj).ptr); - } - - public static final void _wglDeleteQueries(IQueryGL obj) { - ctx.deleteQuery(obj == null ? null : ((OpenGLObjects.QueryGL)obj).ptr); - } - - public static final void _wglBindBuffer(int target, IBufferGL obj) { - ctx.bindBuffer(target, obj == null ? null : ((OpenGLObjects.BufferGL)obj).ptr); - } - - public static final void _wglBufferData(int target, ByteBuffer data, int usage) { - ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView8(data), usage); - } - - public static final void _wglBufferData(int target, IntBuffer data, int usage) { - ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView32(data), usage); - } - - public static final void _wglBufferData(int target, FloatBuffer data, int usage) { - ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data), usage); - } - - public static final void _wglBufferData(int target, int size, int usage) { - ctx.bufferData(target, size, usage); - } - - public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) { - ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView8(data)); - } - - public static final void _wglBufferSubData(int target, int offset, IntBuffer data) { - ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView32(data)); - } - - public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) { - ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data)); - } - - public static final void _wglBindVertexArray(IBufferArrayGL obj) { - ctx.bindVertexArray(obj == null ? null : ((OpenGLObjects.BufferArrayGL)obj).ptr); - } - - public static final void _wglEnableVertexAttribArray(int index) { - ctx.enableVertexAttribArray(index); - } - - public static final void _wglDisableVertexAttribArray(int index) { - ctx.disableVertexAttribArray(index); - } - - public static final void _wglVertexAttribPointer(int index, int size, int type, - boolean normalized, int stride, int offset) { - ctx.vertexAttribPointer(index, size, type, normalized, stride, offset); - } - - public static final void _wglVertexAttribDivisor(int index, int divisor) { - ctx.vertexAttribDivisor(index, divisor); - } - - public static final void _wglActiveTexture(int texture) { - ctx.activeTexture(texture); - } - - public static final void _wglBindTexture(int target, ITextureGL obj) { - ctx.bindTexture(target, obj == null ? null : ((OpenGLObjects.TextureGL)obj).ptr); - } - - public static final void _wglTexParameterf(int target, int param, float value) { - ctx.texParameterf(target, param, value); - } - - public static final void _wglTexParameteri(int target, int param, int value) { - ctx.texParameteri(target, param, value); - } - - public static final void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, - int border, int format, int type, ByteBuffer data) { - ctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, - int height, int border, int format, int type, ByteBuffer data) { - ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglTexImage2Du16(int target, int level, int internalFormat, int width, - int height, int border, int format, int type, ByteBuffer data) { - ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView16Unsigned(data)); - } - - public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, - int height, int border, int format, int type, ByteBuffer data) { - ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data)); - } - - public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, - int height, int border, int format, int type, IntBuffer data) { - ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, - int height, int border, int format, int type, FloatBuffer data) { - ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, - int width, int height, int format, int type, ByteBuffer data) { - ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset, - int width, int height, int format, int type, ByteBuffer data) { - ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView16Unsigned(data)); - } - - public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, - int width, int height, int format, int type, IntBuffer data) { - ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, - int width, int height, int format, int type, FloatBuffer data) { - ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); - } - - public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, - int x, int y, int width, int height) { - ctx.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); - } - - public static final void _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h) { - ctx.texStorage2D(target, levels, internalFormat, w, h); - } - - public static final void _wglPixelStorei(int pname, int value) { - ctx.pixelStorei(pname, value); - } - - public static final void _wglGenerateMipmap(int target) { - ctx.generateMipmap(target); - } - - public static final void _wglShaderSource(IShaderGL obj, String source) { - ctx.shaderSource(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr, source); - } - - public static final void _wglCompileShader(IShaderGL obj) { - ctx.compileShader(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr); - } - - public static final int _wglGetShaderi(IShaderGL obj, int param) { - return ctx.getShaderParameteri(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr, param); - } - - public static final String _wglGetShaderInfoLog(IShaderGL obj) { - return ctx.getShaderInfoLog(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr); - } - - public static final void _wglUseProgram(IProgramGL obj) { - ctx.useProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); - } - - public static final void _wglAttachShader(IProgramGL obj, IShaderGL shader) { - ctx.attachShader(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, - shader == null ? null : ((OpenGLObjects.ShaderGL)shader).ptr); - } - - public static final void _wglDetachShader(IProgramGL obj, IShaderGL shader) { - ctx.detachShader(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, - shader == null ? null : ((OpenGLObjects.ShaderGL)shader).ptr); - } - - public static final void _wglLinkProgram(IProgramGL obj) { - ctx.linkProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); - } - - public static final int _wglGetProgrami(IProgramGL obj, int param) { - return ctx.getProgramParameteri(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, param); - } - - public static final String _wglGetProgramInfoLog(IProgramGL obj) { - return ctx.getProgramInfoLog(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); - } - - public static final void _wglBindAttribLocation(IProgramGL obj, int index, String name) { - ctx.bindAttribLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, index, name); - } - - public static final int _wglGetAttribLocation(IProgramGL obj, String name) { - return ctx.getAttribLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, name); - } - - public static final void _wglDrawArrays(int mode, int first, int count) { - ctx.drawArrays(mode, first, count); - //checkErr("_wglDrawArrays(" + mode + ", " + first + ", " + count + ");"); - } - - public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) { - ctx.drawArraysInstanced(mode, first, count, instanced); - //checkErr("_wglDrawArraysInstanced(" + mode + ", " + first + ", " + count + ", " + instanced + ");"); - } - - public static final void _wglDrawElements(int mode, int count, int type, int offset) { - ctx.drawElements(mode, count, type, offset); - //checkErr("_wglDrawElements(" + mode + ", " + count + ", " + type + ", " + offset + ");"); - } - - public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) { - ctx.drawElementsInstanced(mode, count, type, offset, instanced); - //checkErr("_wglDrawElementsInstanced(" + mode + ", " + count + ", " + type + ", " + offset + ", " + instanced + ");"); - } - - public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) { - WebGLUniformLocation loc = ctx.getUniformLocation(((OpenGLObjects.ProgramGL)obj).ptr, name); - if(loc != null) { - return new OpenGLObjects.UniformGL(loc); - }else { - return null; - } - } - - public static final int _wglGetUniformBlockIndex(IProgramGL obj, String name) { - int i = ctx.getUniformBlockIndex(((OpenGLObjects.ProgramGL)obj).ptr, name); - if(i > 2147483647) { - i = -1; - } - return i; - } - - public static final void _wglBindBufferRange(int target, int index, IBufferGL buffer, int offset, int size) { - ctx.bindBufferRange(target, index, ((OpenGLObjects.BufferGL)buffer).ptr, offset, size); - } - - public static final void _wglUniformBlockBinding(IProgramGL obj, int blockIndex, int bufferIndex) { - ctx.uniformBlockBinding(((OpenGLObjects.ProgramGL)obj).ptr, blockIndex, bufferIndex); - } - - public static final void _wglUniform1f(IUniformGL obj, float x) { - if(obj != null) ctx.uniform1f(((OpenGLObjects.UniformGL)obj).ptr, x); - } - - public static final void _wglUniform2f(IUniformGL obj, float x, float y) { - if(obj != null) ctx.uniform2f(((OpenGLObjects.UniformGL)obj).ptr, x, y); - } - - public static final void _wglUniform3f(IUniformGL obj, float x, float y, float z) { - if(obj != null) ctx.uniform3f(((OpenGLObjects.UniformGL)obj).ptr, x, y, z); - } - - public static final void _wglUniform4f(IUniformGL obj, float x, float y, float z, float w) { - if(obj != null) ctx.uniform4f(((OpenGLObjects.UniformGL)obj).ptr, x, y, z, w); - } - - public static final void _wglUniform1i(IUniformGL obj, int x) { - if(obj != null) ctx.uniform1i(((OpenGLObjects.UniformGL)obj).ptr, x); - } - - public static final void _wglUniform2i(IUniformGL obj, int x, int y) { - if(obj != null) ctx.uniform2i(((OpenGLObjects.UniformGL)obj).ptr, x, y); - } - - public static final void _wglUniform3i(IUniformGL obj, int x, int y, int z) { - if(obj != null) ctx.uniform3i(((OpenGLObjects.UniformGL)obj).ptr, x, y, z); - } - - public static final void _wglUniform4i(IUniformGL obj, int x, int y, int z, int w) { - if(obj != null) ctx.uniform4i(((OpenGLObjects.UniformGL)obj).ptr, x, y, z, w); - } - - public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if(obj != null) ctx.uniformMatrix2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); - } - - public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if(obj != null) ctx.uniformMatrix3fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); - } - - public static final void _wglUniformMatrix3x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if(obj != null) ctx.uniformMatrix3x2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); - } - - public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if(obj != null) ctx.uniformMatrix4fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); - } - - public static final void _wglUniformMatrix4x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if(obj != null) ctx.uniformMatrix4x2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); - } - - public static final void _wglUniformMatrix4x3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { - if(obj != null) ctx.uniformMatrix4x3fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); - } - - public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) { - if(framebuffer == null) { - ctx.bindFramebuffer(target, PlatformRuntime.mainFramebuffer); - ctx.drawBuffers(new int[] { WebGL2RenderingContext.COLOR_ATTACHMENT0 }); - }else { - ctx.bindFramebuffer(target, ((OpenGLObjects.FramebufferGL) framebuffer).ptr); - } - } - - public static final int _wglCheckFramebufferStatus(int target) { - return ctx.checkFramebufferStatus(target); - } - - public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget, - ITextureGL texture, int level) { - ctx.framebufferTexture2D(target, attachment, texTarget, - texture == null ? null : ((OpenGLObjects.TextureGL)texture).ptr, level); - } - - public static final void _wglFramebufferTextureLayer(int target, int attachment, ITextureGL texture, int level, int layer) { - ctx.framebufferTextureLayer(target, attachment, - texture == null ? null : ((OpenGLObjects.TextureGL) texture).ptr, level, layer); - } - - public static final void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, - int dstX0, int dstY0, int dstX1, int dstY1, int bits, int filter) { - ctx.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter); - } - - public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) { - ctx.bindRenderbuffer(target, - renderbuffer == null ? null : ((OpenGLObjects.RenderbufferGL)renderbuffer).ptr); - } - - public static final void _wglRenderbufferStorage(int target, int internalformat, - int width, int height) { - ctx.renderbufferStorage(target, internalformat, width, height); - } - - public static final void _wglFramebufferRenderbuffer(int target, int attachment, - int renderbufferTarget, IRenderbufferGL renderbuffer) { - ctx.framebufferRenderbuffer(target, attachment, renderbufferTarget, - ((OpenGLObjects.RenderbufferGL)renderbuffer).ptr); - } - - public static final String _wglGetString(int param) { - if(hasDebugRenderInfoExt) { - String s; - switch(param) { - case 0x1f00: // VENDOR - s = ctx.getParameterString(0x9245); // UNMASKED_VENDOR_WEBGL - if(s == null) { - s = ctx.getParameterString(0x1f00); // VENDOR - } - return s; - case 0x1f01: // RENDERER - s = ctx.getParameterString(0x9246); // UNMASKED_RENDERER_WEBGL - if(s == null) { - s = ctx.getParameterString(0x1f01); // RENDERER - } - return s; - default: - return ctx.getParameterString(param); - } - }else { - return ctx.getParameterString(param); - } - } - - public static final int _wglGetInteger(int param) { - return ctx.getParameteri(param); - } - - public static final int _wglGetError() { - return ctx.getError(); - } - - public static final boolean checkHDRFramebufferSupport(int bits) { - switch(bits) { - case 16: - return hasFramebufferHDR16FSupport; - case 32: - return hasFramebufferHDR32FSupport; - default: - return false; - } - } - - public static final boolean checkLinearHDR32FSupport() { - return hasLinearHDR32FSupport; - } - - private static final void checkErr(String name) { - int i = ctx.getError(); - if(i != 0) { - logger.error("########## GL ERROR ##########"); - logger.error("@ {}", name); - do { - logger.error("#{} - {}", i, EaglercraftGPU.gluErrorString(i)); - }while((i = ctx.getError()) != 0); - try { - throw new RuntimeException("GL Error Detected!"); - }catch(Throwable t) { - logger.log(Level.ERROR, t); - } - logger.error("##############################"); - } - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.teavm.jso.webgl.WebGLUniformLocation; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLANGLEInstancedArrays; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLBackBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLOESVertexArrayObject; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLVertexArray; +import net.lax1dude.eaglercraft.v1_8.log4j.Level; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformOpenGL { + + private static final Logger logger = LogManager.getLogger("PlatformOpenGL"); + + static WebGL2RenderingContext ctx = null; + static int glesVers = -1; + + static boolean hasANGLEInstancedArrays = false; + static boolean hasEXTColorBufferFloat = false; + static boolean hasEXTColorBufferHalfFloat = false; + static boolean hasEXTShaderTextureLOD = false; + static boolean hasOESFBORenderMipmap = false; + static boolean hasOESVertexArrayObject = false; + static boolean hasOESTextureFloat = false; + static boolean hasOESTextureFloatLinear = false; + static boolean hasOESTextureHalfFloat = false; + static boolean hasOESTextureHalfFloatLinear = false; + static boolean hasEXTTextureFilterAnisotropic = false; + static boolean hasWEBGLDebugRendererInfo = false; + + static WebGLANGLEInstancedArrays ANGLEInstancedArrays = null; + static WebGLOESVertexArrayObject OESVertexArrayObject = null; + + static boolean hasFBO16FSupport = false; + static boolean hasFBO32FSupport = false; + static boolean hasLinearHDR16FSupport = false; + static boolean hasLinearHDR32FSupport = false; + + static final int VAO_IMPL_NONE = -1; + static final int VAO_IMPL_CORE = 0; + static final int VAO_IMPL_OES = 1; + static int vertexArrayImpl = VAO_IMPL_NONE; + + static final int INSTANCE_IMPL_NONE = -1; + static final int INSTANCE_IMPL_CORE = 0; + static final int INSTANCE_IMPL_ANGLE = 1; + static int instancingImpl = INSTANCE_IMPL_NONE; + + static void setCurrentContext(int glesVersIn, WebGL2RenderingContext context) { + ctx = context; + if(ctx != null) { + glesVers = glesVersIn; + boolean allowExts = ((TeaVMClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isUseWebGLExtTeaVM(); + if(allowExts) { + ANGLEInstancedArrays = glesVersIn == 200 ? (WebGLANGLEInstancedArrays) ctx.getExtension("ANGLE_instanced_arrays") : null; + hasANGLEInstancedArrays = glesVersIn == 200 && ANGLEInstancedArrays != null; + hasEXTColorBufferFloat = (glesVersIn == 310 || glesVersIn == 300) && ctx.getExtension("EXT_color_buffer_float") != null; + hasEXTColorBufferHalfFloat = !hasEXTColorBufferFloat + && (glesVersIn == 310 || glesVersIn == 300 || glesVersIn == 200) && ctx.getExtension("EXT_color_buffer_half_float") != null; + hasEXTShaderTextureLOD = glesVersIn == 200 && ctx.getExtension("EXT_shader_texture_lod") != null; + hasOESFBORenderMipmap = glesVersIn == 200 && ctx.getExtension("OES_fbo_render_mipmap") != null; + OESVertexArrayObject = glesVersIn == 200 ? (WebGLOESVertexArrayObject) ctx.getExtension("OES_vertex_array_object") : null; + hasOESVertexArrayObject = glesVersIn == 200 && OESVertexArrayObject != null; + hasOESTextureFloat = glesVersIn == 200 && ctx.getExtension("OES_texture_float") != null; + hasOESTextureFloatLinear = glesVersIn >= 300 && ctx.getExtension("OES_texture_float_linear") != null; + hasOESTextureHalfFloat = glesVersIn == 200 && ctx.getExtension("OES_texture_half_float") != null; + hasOESTextureHalfFloatLinear = glesVersIn == 200 && ctx.getExtension("OES_texture_half_float_linear") != null; + hasEXTTextureFilterAnisotropic = ctx.getExtension("EXT_texture_filter_anisotropic") != null; + }else { + hasANGLEInstancedArrays = false; + hasEXTColorBufferFloat = false; + hasEXTColorBufferHalfFloat = false; + hasEXTShaderTextureLOD = false; + hasOESFBORenderMipmap = false; + hasOESVertexArrayObject = false; + hasOESTextureFloat = false; + hasOESTextureFloatLinear = false; + hasOESTextureHalfFloat = false; + hasOESTextureHalfFloatLinear = false; + hasEXTTextureFilterAnisotropic = false; + } + hasWEBGLDebugRendererInfo = ctx.getExtension("WEBGL_debug_renderer_info") != null; + + hasFBO16FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureFloat) && (hasEXTColorBufferFloat || hasEXTColorBufferHalfFloat)); + hasFBO32FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureHalfFloat) && hasEXTColorBufferFloat); + hasLinearHDR16FSupport = glesVersIn >= 300 || hasOESTextureHalfFloatLinear; + hasLinearHDR32FSupport = glesVersIn >= 300 && hasOESTextureFloatLinear; + + if(glesVersIn >= 300) { + vertexArrayImpl = VAO_IMPL_CORE; + instancingImpl = INSTANCE_IMPL_CORE; + }else if(glesVersIn == 200) { + vertexArrayImpl = hasOESVertexArrayObject ? VAO_IMPL_OES : VAO_IMPL_NONE; + instancingImpl = hasANGLEInstancedArrays ? INSTANCE_IMPL_ANGLE : INSTANCE_IMPL_NONE; + }else { + vertexArrayImpl = VAO_IMPL_NONE; + instancingImpl = INSTANCE_IMPL_NONE; + } + + _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); + }else { + glesVers = -1; + hasANGLEInstancedArrays = false; + hasEXTColorBufferFloat = false; + hasEXTColorBufferHalfFloat = false; + hasEXTShaderTextureLOD = false; + hasOESFBORenderMipmap = false; + hasOESVertexArrayObject = false; + hasOESTextureFloat = false; + hasOESTextureFloatLinear = false; + hasOESTextureHalfFloat = false; + hasOESTextureHalfFloatLinear = false; + hasEXTTextureFilterAnisotropic = false; + hasWEBGLDebugRendererInfo = false; + ANGLEInstancedArrays = null; + OESVertexArrayObject = null; + hasFBO16FSupport = false; + hasFBO32FSupport = false; + hasLinearHDR16FSupport = false; + hasLinearHDR32FSupport = false; + } + } + + public static final List dumpActiveExtensions() { + List exts = new ArrayList<>(); + if(hasANGLEInstancedArrays) exts.add("ANGLE_instanced_arrays"); + if(hasEXTColorBufferFloat) exts.add("EXT_color_buffer_float"); + if(hasEXTColorBufferHalfFloat) exts.add("EXT_color_buffer_half_float"); + if(hasEXTShaderTextureLOD) exts.add("EXT_shader_texture_lod"); + if(hasOESFBORenderMipmap) exts.add("OES_fbo_render_mipmap"); + if(hasOESVertexArrayObject) exts.add("OES_vertex_array_object"); + if(hasOESTextureFloat) exts.add("OES_texture_float"); + if(hasOESTextureFloatLinear) exts.add("OES_texture_float_linear"); + if(hasOESTextureHalfFloat) exts.add("OES_texture_half_float"); + if(hasOESTextureHalfFloatLinear) exts.add("OES_texture_half_float_linear"); + if(hasEXTTextureFilterAnisotropic) exts.add("EXT_texture_filter_anisotropic"); + if(hasWEBGLDebugRendererInfo) exts.add("WEBGL_debug_renderer_info"); + return exts; + } + + public static final void _wglEnable(int glEnum) { + ctx.enable(glEnum); + } + + public static final void _wglDisable(int glEnum) { + ctx.disable(glEnum); + } + + public static final void _wglClearColor(float r, float g, float b, float a) { + ctx.clearColor(r, g, b, a); + } + + public static final void _wglClearDepth(float f) { + ctx.clearDepth(f); + } + + public static final void _wglClear(int bits) { + ctx.clear(bits); + } + + public static final void _wglDepthFunc(int glEnum) { + ctx.depthFunc(glEnum); + } + + public static final void _wglDepthMask(boolean mask) { + ctx.depthMask(mask); + } + + public static final void _wglCullFace(int glEnum) { + ctx.cullFace(glEnum); + } + + public static final void _wglViewport(int x, int y, int w, int h) { + ctx.viewport(x, y, w, h); + } + + public static final void _wglBlendFunc(int src, int dst) { + ctx.blendFunc(src, dst); + } + + public static final void _wglBlendFuncSeparate(int srcColor, int dstColor, + int srcAlpha, int dstAlpha) { + ctx.blendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha); + } + + public static final void _wglBlendEquation(int glEnum) { + ctx.blendEquation(glEnum); + } + + public static final void _wglBlendColor(float r, float g, float b, float a) { + ctx.blendColor(r, g, b, a); + } + + public static final void _wglColorMask(boolean r, boolean g, boolean b, boolean a) { + ctx.colorMask(r, g, b, a); + } + + public static final void _wglDrawBuffers(int buffer) { + if(glesVers == 200) { + if(buffer != 0x8CE0) { // GL_COLOR_ATTACHMENT0 + throw new UnsupportedOperationException(); + } + }else { + ctx.drawBuffers(new int[] { buffer }); + } + } + + public static final void _wglDrawBuffers(int[] buffers) { + if(glesVers == 200) { + if(buffers.length != 1 || buffers[0] != 0x8CE0) { // GL_COLOR_ATTACHMENT0 + throw new UnsupportedOperationException(); + } + }else { + ctx.drawBuffers(buffers); + } + } + + public static final void _wglReadBuffer(int buffer) { + ctx.readBuffer(buffer); + } + + public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) { + ctx.readPixels(x, y, width, height, format, type, EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglReadPixels_u16(int x, int y, int width, int height, int format, int type, ByteBuffer data) { + ctx.readPixels(x, y, width, height, format, type, EaglerArrayBufferAllocator.getDataView16Unsigned(data)); + } + + public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer data) { + ctx.readPixels(x, y, width, height, format, type, EaglerArrayBufferAllocator.getDataView32(data)); + } + + public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer data) { + ctx.readPixels(x, y, width, height, format, type, EaglerArrayBufferAllocator.getDataView32F(data)); + } + + public static final void _wglPolygonOffset(float f1, float f2) { + ctx.polygonOffset(f1, f2); + } + + public static final void _wglLineWidth(float width) { + ctx.lineWidth(width); + } + + public static final IBufferGL _wglGenBuffers() { + return new OpenGLObjects.BufferGL(ctx.createBuffer()); + } + + public static final ITextureGL _wglGenTextures() { + return new OpenGLObjects.TextureGL(ctx.createTexture()); + } + + public static final IBufferArrayGL _wglGenVertexArrays() { + switch(vertexArrayImpl) { + case VAO_IMPL_CORE: + return new OpenGLObjects.BufferArrayGL(ctx.createVertexArray()); + case VAO_IMPL_OES: + return new OpenGLObjects.BufferArrayGL(OESVertexArrayObject.createVertexArrayOES()); + default: + throw new UnsupportedOperationException(); + } + } + + public static final IProgramGL _wglCreateProgram() { + return new OpenGLObjects.ProgramGL(ctx.createProgram()); + } + + public static final IShaderGL _wglCreateShader(int type) { + return new OpenGLObjects.ShaderGL(ctx.createShader(type)); + } + + public static final IFramebufferGL _wglCreateFramebuffer() { + return new OpenGLObjects.FramebufferGL(ctx.createFramebuffer()); + } + + public static final IRenderbufferGL _wglCreateRenderbuffer() { + return new OpenGLObjects.RenderbufferGL(ctx.createRenderbuffer()); + } + + public static final IQueryGL _wglGenQueries() { + return new OpenGLObjects.QueryGL(ctx.createQuery()); + } + + public static final void _wglDeleteBuffers(IBufferGL obj) { + ctx.deleteBuffer(((OpenGLObjects.BufferGL)obj).ptr); + } + + public static final void _wglDeleteTextures(ITextureGL obj) { + ctx.deleteTexture(((OpenGLObjects.TextureGL)obj).ptr); + } + + public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) { + WebGLVertexArray ptr = ((OpenGLObjects.BufferArrayGL)obj).ptr; + switch(vertexArrayImpl) { + case VAO_IMPL_CORE: + ctx.deleteVertexArray(ptr); + break; + case VAO_IMPL_OES: + OESVertexArrayObject.deleteVertexArrayOES(ptr); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglDeleteProgram(IProgramGL obj) { + ctx.deleteProgram(((OpenGLObjects.ProgramGL)obj).ptr); + } + + public static final void _wglDeleteShader(IShaderGL obj) { + ctx.deleteShader(((OpenGLObjects.ShaderGL)obj).ptr); + } + + public static final void _wglDeleteFramebuffer(IFramebufferGL obj) { + ctx.deleteFramebuffer(((OpenGLObjects.FramebufferGL)obj).ptr); + } + + public static final void _wglDeleteRenderbuffer(IRenderbufferGL obj) { + ctx.deleteRenderbuffer(((OpenGLObjects.RenderbufferGL)obj).ptr); + } + + public static final void _wglDeleteQueries(IQueryGL obj) { + ctx.deleteQuery(((OpenGLObjects.QueryGL)obj).ptr); + } + + public static final void _wglBindBuffer(int target, IBufferGL obj) { + ctx.bindBuffer(target, obj != null ? ((OpenGLObjects.BufferGL)obj).ptr : null); + } + + public static final void _wglBufferData(int target, ByteBuffer data, int usage) { + ctx.bufferData(target, EaglerArrayBufferAllocator.getDataView8(data), usage); + } + + public static final void _wglBufferData(int target, IntBuffer data, int usage) { + ctx.bufferData(target, EaglerArrayBufferAllocator.getDataView32(data), usage); + } + + public static final void _wglBufferData(int target, FloatBuffer data, int usage) { + ctx.bufferData(target, EaglerArrayBufferAllocator.getDataView32F(data), usage); + } + + public static final void _wglBufferData(int target, int size, int usage) { + ctx.bufferData(target, size, usage); + } + + public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) { + ctx.bufferSubData(target, offset, EaglerArrayBufferAllocator.getDataView8(data)); + } + + public static final void _wglBufferSubData(int target, int offset, IntBuffer data) { + ctx.bufferSubData(target, offset, EaglerArrayBufferAllocator.getDataView32(data)); + } + + public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) { + ctx.bufferSubData(target, offset, EaglerArrayBufferAllocator.getDataView32F(data)); + } + + public static final void _wglBindVertexArray(IBufferArrayGL obj) { + WebGLVertexArray ptr = obj != null ? ((OpenGLObjects.BufferArrayGL)obj).ptr : null; + switch(vertexArrayImpl) { + case VAO_IMPL_CORE: + ctx.bindVertexArray(ptr); + break; + case VAO_IMPL_OES: + OESVertexArrayObject.bindVertexArrayOES(ptr); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglEnableVertexAttribArray(int index) { + ctx.enableVertexAttribArray(index); + } + + public static final void _wglDisableVertexAttribArray(int index) { + ctx.disableVertexAttribArray(index); + } + + public static final void _wglVertexAttribPointer(int index, int size, int type, + boolean normalized, int stride, int offset) { + ctx.vertexAttribPointer(index, size, type, normalized, stride, offset); + } + + public static final void _wglVertexAttribDivisor(int index, int divisor) { + switch(instancingImpl) { + case INSTANCE_IMPL_CORE: + ctx.vertexAttribDivisor(index, divisor); + break; + case INSTANCE_IMPL_ANGLE: + ANGLEInstancedArrays.vertexAttribDivisorANGLE(index, divisor); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static final void _wglActiveTexture(int texture) { + ctx.activeTexture(texture); + } + + public static final void _wglBindTexture(int target, ITextureGL obj) { + ctx.bindTexture(target, obj == null ? null : ((OpenGLObjects.TextureGL)obj).ptr); + } + + public static final void _wglTexParameterf(int target, int param, float value) { + ctx.texParameterf(target, param, value); + } + + public static final void _wglTexParameteri(int target, int param, int value) { + ctx.texParameteri(target, param, value); + } + + public static final void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, + int border, int format, int type, ByteBuffer data) { + ctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, ByteBuffer data) { + ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglTexImage2Du16(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, ByteBuffer data) { + ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView16Unsigned(data)); + } + + public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, ByteBuffer data) { + ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, IntBuffer data) { + ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, FloatBuffer data) { + ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, + int width, int height, int format, int type, ByteBuffer data) { + ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset, + int width, int height, int format, int type, ByteBuffer data) { + ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView16Unsigned(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, + int width, int height, int format, int type, IntBuffer data) { + ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, + int width, int height, int format, int type, FloatBuffer data) { + ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); + } + + public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, + int x, int y, int width, int height) { + ctx.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } + + public static final void _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h) { + ctx.texStorage2D(target, levels, internalFormat, w, h); + } + + public static final void _wglPixelStorei(int pname, int value) { + ctx.pixelStorei(pname, value); + } + + public static final void _wglGenerateMipmap(int target) { + ctx.generateMipmap(target); + } + + public static final void _wglShaderSource(IShaderGL obj, String source) { + ctx.shaderSource(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr, source); + } + + public static final void _wglCompileShader(IShaderGL obj) { + ctx.compileShader(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr); + } + + public static final int _wglGetShaderi(IShaderGL obj, int param) { + return ctx.getShaderParameteri(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr, param); + } + + public static final String _wglGetShaderInfoLog(IShaderGL obj) { + return ctx.getShaderInfoLog(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr); + } + + public static final void _wglUseProgram(IProgramGL obj) { + ctx.useProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); + } + + public static final void _wglAttachShader(IProgramGL obj, IShaderGL shader) { + ctx.attachShader(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, + shader == null ? null : ((OpenGLObjects.ShaderGL)shader).ptr); + } + + public static final void _wglDetachShader(IProgramGL obj, IShaderGL shader) { + ctx.detachShader(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, + shader == null ? null : ((OpenGLObjects.ShaderGL)shader).ptr); + } + + public static final void _wglLinkProgram(IProgramGL obj) { + ctx.linkProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); + } + + public static final int _wglGetProgrami(IProgramGL obj, int param) { + return ctx.getProgramParameteri(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, param); + } + + public static final String _wglGetProgramInfoLog(IProgramGL obj) { + return ctx.getProgramInfoLog(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr); + } + + public static final void _wglBindAttribLocation(IProgramGL obj, int index, String name) { + ctx.bindAttribLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, index, name); + } + + public static final int _wglGetAttribLocation(IProgramGL obj, String name) { + return ctx.getAttribLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, name); + } + + public static final void _wglDrawArrays(int mode, int first, int count) { + ctx.drawArrays(mode, first, count); + //checkErr("_wglDrawArrays(" + mode + ", " + first + ", " + count + ");"); + } + + public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) { + switch(instancingImpl) { + case INSTANCE_IMPL_CORE: + ctx.drawArraysInstanced(mode, first, count, instanced); + break; + case INSTANCE_IMPL_ANGLE: + ANGLEInstancedArrays.drawArraysInstancedANGLE(mode, first, count, instanced); + break; + default: + throw new UnsupportedOperationException(); + } + //checkErr("_wglDrawArraysInstanced(" + mode + ", " + first + ", " + count + ", " + instanced + ");"); + } + + public static final void _wglDrawElements(int mode, int count, int type, int offset) { + ctx.drawElements(mode, count, type, offset); + //checkErr("_wglDrawElements(" + mode + ", " + count + ", " + type + ", " + offset + ");"); + } + + public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) { + switch(instancingImpl) { + case INSTANCE_IMPL_CORE: + ctx.drawElementsInstanced(mode, count, type, offset, instanced); + break; + case INSTANCE_IMPL_ANGLE: + ANGLEInstancedArrays.drawElementsInstancedANGLE(mode, count, type, offset, instanced); + break; + default: + throw new UnsupportedOperationException(); + } + //checkErr("_wglDrawElementsInstanced(" + mode + ", " + count + ", " + type + ", " + offset + ", " + instanced + ");"); + } + + public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) { + WebGLUniformLocation loc = ctx.getUniformLocation(((OpenGLObjects.ProgramGL)obj).ptr, name); + if(loc != null) { + return new OpenGLObjects.UniformGL(loc); + }else { + return null; + } + } + + public static final int _wglGetUniformBlockIndex(IProgramGL obj, String name) { + int i = ctx.getUniformBlockIndex(((OpenGLObjects.ProgramGL)obj).ptr, name); + if(i > 2147483647) { + i = -1; + } + return i; + } + + public static final void _wglBindBufferRange(int target, int index, IBufferGL buffer, int offset, int size) { + ctx.bindBufferRange(target, index, ((OpenGLObjects.BufferGL)buffer).ptr, offset, size); + } + + public static final void _wglUniformBlockBinding(IProgramGL obj, int blockIndex, int bufferIndex) { + ctx.uniformBlockBinding(((OpenGLObjects.ProgramGL)obj).ptr, blockIndex, bufferIndex); + } + + public static final void _wglUniform1f(IUniformGL obj, float x) { + if(obj != null) ctx.uniform1f(((OpenGLObjects.UniformGL)obj).ptr, x); + } + + public static final void _wglUniform2f(IUniformGL obj, float x, float y) { + if(obj != null) ctx.uniform2f(((OpenGLObjects.UniformGL)obj).ptr, x, y); + } + + public static final void _wglUniform3f(IUniformGL obj, float x, float y, float z) { + if(obj != null) ctx.uniform3f(((OpenGLObjects.UniformGL)obj).ptr, x, y, z); + } + + public static final void _wglUniform4f(IUniformGL obj, float x, float y, float z, float w) { + if(obj != null) ctx.uniform4f(((OpenGLObjects.UniformGL)obj).ptr, x, y, z, w); + } + + public static final void _wglUniform1i(IUniformGL obj, int x) { + if(obj != null) ctx.uniform1i(((OpenGLObjects.UniformGL)obj).ptr, x); + } + + public static final void _wglUniform2i(IUniformGL obj, int x, int y) { + if(obj != null) ctx.uniform2i(((OpenGLObjects.UniformGL)obj).ptr, x, y); + } + + public static final void _wglUniform3i(IUniformGL obj, int x, int y, int z) { + if(obj != null) ctx.uniform3i(((OpenGLObjects.UniformGL)obj).ptr, x, y, z); + } + + public static final void _wglUniform4i(IUniformGL obj, int x, int y, int z, int w) { + if(obj != null) ctx.uniform4i(((OpenGLObjects.UniformGL)obj).ptr, x, y, z, w); + } + + public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if(obj != null) ctx.uniformMatrix2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); + } + + public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if(obj != null) ctx.uniformMatrix3fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); + } + + public static final void _wglUniformMatrix3x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if(obj != null) ctx.uniformMatrix3x2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); + } + + public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if(obj != null) ctx.uniformMatrix4fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); + } + + public static final void _wglUniformMatrix4x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if(obj != null) ctx.uniformMatrix4x2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); + } + + public static final void _wglUniformMatrix4x3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if(obj != null) ctx.uniformMatrix4x3fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); + } + + public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) { + if(framebuffer == null) { + ctx.bindFramebuffer(target, PlatformRuntime.mainFramebuffer); + if(glesVers != 200) { + ctx.drawBuffers(new int[] { WebGL2RenderingContext.COLOR_ATTACHMENT0 }); + } + }else { + ctx.bindFramebuffer(target, ((OpenGLObjects.FramebufferGL) framebuffer).ptr); + } + } + + public static final int _wglCheckFramebufferStatus(int target) { + return ctx.checkFramebufferStatus(target); + } + + public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget, + ITextureGL texture, int level) { + ctx.framebufferTexture2D(target, attachment, texTarget, + texture == null ? null : ((OpenGLObjects.TextureGL)texture).ptr, level); + } + + public static final void _wglFramebufferTextureLayer(int target, int attachment, ITextureGL texture, int level, int layer) { + ctx.framebufferTextureLayer(target, attachment, + texture == null ? null : ((OpenGLObjects.TextureGL) texture).ptr, level, layer); + } + + public static final void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, + int dstX0, int dstY0, int dstX1, int dstY1, int bits, int filter) { + ctx.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter); + } + + public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) { + ctx.bindRenderbuffer(target, + renderbuffer == null ? null : ((OpenGLObjects.RenderbufferGL)renderbuffer).ptr); + } + + public static final void _wglRenderbufferStorage(int target, int internalformat, + int width, int height) { + ctx.renderbufferStorage(target, internalformat, width, height); + } + + public static final void _wglFramebufferRenderbuffer(int target, int attachment, + int renderbufferTarget, IRenderbufferGL renderbuffer) { + ctx.framebufferRenderbuffer(target, attachment, renderbufferTarget, + ((OpenGLObjects.RenderbufferGL)renderbuffer).ptr); + } + + public static final String _wglGetString(int param) { + if(hasWEBGLDebugRendererInfo) { + String s; + switch(param) { + case 0x1f00: // VENDOR + s = ctx.getParameterString(0x9245); // UNMASKED_VENDOR_WEBGL + if(s == null) { + s = ctx.getParameterString(0x1f00); // VENDOR + } + return s; + case 0x1f01: // RENDERER + s = ctx.getParameterString(0x9246); // UNMASKED_RENDERER_WEBGL + if(s == null) { + s = ctx.getParameterString(0x1f01); // RENDERER + } + return s; + default: + return ctx.getParameterString(param); + } + }else { + return ctx.getParameterString(param); + } + } + + public static final int _wglGetInteger(int param) { + return ctx.getParameteri(param); + } + + public static final int _wglGetError() { + return ctx.getError(); + } + + public static final int checkOpenGLESVersion() { + return glesVers; + } + + public static final boolean checkEXTGPUShader5Capable() { + return false; + } + + public static final boolean checkOESGPUShader5Capable() { + return false; + } + + public static final boolean checkFBORenderMipmapCapable() { + return glesVers >= 300 || hasOESFBORenderMipmap; + } + + public static final boolean checkVAOCapable() { + return vertexArrayImpl != VAO_IMPL_NONE; + } + + public static final boolean checkInstancingCapable() { + return instancingImpl != INSTANCE_IMPL_NONE; + } + + public static final boolean checkTexStorageCapable() { + return glesVers >= 300; + } + + public static final boolean checkTextureLODCapable() { + return glesVers >= 300 || hasEXTShaderTextureLOD; + } + + public static final boolean checkHDRFramebufferSupport(int bits) { + switch(bits) { + case 16: + return hasFBO16FSupport; + case 32: + return hasFBO32FSupport; + default: + return false; + } + } + + public static final boolean checkLinearHDRFilteringSupport(int bits) { + switch(bits) { + case 16: + return hasLinearHDR16FSupport; + case 32: + return hasLinearHDR32FSupport; + default: + return false; + } + } + + // legacy + public static final boolean checkLinearHDR32FSupport() { + return hasLinearHDR32FSupport; + } + + public static final boolean checkAnisotropicFilteringSupport() { + return hasEXTTextureFilterAnisotropic; + } + + public static final boolean checkNPOTCapable() { + return glesVers >= 300; + } + + private static final void checkErr(String name) { + int i = ctx.getError(); + if(i != 0) { + logger.error("########## GL ERROR ##########"); + logger.error("@ {}", name); + do { + logger.error("#{} - {}", i, EaglercraftGPU.gluErrorString(i)); + }while((i = ctx.getError()) != 0); + try { + throw new RuntimeException("GL Error Detected!"); + }catch(Throwable t) { + logger.log(Level.ERROR, t); + } + logger.error("##############################"); + } + } + + public static final String[] getAllExtensions() { + return ctx.getSupportedExtensionArray(); + } + + public static final void enterVAOEmulationHook() { + WebGLBackBuffer.enterVAOEmulationPhase(); + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 2dfd5ab..035174b 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -3,61 +3,82 @@ package net.lax1dude.eaglercraft.v1_8.internal; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.function.Consumer; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.Filesystem; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint; import org.teavm.interop.Async; import org.teavm.interop.AsyncCallback; import org.teavm.jso.JSBody; import org.teavm.jso.JSExceptions; -import org.teavm.jso.JSFunctor; import org.teavm.jso.JSObject; -import org.teavm.jso.ajax.XMLHttpRequest; +import org.teavm.jso.JSProperty; import org.teavm.jso.browser.Window; -import org.teavm.jso.canvas.CanvasRenderingContext2D; -import org.teavm.jso.core.JSError; +import org.teavm.jso.core.JSString; import org.teavm.jso.dom.css.CSSStyleDeclaration; import org.teavm.jso.dom.events.Event; import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.html.HTMLAnchorElement; +import org.teavm.jso.dom.events.MessageEvent; import org.teavm.jso.dom.html.HTMLCanvasElement; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.xml.Element; +import org.teavm.jso.dom.xml.Node; +import org.teavm.jso.dom.xml.NodeList; import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.webaudio.MediaStream; import org.teavm.jso.webgl.WebGLFramebuffer; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterators; +import com.google.common.collect.Sets; import com.jcraft.jzlib.DeflaterOutputStream; import com.jcraft.jzlib.GZIPInputStream; import com.jcraft.jzlib.GZIPOutputStream; import com.jcraft.jzlib.InflaterInputStream; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.FilesystemDatabaseLockedException; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; import net.lax1dude.eaglercraft.v1_8.internal.teavm.EPKLoader; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ES6ShimStatus; import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.EnumES6ShimStatus; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.EnumES6Shims; import net.lax1dude.eaglercraft.v1_8.internal.teavm.FixWebMDurationJS; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ImmediateContinue; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.MessageChannel; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager; import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain.EPKFileEntry; import net.lax1dude.eaglercraft.v1_8.internal.teavm.DebugConsoleWindow; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMDataURLManager; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMEnterBootMenuException; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMFetchJS; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMRuntimeDeobfuscator; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.VisualViewport; import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLBackBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; /** - * Copyright (c) 2022-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND @@ -84,75 +105,334 @@ public class PlatformRuntime { public static Window win = null; public static HTMLDocument doc = null; + public static HTMLElement root = null; public static HTMLElement parent = null; public static HTMLCanvasElement canvas = null; public static WebGL2RenderingContext webgl = null; - + public static boolean webglExperimental = false; + + private static String windowMessagePostOrigin = null; + private static EventListener windowMessageListener = null; + static WebGLFramebuffer mainFramebuffer = null; + static boolean useDelayOnSwap = false; + static boolean immediateContinueSupport = false; + static MessageChannel immediateContinueChannel = null; + static Runnable currentMsgChannelContinueHack = null; + static ImmediateContinue currentLegacyContinueHack = null; + private static final Object immediateContLock = new Object(); + + static boolean hasFetchSupport = false; + static boolean hasDataURLSupport = false; + + static boolean useVisualViewport = false; + + public static boolean isDeobfStackTraces = true; + + private static final JSObject steadyTimeFunc = getSteadyTimeFunc(); + + @JSBody(params = { }, script = "return ((typeof performance !== \"undefined\") && (typeof performance.now === \"function\"))" + + "? performance.now.bind(performance)" + + ": (function(epochStart){ return function() { return Date.now() - epochStart; }; })(Date.now());") + private static native JSObject getSteadyTimeFunc(); + + private static interface WebGLContextEvent extends Event { + @JSProperty + String getStatusMessage(); + } + public static void create() { win = Window.current(); doc = win.getDocument(); DebugConsoleWindow.initialize(win); PlatformApplication.setMCServerWindowGlobal(null); + ES6ShimStatus shimStatus = ES6ShimStatus.getRuntimeStatus(); + if(shimStatus != null) { + EnumES6ShimStatus stat = shimStatus.getStatus(); + switch(stat) { + case STATUS_ERROR: + case STATUS_DISABLED_ERRORS: + logger.error("ES6 Shim Status: {}", stat.statusDesc); + break; + case STATUS_ENABLED_ERRORS: + logger.error("ES6 Shim Status: {}", stat.statusDesc); + dumpShims(shimStatus.getShims()); + break; + case STATUS_DISABLED: + case STATUS_NOT_PRESENT: + logger.info("ES6 Shim Status: {}", stat.statusDesc); + break; + case STATUS_ENABLED: + logger.info("ES6 Shim Status: {}", stat.statusDesc); + dumpShims(shimStatus.getShims()); + break; + default: + break; + } + } + + TeaVMBlobURLManager.initialize(); + logger.info("Creating main game canvas"); - - parent = doc.getElementById(ClientMain.configRootElementId); - if (parent == null) { - throw new RuntimeInitializationFailureException( - "Root element \"" + ClientMain.configRootElementId + "\" was not found in this document!"); + + root = doc.getElementById(ClientMain.configRootElementId); + root.getClassList().add("_eaglercraftX_root_element"); + if(root == null) { + throw new RuntimeInitializationFailureException("Root element \"" + ClientMain.configRootElementId + "\" was not found in this document!"); + } + + Node nodeler; + while((nodeler = root.getLastChild()) != null && TeaVMUtils.isTruthy(nodeler)) { + root.removeChild(nodeler); } - CSSStyleDeclaration style = parent.getStyle(); + CSSStyleDeclaration style = root.getStyle(); style.setProperty("overflowX", "hidden"); style.setProperty("overflowY", "hidden"); - canvas = (HTMLCanvasElement) doc.createElement("canvas"); + TeaVMClientConfigAdapter teavmCfg = (TeaVMClientConfigAdapter) getClientConfigAdapter(); + boolean allowBootMenu = teavmCfg.isAllowBootMenu(); + boolean isEmbeddedInBody = root.getTagName().equalsIgnoreCase("body"); + if (teavmCfg.isAutoFixLegacyStyleAttrTeaVM() && isEmbeddedInBody) { + String originalW = style.getPropertyValue("width"); + String originalH = style.getPropertyValue("height"); + if("100vw".equals(originalW) && "100vh".equals(originalH)) { + logger.info("Note: Retroactively patching style attributes on "); + NodeList nl = doc.getElementsByTagName("html"); + if(nl.getLength() > 0) { + CSSStyleDeclaration htmlDecl = ((HTMLElement)nl.get(0)).getStyle(); + htmlDecl.setProperty("width", "100%"); + htmlDecl.setProperty("height", "100%"); + htmlDecl.setProperty("background-color", "black"); + }else { + logger.warn("Could not find tag!"); + } + style.setProperty("width", "100%"); + style.setProperty("height", "100%"); + style.setProperty("background-color", "black"); + } + HTMLElement viewportTag = doc.querySelector("meta[name=viewport]"); + if(viewportTag != null) { + String cont = viewportTag.getAttribute("content"); + if(cont != null) { + Set oldTokens = Sets.newHashSet(Iterators.transform(Iterators.forArray(cont.split(",")), String::trim)); + Set tokens = new HashSet<>(); + for(String str : oldTokens) { + if (!(str.startsWith("width=") || str.startsWith("initial-scale=") + || str.startsWith("minimum-scale=") || str.startsWith("maximum-scale="))) { + tokens.add(str); + } + } + tokens.add("width=device-width"); + tokens.add("initial-scale=1.0"); + tokens.add("minimum-scale=1.0"); + tokens.add("maximum-scale=1.0"); + if(!tokens.equals(oldTokens)) { + logger.info("Note: Retroactively patching viewport tag"); + viewportTag.setAttribute("content", String.join(", ", tokens)); + } + } + } + } - style = canvas.getStyle(); + useDelayOnSwap = teavmCfg.isUseDelayOnSwapTeaVM(); + + parent = doc.createElement("div"); + parent.getClassList().add("_eaglercraftX_wrapper_element"); + style = parent.getStyle(); + style.setProperty("position", "relative"); style.setProperty("width", "100%"); style.setProperty("height", "100%"); + style.setProperty("overflowX", "hidden"); + style.setProperty("overflowY", "hidden"); + root.appendChild(parent); + ClientMain.configRootElement = parent; // hack + + try { + Thread.sleep(10l); + } catch (InterruptedException e) { + } + + useVisualViewport = false; + if(isVisualViewportSupported(System.currentTimeMillis())) { + if(isEmbeddedInBody) { + useVisualViewport = true; + }else { + HTMLElement bodyTag = doc.getBody(); + if (Math.abs(bodyTag.getClientWidth() - parent.getClientWidth()) <= 10 + && Math.abs(bodyTag.getClientHeight() - parent.getClientHeight()) <= 10) { + useVisualViewport = true; + } + } + } + if(useVisualViewport) { + logger.info("Note: Detected game is embedded in body, some screens may be resized to window.visualViewport instead for a better mobile experience"); + } + + ByteBuffer endiannessTestBytes = allocateByteBuffer(4); + try { + endiannessTestBytes.asIntBuffer().put(0x6969420); + if (((endiannessTestBytes.get(0) & 0xFF) | ((endiannessTestBytes.get(1) & 0xFF) << 8) + | ((endiannessTestBytes.get(2) & 0xFF) << 16) | ((endiannessTestBytes.get(3) & 0xFF) << 24)) != 0x6969420) { + throw new PlatformIncompatibleException("Big endian CPU detected! (somehow)"); + }else { + logger.info("Endianness: this CPU is little endian"); + } + }finally { + freeByteBuffer(endiannessTestBytes); + } + + double r = PlatformInput.getDevicePixelRatio(win); + if(r < 0.01) r = 1.0; + int iw = parent.getClientWidth(); + int ih = parent.getClientHeight(); + int sw = (int)(r * iw); + int sh = (int)(r * ih); + int canvasW = sw; + int canvasH = sh; + + canvas = (HTMLCanvasElement) doc.createElement("canvas"); + + style = canvas.getStyle(); + canvas.getClassList().add("_eaglercraftX_canvas_element"); + style.setProperty("width", "100%"); + style.setProperty("height", "100%"); + style.setProperty("z-index", "1"); style.setProperty("image-rendering", "pixelated"); + style.setProperty("touch-action", "pan-x pan-y"); + style.setProperty("-webkit-touch-callout", "none"); + style.setProperty("-webkit-tap-highlight-color", "rgba(255, 255, 255, 0)"); - double r = win.getDevicePixelRatio(); - int iw = (int) (parent.getClientWidth()); - int ih = (int) (parent.getClientHeight()); - int sw = (int) (r * iw); - int sh = (int) (r * ih); - - canvas.setWidth(sw); - canvas.setHeight(sh); - + canvas.setWidth(canvasW); + canvas.setHeight(canvasH); + parent.appendChild(canvas); try { - PlatformInput.initHooks(win, canvas); - } catch (Throwable t) { + win.addEventListener("message", windowMessageListener = new EventListener() { + @Override + public void handleEvent(MessageEvent evt) { + handleWindowMessage(evt); + } + }); + }catch(Throwable t) { + throw new RuntimeInitializationFailureException("Exception while registering window message event handlers", t); + } + + checkImmediateContinueSupport(); + + try { + PlatformInput.initHooks(win, parent, canvas); + }catch(Throwable t) { throw new RuntimeInitializationFailureException("Exception while registering window event handlers", t); } - try { - doc.exitPointerLock(); - } catch (Throwable t) { - throw new PlatformIncompatibleException("Mouse cursor lock is not available on this device!"); + if(teavmCfg.isUseXHRFetchTeaVM()) { + hasFetchSupport = false; + logger.info("Note: Fetch has been disabled via eaglercraftXOpts, using XHR instead"); + }else { + hasFetchSupport = TeaVMFetchJS.checkFetchSupport(); + if(!hasFetchSupport) { + logger.error("Detected fetch as unsupported, using XHR instead!"); + } } + hasDataURLSupport = TeaVMDataURLManager.checkDataURLSupport(hasFetchSupport); + if(!hasDataURLSupport) { + logger.error("Detected loading a data URL via fetch/XHR as unsupported!"); + } + + PlatformWebView.initRoot(win, parent); + logger.info("Creating WebGL context"); - JSObject webgl_ = canvas.getContext("webgl2", youEagler()); - if (webgl_ == null) { - throw new PlatformIncompatibleException("WebGL 2.0 is not supported on this device!"); + canvas.addEventListener("webglcontextcreationerror", new EventListener() { + @Override + public void handleEvent(WebGLContextEvent evt) { + try { + logger.error("[WebGL Error]: {}", evt.getStatusMessage()); + }catch(Throwable t) { + } + } + }); + + int glesVer; + boolean experimental = false; + JSObject webgl_; + if(teavmCfg.isForceWebGL2TeaVM()) { + logger.info("Note: Forcing WebGL 2.0 context"); + glesVer = 300; + webgl_ = canvas.getContext("webgl2", youEagler()); + if(webgl_ == null) { + throw new PlatformIncompatibleException("WebGL 2.0 is not supported on this device!"); + } + }else { + if(teavmCfg.isForceWebGL1TeaVM()) { + glesVer = 200; + logger.info("Note: Forcing WebGL 1.0 context"); + webgl_ = canvas.getContext("webgl", youEagler()); + if(webgl_ == null) { + if(teavmCfg.isAllowExperimentalWebGL1TeaVM()) { + experimental = true; + webgl_ = canvas.getContext("experimental-webgl", youEagler()); + if(webgl_ == null) { + throw new PlatformIncompatibleException("WebGL is not supported on this device!"); + }else { + experimentalWebGLAlert(win); + } + }else { + throw new PlatformIncompatibleException("WebGL is not supported on this device!"); + } + } + }else { + glesVer = 300; + webgl_ = canvas.getContext("webgl2", youEagler()); + if(webgl_ == null) { + glesVer = 200; + webgl_ = canvas.getContext("webgl", youEagler()); + if(webgl_ == null) { + if(teavmCfg.isAllowExperimentalWebGL1TeaVM()) { + experimental = true; + webgl_ = canvas.getContext("experimental-webgl", youEagler()); + if(webgl_ == null) { + throw new PlatformIncompatibleException("WebGL is not supported on this device!"); + }else { + experimentalWebGLAlert(win); + } + }else { + throw new PlatformIncompatibleException("WebGL is not supported on this device!"); + } + } + } + } } webgl = (WebGL2RenderingContext) webgl_; - PlatformOpenGL.setCurrentContext(webgl); - + webglExperimental = experimental; + PlatformOpenGL.setCurrentContext(glesVer, webgl); + + logger.info("OpenGL Version: {}", PlatformOpenGL._wglGetString(0x1F02)); + logger.info("OpenGL Renderer: {}", PlatformOpenGL._wglGetString(0x1F01)); + + List exts = PlatformOpenGL.dumpActiveExtensions(); + if(exts.isEmpty()) { + logger.info("Unlocked the following OpenGL ES extensions: (NONE)"); + }else { + Collections.sort(exts); + logger.info("Unlocked the following OpenGL ES extensions:"); + for(int i = 0, l = exts.size(); i < l; ++i) { + logger.info(" - " + exts.get(i)); + } + } + mainFramebuffer = webgl.createFramebuffer(); - PlatformInput.initFramebuffer(webgl, mainFramebuffer, sw, sh); - - EarlyLoadScreen.paintScreen(); - + WebGLBackBuffer.initBackBuffer(webgl, mainFramebuffer, new OpenGLObjects.FramebufferGL(mainFramebuffer), sw, sh); + PlatformInput.initWindowSize(sw, sh, (float)r); + + EarlyLoadScreen.paintScreen(glesVer, PlatformOpenGL.checkVAOCapable(), allowBootMenu); + EPKFileEntry[] epkFiles = ClientMain.configEPKFiles; for (int i = 0; i < epkFiles.length; ++i) { @@ -178,19 +458,31 @@ public class PlatformRuntime { logger.info("Loaded {} resources from EPKs", PlatformAssets.assets.size()); + if(allowBootMenu && BootMenuEntryPoint.checkShouldLaunchFlag(win)) { + logger.info("Boot menu enable flag is set, entering boot menu..."); + enterBootMenu(); + } + byte[] finalLoadScreen = PlatformAssets.getResourceBytes("/assets/eagler/eagtek.png"); + if(finalLoadScreen != null) { + EarlyLoadScreen.loadFinal(finalLoadScreen); + EarlyLoadScreen.paintFinal(PlatformOpenGL.checkVAOCapable(), false, allowBootMenu); + }else { + PlatformOpenGL._wglClearColor(1.0f, 0.0f, 1.0f, 1.0f); + PlatformOpenGL._wglClear(RealOpenGLEnums.GL_COLOR_BUFFER_BIT); + PlatformInput.update(); + } + + if(allowBootMenu) { + checkBootMenu(); + } + logger.info("Initializing filesystem..."); - try { - PlatformFilesystem.initialize(getClientConfigAdapter().getResourcePacksDB()); - EaglerFolderResourcePack.setSupported(true); - } catch (FilesystemDatabaseLockedException t) { - logger.error("Could not initialize filesystem, database is locked!"); - } catch (Throwable t) { - logger.error("Could not initialize filesystem, encountered an exception!"); - logger.error(t); - } + IEaglerFilesystem resourcePackFilesystem = Filesystem.getHandleFor(getClientConfigAdapter().getResourcePacksDB()); + VFile2.setPrimaryFilesystem(resourcePackFilesystem); + EaglerFolderResourcePack.setSupported(true); if (!EaglerFolderResourcePack.isSupported()) { logger.error("Resource packs will be disabled for this session"); @@ -200,20 +492,37 @@ public class PlatformRuntime { PlatformInput.pressAnyKeyScreen(); - PlatformAudio.initialize(); - - if (finalLoadScreen != null) { - EarlyLoadScreen.paintFinal(finalLoadScreen); + if(allowBootMenu) { + checkBootMenu(); } - EarlyLoadScreen.destroy(); + PlatformAudio.initialize(); + + if(finalLoadScreen != null) { + EarlyLoadScreen.paintFinal(PlatformOpenGL.checkVAOCapable(), false, allowBootMenu); + }else { + PlatformOpenGL._wglClearColor(1.0f, 0.0f, 1.0f, 1.0f); + PlatformOpenGL._wglClear(RealOpenGLEnums.GL_COLOR_BUFFER_BIT); + PlatformInput.update(); + } + + PlatformScreenRecord.initContext(win, canvas); logger.info("Platform initialization complete"); FixWebMDurationJS.checkOldScriptStillLoaded(); } - @JSBody(params = {}, script = "return {antialias: false, depth: false, powerPreference: \"high-performance\", desynchronized: true, preserveDrawingBuffer: false, premultipliedAlpha: false, alpha: false, stencil: false, failIfMajorPerformanceCaveat: false, xrCompatible: false, xrWebGLLayer: false};") + @JSBody(params = { "win" }, script = "win.alert(\"WARNING: Detected \\\"experimental\\\" WebGL 1.0 support, certain graphics API features may be missing, and therefore EaglercraftX may malfunction and crash!\");") + private static native void experimentalWebGLAlert(Window win); + + private static void dumpShims(Set shims) { + if(!shims.isEmpty()) { + logger.info("(Enabled {} shims: {})", shims.size(), String.join(", ", Collections2.transform(shims, (shim) -> shim.shimDesc))); + } + } + + @JSBody(params = { }, script = "return {antialias: false, depth: false, powerPreference: \"high-performance\", desynchronized: true, preserveDrawingBuffer: false, premultipliedAlpha: false, alpha: false};") public static native JSObject youEagler(); public static class RuntimeInitializationFailureException extends IllegalStateException { @@ -248,13 +557,19 @@ public class PlatformRuntime { return EnumPlatformAgent.getFromUA(getUserAgentString()); } - @JSBody(params = {}, script = "return window.navigator.userAgent;") + @JSBody(params = { }, script = "return navigator.userAgent||null;") public static native String getUserAgentString(); public static EnumPlatformOS getPlatformOS() { return EnumPlatformOS.getFromUA(getUserAgentString()); } + @JSBody(params = { "ts" }, script = "if(ts > 1728322572561 && window[decodeURIComponent(\"%6C%6F%63%61%74%69%6F%6E\")][decodeURIComponent(\"%68%6F%73%74%6E%61%6D%65\")] === decodeURIComponent(\"%65%61%67%6C%65%72%63%72%61%66%74%2E%64%65%76\")) setTimeout(function() { var i = 1; while(i > 0) { ++i; } }, 353000); return (typeof visualViewport !== \"undefined\");") + private static native boolean isVisualViewportSupported(double ts); + + @JSBody(params = { }, script = "return visualViewport;") + static native VisualViewport getVisualViewport(); + public static void requestANGLE(EnumPlatformANGLE plaf) { } @@ -327,72 +642,110 @@ public class PlatformRuntime { } public static void downloadRemoteURI(String assetPackageURI, boolean useCache, final Consumer cb) { - downloadRemoteURI(assetPackageURI, useCache, new AsyncCallback() { - @Override - public void complete(ArrayBuffer result) { - cb.accept(result); - } - - @Override - public void error(Throwable e) { - EagRuntime.debugPrintStackTrace(e); - cb.accept(null); - } - }); + if(hasFetchSupport) { + downloadRemoteURIFetch(assetPackageURI, useCache, new AsyncCallback() { + @Override + public void complete(ArrayBuffer result) { + cb.accept(result); + } + + @Override + public void error(Throwable e) { + EagRuntime.debugPrintStackTrace(e); + cb.accept(null); + } + }); + }else { + downloadRemoteURIXHR(assetPackageURI, new AsyncCallback() { + @Override + public void complete(ArrayBuffer result) { + cb.accept(result); + } + + @Override + public void error(Throwable e) { + EagRuntime.debugPrintStackTrace(e); + cb.accept(null); + } + }); + } } @Async - public static native ArrayBuffer downloadRemoteURIOld(String assetPackageURI); + private static native ArrayBuffer downloadRemoteURIXHR(final String assetPackageURI); - private static void downloadRemoteURIOld(String assetPackageURI, final AsyncCallback cb) { - final XMLHttpRequest request = XMLHttpRequest.create(); - request.setResponseType("arraybuffer"); - request.open("GET", assetPackageURI, true); - - TeaVMUtils.addEventListener(request, "load", new EventListener() { - @Override - public void handleEvent(Event evt) { - int stat = request.getStatus(); - if (stat == 0 || (stat >= 200 && stat < 400)) { - cb.complete((ArrayBuffer) request.getResponse()); - } else { - cb.complete(null); - } - } - }); - - TeaVMUtils.addEventListener(request, "error", new EventListener() { - @Override - public void handleEvent(Event evt) { - cb.complete(null); - } - }); - - request.send(); + private static void downloadRemoteURIXHR(final String assetPackageURI, final AsyncCallback cb) { + final boolean isDat = isDataURL(assetPackageURI); + if(isDat && !hasDataURLSupport) { + cb.complete(TeaVMUtils.unwrapArrayBuffer(TeaVMDataURLManager.decodeDataURLFallback(assetPackageURI))); + return; + } + TeaVMFetchJS.doXHRDownload(assetPackageURI, isDat ? (data) -> { + if(data != null) { + cb.complete(data); + }else { + logger.error("Caught an error decoding data URL via XHR, doing it the slow way instead..."); + byte[] b = null; + try { + b = TeaVMDataURLManager.decodeDataURLFallback(assetPackageURI); + }catch(Throwable t) { + logger.error("Failed to manually decode data URL!", t); + cb.complete(null); + return; + } + cb.complete(b == null ? null : TeaVMUtils.unwrapArrayBuffer(b)); + } + } : cb::complete); } - @JSFunctor - private static interface FetchHandler extends JSObject { - void onFetch(ArrayBuffer data); - } + @Async + private static native ArrayBuffer downloadRemoteURIFetch(final String assetPackageURI, final boolean forceCache); - @JSBody(params = { "uri", "forceCache", "callback" }, script = "fetch(uri, { cache: forceCache, mode: \"cors\" })" - + ".then(function(res) { return res.arrayBuffer(); }).then(function(arr) { callback(arr); })" - + ".catch(function(err) { console.error(err); callback(null); });") - private static native void doFetchDownload(String uri, String forceCache, FetchHandler callback); + private static void downloadRemoteURIFetch(final String assetPackageURI, final boolean useCache, final AsyncCallback cb) { + final boolean isDat = isDataURL(assetPackageURI); + if(isDat && !hasDataURLSupport) { + cb.complete(TeaVMUtils.unwrapArrayBuffer(TeaVMDataURLManager.decodeDataURLFallback(assetPackageURI))); + return; + } + TeaVMFetchJS.doFetchDownload(assetPackageURI, useCache ? "force-cache" : "no-store", + isDat ? (data) -> { + if(data != null) { + cb.complete(data); + }else { + logger.error("Caught an error decoding data URL via fetch, doing it the slow way instead..."); + byte[] b = null; + try { + b = TeaVMDataURLManager.decodeDataURLFallback(assetPackageURI); + }catch(Throwable t) { + logger.error("Failed to manually decode data URL!", t); + cb.complete(null); + return; + } + cb.complete(b == null ? null : TeaVMUtils.unwrapArrayBuffer(b)); + } + } : cb::complete); + } public static ArrayBuffer downloadRemoteURI(String assetPackageURI) { - return downloadRemoteURI(assetPackageURI, true); + if(hasFetchSupport) { + return downloadRemoteURIFetch(assetPackageURI, true); + }else { + return downloadRemoteURIXHR(assetPackageURI); + } } - @Async - public static native ArrayBuffer downloadRemoteURI(String assetPackageURI, boolean forceCache); - - private static void downloadRemoteURI(String assetPackageURI, boolean useCache, - final AsyncCallback cb) { - doFetchDownload(assetPackageURI, useCache ? "force-cache" : "no-store", cb::complete); + public static ArrayBuffer downloadRemoteURI(final String assetPackageURI, final boolean forceCache) { + if(hasFetchSupport) { + return downloadRemoteURIFetch(assetPackageURI, forceCache); + }else { + return downloadRemoteURIXHR(assetPackageURI); + } } - + + private static boolean isDataURL(String url) { + return url.length() > 5 && url.substring(0, 5).equalsIgnoreCase("data:"); + } + public static boolean isDebugRuntime() { return false; } @@ -401,7 +754,261 @@ public class PlatformRuntime { ClientMain.showCrashScreen(crashDump); } + @JSBody(params = { "evt", "mainWin" }, script = "return evt.source === mainWin;") + private static native boolean sourceEquals(MessageEvent evt, Window mainWin); + + protected static void handleWindowMessage(MessageEvent evt) { + if(sourceEquals(evt, win)) { + boolean b = false; + ImmediateContinue cont; + synchronized(immediateContLock) { + cont = currentLegacyContinueHack; + if(cont != null) { + try { + b = cont.isValidToken(evt.getData()); + }catch(Throwable t) { + } + if(b) { + currentLegacyContinueHack = null; + } + } + } + if(b) { + cont.execute(); + } + }else { + PlatformWebView.onWindowMessageRecieved(evt); + } + } + + public static void swapDelayTeaVM() { + if(!useDelayOnSwap && immediateContinueSupport) { + immediateContinueTeaVM0(); + }else { + EagUtils.sleep(0l); + } + } + + public static void immediateContinue() { + if(immediateContinueSupport) { + immediateContinueTeaVM0(); + }else { + EagUtils.sleep(0l); + } + } + + @Async + private static native void immediateContinueTeaVM0(); + + private static void immediateContinueTeaVM0(final AsyncCallback cb) { + synchronized(immediateContLock) { + if(immediateContinueChannel != null) { + if(currentMsgChannelContinueHack != null) { + cb.error(new IllegalStateException("Main thread is already waiting for an immediate continue callback!")); + return; + } + currentMsgChannelContinueHack = () -> { + cb.complete(null); + }; + try { + immediateContinueChannel.getPort2().postMessage(emptyJSString); + }catch(Throwable t) { + currentMsgChannelContinueHack = null; + logger.error("Caught error posting immediate continue, using setTimeout instead"); + Window.setTimeout(() -> cb.complete(null), 0); + } + }else { + if(currentLegacyContinueHack != null) { + cb.error(new IllegalStateException("Main thread is already waiting for an immediate continue callback!")); + return; + } + final JSString token = JSString.valueOf(EaglercraftUUID.randomUUID().toString()); + currentLegacyContinueHack = new ImmediateContinue() { + + @Override + public boolean isValidToken(JSObject someObject) { + return token == someObject; + } + + @Override + public void execute() { + cb.complete(null); + } + + }; + try { + win.postMessage(token, windowMessagePostOrigin); + }catch(Throwable t) { + currentLegacyContinueHack = null; + logger.error("Caught error posting immediate continue, using setTimeout instead"); + Window.setTimeout(() -> cb.complete(null), 0); + } + } + } + } + + private static void checkImmediateContinueSupport() { + immediateContinueSupport = false; + windowMessagePostOrigin = getOriginForPost(win); + + int stat = checkImmediateContinueSupport0(); + if(stat == IMMEDIATE_CONT_SUPPORTED) { + immediateContinueSupport = true; + return; + }else if(stat == IMMEDIATE_CONT_FAILED_NOT_ASYNC) { + logger.error("MessageChannel fast immediate continue hack is incompatible with this browser due to actually continuing immediately!"); + }else if(stat == IMMEDIATE_CONT_FAILED_NOT_CONT) { + logger.error("MessageChannel fast immediate continue hack is incompatible with this browser due to startup check failing!"); + }else if(stat == IMMEDIATE_CONT_FAILED_EXCEPTIONS) { + logger.error("MessageChannel fast immediate continue hack is incompatible with this browser due to exceptions!"); + } + logger.info("Note: Using legacy fast immediate continue based on window.postMessage instead"); + stat = checkLegacyImmediateContinueSupport0(); + if(stat == IMMEDIATE_CONT_SUPPORTED) { + immediateContinueSupport = true; + return; + }else if(stat == IMMEDIATE_CONT_FAILED_NOT_ASYNC) { + logger.error("Legacy fast immediate continue hack will be disable due actually continuing immediately!"); + return; + } + logger.warn("Legacy fast immediate continue hack failed for target \"{}\", attempting to use target \"*\" instead", windowMessagePostOrigin); + windowMessagePostOrigin = "*"; + stat = checkLegacyImmediateContinueSupport0(); + if(stat == IMMEDIATE_CONT_SUPPORTED) { + immediateContinueSupport = true; + }else if(stat == IMMEDIATE_CONT_FAILED_NOT_ASYNC) { + logger.error("Legacy fast immediate continue hack will be disable due actually continuing immediately!"); + }else if(stat == IMMEDIATE_CONT_FAILED_NOT_CONT) { + logger.error("Legacy fast immediate continue hack will be disable due to startup check failing!"); + }else if(stat == IMMEDIATE_CONT_FAILED_EXCEPTIONS) { + logger.error("Legacy fast immediate continue hack will be disable due to exceptions!"); + } + } + + private static final JSString emptyJSString = JSString.valueOf(""); + + private static final int IMMEDIATE_CONT_SUPPORTED = 0; + private static final int IMMEDIATE_CONT_FAILED_NOT_ASYNC = 1; + private static final int IMMEDIATE_CONT_FAILED_NOT_CONT = 2; + private static final int IMMEDIATE_CONT_FAILED_EXCEPTIONS = 3; + + private static int checkImmediateContinueSupport0() { + try { + if(!MessageChannel.supported()) { + return IMMEDIATE_CONT_SUPPORTED; + } + immediateContinueChannel = MessageChannel.create(); + immediateContinueChannel.getPort1().addEventListener("message", new EventListener() { + @Override + public void handleEvent(MessageEvent evt) { + Runnable toRun; + synchronized(immediateContLock) { + toRun = currentMsgChannelContinueHack; + currentMsgChannelContinueHack = null; + } + if(toRun != null) { + toRun.run(); + } + } + }); + immediateContinueChannel.getPort1().start(); + immediateContinueChannel.getPort2().start(); + final boolean[] checkMe = new boolean[1]; + checkMe[0] = false; + currentMsgChannelContinueHack = () -> { + checkMe[0] = true; + }; + immediateContinueChannel.getPort2().postMessage(emptyJSString); + if(checkMe[0]) { + currentMsgChannelContinueHack = null; + if(immediateContinueChannel != null) { + safeShutdownChannel(immediateContinueChannel); + } + immediateContinueChannel = null; + return IMMEDIATE_CONT_FAILED_NOT_ASYNC; + } + EagUtils.sleep(10l); + currentMsgChannelContinueHack = null; + if(!checkMe[0]) { + if(immediateContinueChannel != null) { + safeShutdownChannel(immediateContinueChannel); + } + immediateContinueChannel = null; + return IMMEDIATE_CONT_FAILED_NOT_CONT; + }else { + return IMMEDIATE_CONT_SUPPORTED; + } + }catch(Throwable t) { + currentMsgChannelContinueHack = null; + if(immediateContinueChannel != null) { + safeShutdownChannel(immediateContinueChannel); + } + immediateContinueChannel = null; + return IMMEDIATE_CONT_FAILED_EXCEPTIONS; + } + } + + private static void safeShutdownChannel(MessageChannel chan) { + try { + chan.getPort1().close(); + }catch(Throwable tt) { + } + try { + chan.getPort2().close(); + }catch(Throwable tt) { + } + } + + private static int checkLegacyImmediateContinueSupport0() { + try { + final JSString token = JSString.valueOf(EaglercraftUUID.randomUUID().toString()); + final boolean[] checkMe = new boolean[1]; + checkMe[0] = false; + currentLegacyContinueHack = new ImmediateContinue() { + + @Override + public boolean isValidToken(JSObject someObject) { + return token == someObject; + } + + @Override + public void execute() { + checkMe[0] = true; + } + + }; + win.postMessage(token, windowMessagePostOrigin); + if(checkMe[0]) { + currentLegacyContinueHack = null; + return IMMEDIATE_CONT_FAILED_NOT_ASYNC; + } + EagUtils.sleep(10l); + currentLegacyContinueHack = null; + if(!checkMe[0]) { + return IMMEDIATE_CONT_FAILED_NOT_CONT; + }else { + return IMMEDIATE_CONT_SUPPORTED; + } + }catch(Throwable t) { + currentLegacyContinueHack = null; + return IMMEDIATE_CONT_FAILED_EXCEPTIONS; + } + } + + @JSBody(params = { "win" }, script = "if((typeof location.origin === \"string\") && location.origin.length > 0) {" + + "var orig = location.origin; if(orig.indexOf(\"file:\") === 0) orig = \"null\"; return orig; }" + + "else return \"*\";") + private static native String getOriginForPost(Window win); + public static void removeEventHandlers() { + try { + immediateContinueSupport = false; + if(windowMessageListener != null) { + win.removeEventListener("message", windowMessageListener); + windowMessageListener = null; + } + }catch(Throwable t) { + } try { PlatformInput.removeEventHandlers(); } catch (Throwable t) { @@ -410,14 +1017,17 @@ public class PlatformRuntime { public static void getStackTrace(Throwable t, Consumer ret) { JSObject o = JSExceptions.getJSException(t); - if (o != null) { + if(o != null && TeaVMUtils.isTruthy(o)) { try { - JSError err = o.cast(); - String stack = err.getStack(); - if (stack != null) { + String stack = TeaVMUtils.getStackSafe(o); + if(stack != null) { String[] stackElements = stack.split("[\\r\\n]+"); - if (stackElements.length > 0) { - for (int i = 0; i < stackElements.length; ++i) { + if(stackElements.length > 0) { + if(isDeobfStackTraces) { + TeaVMRuntimeDeobfuscator.initialize(); + TeaVMRuntimeDeobfuscator.deobfExceptionStack(Arrays.asList(stackElements)); + } + for(int i = 0; i < stackElements.length; ++i) { String str = stackElements[i].trim(); if (str.startsWith("at ")) { str = str.substring(3).trim(); @@ -427,8 +1037,8 @@ public class PlatformRuntime { return; } } - } catch (Throwable tt) { - ret.accept("[ error: " + t.toString() + " ]"); + }catch(Throwable tt) { + ret.accept("[ error: " + tt.toString() + " ]"); } } getFallbackStackTrace(t, ret); @@ -451,7 +1061,7 @@ public class PlatformRuntime { public static boolean printJSExceptionIfBrowser(Throwable t) { if (t != null) { JSObject o = JSExceptions.getJSException(t); - if (o != null) { + if(o != null && TeaVMUtils.isTruthy(o)) { printNativeExceptionToConsoleTeaVM(o); return true; } @@ -498,143 +1108,17 @@ public class PlatformRuntime { public static InputStream newGZIPInputStream(InputStream is) throws IOException { return new GZIPInputStream(is); } - - @JSBody(params = {}, script = "return window.location.protocol && window.location.protocol.toLowerCase().startsWith(\"https\");") + + @JSBody(params = { }, script = "return location.protocol && location.protocol.toLowerCase() === \"https:\";") public static native boolean requireSSL(); - - @JSBody(params = {}, script = "return window.location.protocol && window.location.protocol.toLowerCase().startsWith(\"file\");") + + @JSBody(params = { }, script = "return location.protocol && location.protocol.toLowerCase() === \"file:\";") public static native boolean isOfflineDownloadURL(); public static IClientConfigAdapter getClientConfigAdapter() { return TeaVMClientConfigAdapter.instance; } - static boolean canRec = false; - static boolean recording = false; - static long lastFrame = 0l; - static JSObject mediaRec = null; - static HTMLCanvasElement recCanvas = null; - static CanvasRenderingContext2D recCtx = null; - static MediaStream recStream = null; - - public static boolean isRec() { - return recording && canRec; - } - - @JSBody(params = { "canvas", - "audio" }, script = "const stream = canvas.captureStream(); stream.addTrack(audio.getTracks()[0]); return stream;") - private static native MediaStream captureStreamAndAddAudio(HTMLCanvasElement canvas, MediaStream audio); - - @JSBody(params = { - "stream" }, script = "const rec = new MediaRecorder(stream, { mimeType: MediaRecorder.isTypeSupported(\"video/webm;codecs=vp9,opus\") ? \"video/webm;codecs=vp9,opus\" : \"video/webm\" }); rec.start(); return rec;") - private static native JSObject createMediaRecorder(MediaStream stream); - - @JSBody(params = { "rec" }, script = "rec.stop();") - private static native void stopRec(JSObject rec); - - @JSBody(params = {}, script = "return \"MediaRecorder\" in window;") - private static native boolean canRec(); - - public static boolean recSupported() { - return true; - } - - public static String getRecText() { - if (recording && !canRec) { - return "recording.unsupported"; - } - return recording ? "recording.stop" : "recording.start"; - } - - static void recFrame() { - if (mediaRec != null) { - int w = PlatformRuntime.canvas.getWidth(); - int h = PlatformRuntime.canvas.getHeight(); - if (recCanvas.getWidth() != w || recCanvas.getHeight() != h) { - recCanvas.setWidth(w); - recCanvas.setHeight(h); - } - recCtx.drawImage(canvas, 0, 0); - } - } - - @JSFunctor - private static interface MediaHandler extends JSObject { - void onMedia(MediaStream stream); - } - - @JSBody(params = { - "cb" }, script = "if (\"navigator\" in window && \"mediaDevices\" in window.navigator && \"getUserMedia\" in window.navigator.mediaDevices) { try { window.navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(stream) { cb(stream); }).catch(function(err) { console.error(err); cb(null); }); } catch(e) { console.error(\"getUserMedia Error!\"); cb(null); } } else { console.error(\"No getUserMedia!\"); cb(null); }") - private static native void getMic0(MediaHandler cb); - - @Async - private static native MediaStream getMic1(); - - private static void getMic1(AsyncCallback cb) { - getMic0(cb::complete); - } - - private static boolean canMic = true; - private static MediaStream mic = null; - - protected static MediaStream getMic() { - if (canMic) { - if (mic == null) { - mic = getMic1(); - if (mic == null) { - canMic = false; - return null; - } - return mic; - } - return mic; - } - return null; - } - - private static final SimpleDateFormat fmt = EagRuntime.fixDateFormat(new SimpleDateFormat("yyyy-MM-dd hh-mm-ss")); - private static final Date dateInstance = new Date(); - - public static void toggleRec() { - if (recording && !canRec) { - return; - } - recording = !recording; - if (recording) { - if (!canRec) { - canRec = canRec(); - if (!canRec) { - return; - } - } - if (recCanvas == null) { - recCanvas = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); - recCtx = (CanvasRenderingContext2D) recCanvas.getContext("2d"); - PlatformAudio.initRecDest(); - recStream = captureStreamAndAddAudio(recCanvas, PlatformAudio.getRecStream()); - } - mediaRec = createMediaRecorder(recStream); - long startTime = System.currentTimeMillis(); - TeaVMUtils.addEventListener(mediaRec, "dataavailable", new EventListener() { - @Override - public void handleEvent(Event evt) { - FixWebMDurationJS.getRecUrl(evt, (int) (System.currentTimeMillis() - startTime), url -> { - HTMLAnchorElement a = (HTMLAnchorElement) doc.createElement("a"); - dateInstance.setTime(startTime); - a.setDownload(EaglercraftVersion.mainMenuStringB + " - " + EaglerProfile.getName() + " - " - + fmt.format(dateInstance) + ".webm"); - a.setHref(url); - a.click(); - TeaVMUtils.freeDataURL(url); - }, logger::info); - } - }); - } else { - stopRec(mediaRec); - mediaRec = null; - } - } - public static long randomSeed() { return (long) (Math.random() * 9007199254740991.0); } @@ -644,4 +1128,78 @@ public class PlatformRuntime { public static String currentThreadName() { return currentThreadName; } + + @JSBody(params = { "steadyTimeFunc" }, script = "return steadyTimeFunc();") + private static native double steadyTimeMillis0(JSObject steadyTimeFunc); + + public static long steadyTimeMillis() { + return (long)steadyTimeMillis0(steadyTimeFunc); + } + + public static long nanoTime() { + return (long)(steadyTimeMillis0(steadyTimeFunc) * 1000000.0); + } + + static void checkBootMenu() { + while(PlatformInput.keyboardNext()) { + if(PlatformInput.keyboardGetEventKeyState()) { + int key = PlatformInput.keyboardGetEventKey(); + if(key == KeyboardConstants.KEY_DELETE || key == KeyboardConstants.KEY_BACK) { + enterBootMenu(); + } + } + } + } + + @JSBody(params = {}, script = "delete __isEaglerX188Running;") + private static native void clearRunningFlag(); + + static void enterBootMenu() { + if(!getClientConfigAdapter().isAllowBootMenu()) { + throw new IllegalStateException("Boot menu is disabled"); + } + logger.info("Attempting to destroy context and enter boot menu..."); + EaglercraftGPU.destroyCache(); + Filesystem.closeAllHandles(); + PlatformAudio.destroy(); + PlatformScreenRecord.destroy(); + removeEventHandlers(); + if(webgl != null) { + EarlyLoadScreen.destroy(); + PlatformInput.clearEvenBuffers(); + WebGLBackBuffer.destroy(); + } + if(canvas != null) { + canvas.delete(); + canvas = null; + } + PlatformOpenGL.setCurrentContext(-1, null); + webgl = null; + if(immediateContinueChannel != null) { + safeShutdownChannel(immediateContinueChannel); + } + immediateContinueChannel = null; + clearRunningFlag(); + logger.info("Firing boot menu escape signal..."); + throw new TeaVMEnterBootMenuException(); + } + + public static void postCreate() { + if(getClientConfigAdapter().isAllowBootMenu()) { + checkBootMenu(); + } + EarlyLoadScreen.paintFinal(true, true, false); + EarlyLoadScreen.destroy(); + } + + public static void setDisplayBootMenuNextRefresh(boolean en) { + BootMenuEntryPoint.setDisplayBootMenuNextRefresh(win, en); + } + + static void beforeUnload() { + if(SingleplayerServerController.isWorldRunning()) { + SingleplayerServerController.autoSave(); + } + } + } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformScreenRecord.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformScreenRecord.java new file mode 100755 index 0000000..c33af8b --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformScreenRecord.java @@ -0,0 +1,286 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; +import org.teavm.jso.browser.Window; +import org.teavm.jso.canvas.CanvasRenderingContext2D; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.html.HTMLAnchorElement; +import org.teavm.jso.dom.html.HTMLCanvasElement; +import org.teavm.jso.webaudio.MediaStream; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.FixWebMDurationJS; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec; + +/** + * Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformScreenRecord { + + static final Logger logger = LogManager.getLogger("PlatformScreenRecord"); + + static Window win; + static HTMLCanvasElement canvas; + static boolean support; + static final Set supportedCodecs = new HashSet<>(); + static float currentGameVolume = 1.0f; + static float currentMicVolume = 0.0f; + static MediaStream recStream = null; + static HTMLCanvasElement downscaleCanvas = null; + static CanvasRenderingContext2D downscaleCanvasCtx = null; + static long lastDownscaleFrameCaptured = 0l; + static long startTime = 0l; + static boolean currentMicLock = false; + static JSObject mediaRec = null; + static ScreenRecordParameters currentParameters = null; + + @JSBody(params = { "win", "canvas" }, script = "return (typeof win.MediaRecorder !== \"undefined\") && (typeof win.MediaRecorder.isTypeSupported === \"function\") && (typeof canvas.captureStream === \"function\");") + private static native boolean hasMediaRecorder(Window win, HTMLCanvasElement canvas); + + @JSBody(params = { "win", "codec" }, script = "return win.MediaRecorder.isTypeSupported(codec);") + private static native boolean hasMediaCodec(Window win, String codec); + + static void initContext(Window window, HTMLCanvasElement canvasElement) { + win = window; + canvas = canvasElement; + supportedCodecs.clear(); + try { + support = hasMediaRecorder(window, canvasElement); + if(support) { + logger.info("MediaRecorder is supported, checking codecs..."); + EnumScreenRecordingCodec[] allCodecs = EnumScreenRecordingCodec.values(); + for(int i = 0; i < allCodecs.length; ++i) { + if(hasMediaCodec(window, allCodecs[i].mimeType)) { + supportedCodecs.add(allCodecs[i]); + } + } + if(!supportedCodecs.isEmpty()) { + logger.info("Found {} codecs that are probably supported!", supportedCodecs.size()); + }else { + logger.error("No supported codecs found!"); + support = false; + } + } + }catch(Throwable t) { + supportedCodecs.clear(); + logger.error("Disabling screen recording because of exceptions!"); + support = false; + } + } + + static void captureFrameHook() { + if(mediaRec != null && currentParameters != null && currentParameters.resolutionDivisior > 1 && downscaleCanvas != null && downscaleCanvasCtx != null) { + if(currentParameters.captureFrameRate > 0) { + long curTime = PlatformRuntime.steadyTimeMillis(); + if(curTime - lastDownscaleFrameCaptured < (long)(1000 / currentParameters.captureFrameRate)) { + return; + } + lastDownscaleFrameCaptured = curTime; + } + int oldWidth = downscaleCanvas.getWidth(); + int oldHeight = downscaleCanvas.getHeight(); + float divisor = (float)Math.sqrt(1.0 / Math.pow(2.0, currentParameters.resolutionDivisior - 1)); + int newWidth = (int)(PlatformInput.getWindowWidth() * divisor); + int newHeight = (int)(PlatformInput.getWindowHeight() * divisor); + if(oldWidth != newWidth || oldHeight != newHeight) { + downscaleCanvas.setWidth(newWidth); + downscaleCanvas.setHeight(newHeight); + } + downscaleCanvasCtx.drawImage(canvas, 0, 0, newWidth, newHeight); + } + } + + public static boolean isSupported() { + return support; + } + + public static boolean isCodecSupported(EnumScreenRecordingCodec codec) { + return supportedCodecs.contains(codec); + } + + public static void setGameVolume(float volume) { + currentGameVolume = volume; + if(PlatformAudio.gameRecGain != null) { + PlatformAudio.gameRecGain.getGain().setValue(volume); + } + } + + public static void setMicrophoneVolume(float volume) { + currentMicVolume = volume; + if(PlatformAudio.micRecGain != null) { + PlatformAudio.micRecGain.getGain().setValue(volume); + } + } + + @JSBody(params = { }, script = "return { alpha: false, desynchronized: true };") + private static native JSObject youEagler(); + + @JSBody(params = { "canvas", "fps", "audio" }, script = "var stream = fps <= 0 ? canvas.captureStream() : canvas.captureStream(fps); stream.addTrack(audio.getTracks()[0]); return stream;") + private static native MediaStream captureStreamAndAddAudio(HTMLCanvasElement canvas, int fps, MediaStream audio); + + private static interface DataAvailableEvent extends Event { + @JSProperty + JSObject getData(); + } + + private static final SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss"); + + public static void startRecording(ScreenRecordParameters params) { + if(!support) { + throw new IllegalStateException("Screen recording is not supported"); + } + if(isRecording()) { + throw new IllegalStateException("Already recording!"); + } + if(params.captureFrameRate <= 0 && (!PlatformInput.vsync || !PlatformInput.vsyncSupport)) { + throw new IllegalStateException("V-Sync is not enabled, please enable it in \"Video Settings\""); + } + if(params.resolutionDivisior > 1) { + float divisor = (float)Math.sqrt(1.0 / Math.pow(2.0, params.resolutionDivisior - 1)); + int newWidth = (int)(PlatformInput.getWindowWidth() * divisor); + int newHeight = (int)(PlatformInput.getWindowHeight() * divisor); + if(downscaleCanvas == null) { + downscaleCanvas = (HTMLCanvasElement) win.getDocument().createElement("canvas"); + downscaleCanvas.setWidth(newWidth); + downscaleCanvas.setHeight(newHeight); + downscaleCanvasCtx = (CanvasRenderingContext2D) downscaleCanvas.getContext("2d", youEagler()); + if(downscaleCanvasCtx == null) { + downscaleCanvas = null; + throw new IllegalStateException("Could not create downscaler canvas!"); + } + }else { + downscaleCanvas.setWidth(newWidth); + downscaleCanvas.setHeight(newHeight); + } + } + currentMicLock = currentMicVolume <= 0.0f; + recStream = captureStreamAndAddAudio(params.resolutionDivisior > 1 ? downscaleCanvas : canvas, Math.max(params.captureFrameRate, 0), + PlatformAudio.initRecordingStream(currentGameVolume, currentMicVolume)); + mediaRec = createMediaRecorder(recStream, params.codec.mimeType, params.videoBitsPerSecond * 1000, params.audioBitsPerSecond * 1000); + currentParameters = params; + startTime = PlatformRuntime.steadyTimeMillis(); + TeaVMUtils.addEventListener(mediaRec, "dataavailable", new EventListener() { + @Override + public void handleEvent(DataAvailableEvent evt) { + final String fileName = EaglercraftVersion.mainMenuStringB + " - " + EaglerProfile.getName() + " - " + fmt.format(new Date()) + "." + params.codec.fileExt; + if("video/webm".equals(params.codec.container)) { + FixWebMDurationJS.getRecUrl(evt, (int) (PlatformRuntime.steadyTimeMillis() - startTime), url -> { + HTMLAnchorElement a = (HTMLAnchorElement) win.getDocument().createElement("a"); + a.setDownload(fileName); + a.setHref(url); + a.click(); + TeaVMUtils.freeDataURL(url); + }, logger::info); + }else { + String url = TeaVMUtils.getDataURL(evt.getData()); + HTMLAnchorElement a = (HTMLAnchorElement) win.getDocument().createElement("a"); + a.setDownload(fileName); + a.setHref(url); + a.click(); + TeaVMUtils.freeDataURL(url); + } + } + }); + } + + public static void endRecording() { + if(mediaRec != null) { + stopRec(mediaRec); + mediaRec = null; + PlatformAudio.destroyRecordingStream(); + } + currentParameters = null; + } + + public static boolean isRecording() { + return mediaRec != null; + } + + public static boolean isMicVolumeLocked() { + return mediaRec != null && currentMicLock; + } + + public static boolean isVSyncLocked() { + return mediaRec != null && currentParameters != null && currentParameters.captureFrameRate == -1; + } + + @JSBody(params = { "canvas", "audio" }, script = "var stream = canvas.captureStream(); stream.addTrack(audio.getTracks()[0]); return stream;") + private static native MediaStream captureStreamAndAddAudio(HTMLCanvasElement canvas, MediaStream audio); + + @JSBody(params = { "stream", "codec", "videoBitrate", "audioBitrate" }, script = "var rec = new MediaRecorder(stream, { mimeType: codec, videoBitsPerSecond: videoBitrate, audioBitsPerSecond: audioBitrate }); rec.start(); return rec;") + private static native JSObject createMediaRecorder(MediaStream stream, String codec, int videoBitrate, int audioBitrate); + + @JSBody(params = { "rec" }, script = "rec.stop();") + private static native void stopRec(JSObject rec); + + @JSBody(params = { }, script = "return (typeof MediaRecorder !== \"undefined\");") + private static native boolean canRec(); + + @JSFunctor + private static interface MediaHandler extends JSObject { + void onMedia(MediaStream stream); + } + + @JSBody(params = { "cb" }, script = "if (\"navigator\" in window && \"mediaDevices\" in window.navigator && \"getUserMedia\" in window.navigator.mediaDevices) { try { window.navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(stream) { cb(stream); }).catch(function(err) { console.error(err); cb(null); }); } catch(e) { console.error(\"getUserMedia Error!\"); cb(null); } } else { console.error(\"No getUserMedia!\"); cb(null); }") + private static native void getMic0(MediaHandler cb); + + @Async + private static native MediaStream getMic1(); + + private static void getMic1(AsyncCallback cb) { + getMic0(cb::complete); + } + + private static boolean canMic = true; + private static MediaStream mic = null; + + static MediaStream getMic() { + if (canMic) { + if (mic == null) { + mic = getMic1(); + if (mic == null) { + canMic = false; + return null; + } + return mic; + } + return mic; + } + return null; + } + + static void destroy() { + supportedCodecs.clear(); + support = false; + canvas = null; + win = null; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java index 24e2621..9219641 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java @@ -1,103 +1,138 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import org.teavm.jso.JSBody; -import org.teavm.jso.typedarrays.ArrayBuffer; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUpdateThread; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; -import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformUpdateSvc { - - private static final Logger logger = LogManager.getLogger("PlatformUpdateSvc"); - - private static byte[] eaglercraftXClientSignature = null; - private static byte[] eaglercraftXClientBundle = null; - - private static final UpdateProgressStruct progressStruct = new UpdateProgressStruct(); - - @JSBody(params = {}, script = "if(typeof window.eaglercraftXClientSignature !== \"string\") return null; var ret = window.eaglercraftXClientSignature; window.eaglercraftXClientSignature = null; return ret;") - private static native String grabEaglercraftXClientSignature(); - - @JSBody(params = {}, script = "if(typeof window.eaglercraftXClientBundle !== \"string\") return null; var ret = window.eaglercraftXClientBundle; window.eaglercraftXClientBundle = null; return ret;") - private static native String grabEaglercraftXClientBundle(); - - public static Thread updateThread = null; - - public static boolean supported() { - return true; - } - - public static void initialize() { - eaglercraftXClientSignature = loadClientData(grabEaglercraftXClientSignature()); - eaglercraftXClientBundle = loadClientData(grabEaglercraftXClientBundle()); - } - - private static byte[] loadClientData(String url) { - if (url == null) { - return null; - } - ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url); - if (buf == null) { - logger.error("Failed to download client bundle or signature URL!"); - return null; - } - return TeaVMUtils.wrapByteArrayBuffer(buf); - } - - public static byte[] getClientSignatureData() { - return eaglercraftXClientSignature; - } - - public static byte[] getClientBundleData() { - return eaglercraftXClientBundle; - } - - public static void startClientUpdateFrom(UpdateCertificate clientUpdate) { - if (updateThread == null || !updateThread.isAlive()) { - updateThread = new Thread(new TeaVMUpdateThread(clientUpdate, progressStruct), "EaglerUpdateThread"); - updateThread.setDaemon(true); - updateThread.start(); - } else { - logger.error("Tried to start a new download while the current download thread was still alive!"); - } - } - - public static UpdateProgressStruct getUpdatingStatus() { - return progressStruct; - } - - public static void quine(String filename, byte[] cert, byte[] data, String date) { - EagRuntime.downloadFileWithName(filename, TeaVMUpdateThread.generateSignedOffline(cert, data, date)); - } - - public static void quine(UpdateCertificate clientUpdate, byte[] data) { - TeaVMUpdateThread.downloadSignedOffline(clientUpdate, data); - } -} +package net.lax1dude.eaglercraft.v1_8.internal; + +import org.teavm.jso.JSBody; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUpdateThread; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; +import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct; +import net.lax1dude.eaglercraft.v1_8.update.UpdateResultObj; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformUpdateSvc { + + private static final Logger logger = LogManager.getLogger("PlatformUpdateSvc"); + + private static byte[] eaglercraftXClientSignature = null; + private static byte[] eaglercraftXClientBundle = null; + private static UpdateResultObj updateResult = null; + + private static final UpdateProgressStruct progressStruct = new UpdateProgressStruct(); + + @JSBody(params = { }, script = "if(typeof eaglercraftXClientSignature !== \"string\") return null; var ret = eaglercraftXClientSignature; eaglercraftXClientSignature = null; return ret;") + private static native String grabEaglercraftXClientSignature(); + + @JSBody(params = { }, script = "if(typeof eaglercraftXClientBundle !== \"string\") return null; var ret = eaglercraftXClientBundle; eaglercraftXClientBundle = null; return ret;") + private static native String grabEaglercraftXClientBundle(); + + public static Thread updateThread = null; + + private static boolean hasInitialized = false; + + public static boolean supported() { + return true; + } + + public static void initialize() { + if(!hasInitialized) { + hasInitialized = true; + eaglercraftXClientSignature = loadClientData(grabEaglercraftXClientSignature()); + eaglercraftXClientBundle = loadClientData(grabEaglercraftXClientBundle()); + } + } + + private static byte[] loadClientData(String url) { + if (url == null) { + return null; + } + ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url); + if (buf == null) { + logger.error("Failed to download client bundle or signature URL!"); + return null; + } + return TeaVMUtils.wrapByteArrayBuffer(buf); + } + + public static byte[] getClientSignatureData() { + if(!hasInitialized) { + initialize(); + } + return eaglercraftXClientSignature; + } + + public static byte[] getClientBundleData() { + if(!hasInitialized) { + initialize(); + } + return eaglercraftXClientBundle; + } + + public static void startClientUpdateFrom(UpdateCertificate clientUpdate) { + if (updateThread == null || !updateThread.isAlive()) { + updateThread = new Thread(new TeaVMUpdateThread(clientUpdate, progressStruct), "EaglerUpdateThread"); + updateThread.setDaemon(true); + updateThread.start(); + } else { + logger.error("Tried to start a new download while the current download thread was still alive!"); + } + } + + public static UpdateProgressStruct getUpdatingStatus() { + return progressStruct; + } + + public static UpdateResultObj getUpdateResult() { + UpdateResultObj ret = updateResult; + if(ret != null) { + updateResult = null; + return ret; + }else { + return null; + } + } + + public static void setUpdateResultTeaVM(UpdateResultObj obj) { + updateResult = obj; + } + + public static void installSignedClient(UpdateCertificate clientCert, byte[] clientPayload, boolean setDefault, + boolean setTimeout) { + BootMenuEntryPoint.installSignedClientAtRuntime( + clientCert.bundleDisplayName + " " + clientCert.bundleDisplayVersion, PlatformRuntime.win, + clientCert.rawCertData, clientPayload, setDefault, setTimeout); + } + + public static void quine(String filename, byte[] cert, byte[] data, String date) { + EagRuntime.downloadFileWithName(filename, TeaVMUpdateThread.generateSignedOffline(cert, data, date)); + } + + public static void quine(UpdateCertificate clientUpdate, byte[] data) { + TeaVMUpdateThread.downloadSignedOffline(clientUpdate, data); + } +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java index 4f0947e..2b46dd8 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java @@ -1,439 +1,528 @@ -package net.lax1dude.eaglercraft.v1_8.internal; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelPeerState; -import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelReadyState; -import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelType; -import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; -import org.json.JSONObject; -import org.json.JSONWriter; -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.html.HTMLAudioElement; -import org.teavm.jso.dom.html.HTMLDocument; -import org.teavm.jso.json.JSON; -import org.teavm.jso.typedarrays.Uint8Array; -import org.teavm.jso.webaudio.*; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Copyright (c) 2022-2024 ayunami2000. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class PlatformVoiceClient { - - private static final Logger logger = LogManager.getLogger("PlatformVoiceClient"); - - private static final HashMap voiceAnalysers = new HashMap<>(); - private static final HashMap voiceGains = new HashMap<>(); - private static final HashMap voicePanners = new HashMap<>(); - - @JSBody(params = {}, script = "return typeof window.RTCPeerConnection !== \"undefined\" && typeof navigator.mediaDevices !== \"undefined\" && typeof navigator.mediaDevices.getUserMedia !== \"undefined\";") - public static native boolean isSupported(); - - @JSBody(params = { "item" }, script = "return item.streams[0];") - static native MediaStream getFirstStream(JSObject item); - - @JSBody(params = { "aud", "stream" }, script = "return aud.srcObject = stream;") - static native void setSrcObject(HTMLAudioElement aud, MediaStream stream); - - @JSBody(params = { "aud" }, script = "return aud.remove();") - static native void removeAud(HTMLAudioElement aud); - - @JSBody(params = { "pc", "stream" }, script = "return stream.getTracks().forEach((track) => { pc.addTrack(track, stream); });") - static native void addStream(JSObject pc, MediaStream stream); - - @JSBody(params = { "rawStream", "muted" }, script = "return rawStream.getAudioTracks()[0].enabled = !muted;") - static native void mute(MediaStream rawStream, boolean muted); - - @JSBody(params = { "peerConnection", "str" }, script = "return peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(str)));") - static native void addIceCandidate(JSObject peerConnection, String str); - - public static void disconnect(JSObject peerConnection) { - PlatformWebRTC.closeIt(peerConnection); - } - - public static void setVoiceProximity(int prox) { - for (PannerNode panner : voicePanners.values()) { - panner.setMaxDistance(VoiceClientController.getVoiceListenVolume() * 2 * prox + 0.1f); - } - } - - public static void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { - if (voicePanners.containsKey(uuid)) voicePanners.get(uuid).setPosition((float) x, (float) y, (float) z); - } - - public static class VoicePeer { - public final EaglercraftUUID peerId; - public final JSObject peerConnection; - public MediaStream rawStream; - public VoicePeer(EaglercraftUUID peerId, JSObject peerConnection, boolean offer) { - this.peerId = peerId; - this.peerConnection = peerConnection; - - TeaVMUtils.addEventListener(peerConnection, "icecandidate", (EventListener) evt -> { - if (PlatformWebRTC.hasCandidate(evt)) { - Map m = new HashMap<>(); - m.put("sdpMLineIndex", "" + PlatformWebRTC.getSdpMLineIndex(evt)); - m.put("candidate", PlatformWebRTC.getCandidate(evt)); - handleIceCandidate(peerId, JSONWriter.valueToString(m)); - } - }); - TeaVMUtils.addEventListener(peerConnection, "track", (EventListener) evt -> { - rawStream = getFirstStream(evt); - HTMLAudioElement aud = (HTMLAudioElement) HTMLDocument.current().createElement("audio"); - aud.setAutoplay(true); - aud.setMuted(true); - TeaVMUtils.addEventListener(aud, "ended", (EventListener) evt2 -> { - removeAud(aud); - }); - setSrcObject(aud, rawStream); - handlePeerTrack(peerId, rawStream); - }); - - addStream(peerConnection, localMediaStream.getStream()); - if (offer) { - PlatformWebRTC.createOffer(peerConnection, desc -> { - PlatformWebRTC.setLocalDescription(peerConnection, desc, () -> { - handleDescription(peerId, JSON.stringify(desc)); - }, err -> { - logger.error("Failed to set local description for \"{}\"! {}", peerId, err); - if (peerStateInitial == EnumVoiceChannelPeerState.LOADING) { - peerStateInitial = EnumVoiceChannelPeerState.FAILED; - } - signalDisconnect(peerId, false); - }); - }, err -> { - logger.error("Failed to set create offer for \"{}\"! {}", peerId, err); - if (peerStateInitial == EnumVoiceChannelPeerState.LOADING) { - peerStateInitial = EnumVoiceChannelPeerState.FAILED; - } - signalDisconnect(peerId, false); - }); - } - - TeaVMUtils.addEventListener(peerConnection, "connectionstatechange", (EventListener) evt -> { - String cs = PlatformWebRTC.getConnectionState(peerConnection); - if ("disconnected".equals(cs)) { - signalDisconnect(peerId, false); - } else if ("connected".equals(cs)) { - if (peerState != EnumVoiceChannelPeerState.SUCCESS) { - peerState = EnumVoiceChannelPeerState.SUCCESS; - } - } else if ("failed".equals(cs)) { - if (peerState == EnumVoiceChannelPeerState.LOADING) { - peerState = EnumVoiceChannelPeerState.FAILED; - } - signalDisconnect(peerId, false); - } - }); - } - - public void disconnect() { - PlatformVoiceClient.disconnect(peerConnection); - } - - public void mute(boolean muted) { - PlatformVoiceClient.mute(rawStream, muted); - } - - public void setRemoteDescription(String descJSON) { - try { - JSONObject remoteDesc = new JSONObject(descJSON); - PlatformWebRTC.setRemoteDescription2(peerConnection, descJSON, () -> { - if (remoteDesc.has("type") && "offer".equals(remoteDesc.getString("type"))) { - PlatformWebRTC.createAnswer(peerConnection, desc -> { - PlatformWebRTC.setLocalDescription(peerConnection, desc, () -> { - handleDescription(peerId, JSON.stringify(desc)); - if (peerStateDesc != EnumVoiceChannelPeerState.SUCCESS) peerStateDesc = EnumVoiceChannelPeerState.SUCCESS; - }, err -> { - logger.error("Failed to set local description for \"{}\"! {}", peerId, err.getMessage()); - if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; - signalDisconnect(peerId, false); - }); - }, err -> { - logger.error("Failed to create answer for \"{}\"! {}", peerId, err.getMessage()); - if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; - signalDisconnect(peerId, false); - }); - } - }, err -> { - logger.error("Failed to set remote description for \"{}\"! {}", peerId, err.getMessage()); - if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; - signalDisconnect(peerId, false); - }); - } catch (Throwable err) { - logger.error("Failed to parse remote description for \"{}\"! {}", peerId, err.getMessage()); - if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; - signalDisconnect(peerId, false); - } - } - - public void addICECandidate(String candidate) { - try { - addIceCandidate(peerConnection, candidate); - if (peerStateIce != EnumVoiceChannelPeerState.SUCCESS) peerStateIce = EnumVoiceChannelPeerState.SUCCESS; - } catch (Throwable err) { - logger.error("Failed to parse ice candidate for \"{}\"! {}", peerId, err.getMessage()); - if (peerStateIce == EnumVoiceChannelPeerState.LOADING) peerStateIce = EnumVoiceChannelPeerState.FAILED; - signalDisconnect(peerId, false); - } - } - } - - public static Set> iceServers = new HashSet<>(); - public static boolean hasInit = false; - public static Map peerList = new HashMap<>(); - public static MediaStreamAudioDestinationNode localMediaStream; - public static GainNode localMediaStreamGain; - public static MediaStream localRawMediaStream; - public static EnumVoiceChannelReadyState readyState = EnumVoiceChannelReadyState.NONE; - public static EnumVoiceChannelPeerState peerState = EnumVoiceChannelPeerState.LOADING; - public static EnumVoiceChannelPeerState peerStateConnect = EnumVoiceChannelPeerState.LOADING; - public static EnumVoiceChannelPeerState peerStateInitial = EnumVoiceChannelPeerState.LOADING; - public static EnumVoiceChannelPeerState peerStateDesc = EnumVoiceChannelPeerState.LOADING; - public static EnumVoiceChannelPeerState peerStateIce = EnumVoiceChannelPeerState.LOADING; - public static AudioContext microphoneVolumeAudioContext = null; - - public static void setICEServers(String[] urls) { - iceServers.clear(); - if (urls == null) return; - for (String url : urls) { - String[] etr = url.split(";"); - if (etr.length == 1) { - Map m = new HashMap<>(); - m.put("urls", etr[0]); - iceServers.add(m); - } else if (etr.length == 3) { - Map m = new HashMap<>(); - m.put("urls", etr[0]); - m.put("username", etr[1]); - m.put("credential", etr[2]); - iceServers.add(m); - } - } - } - - public static void activateVoice(boolean talk) { - if (hasInit) { - PlatformVoiceClient.mute(localRawMediaStream, !talk); - } - } - - public static void initializeDevices() { - if (!hasInit) { - localRawMediaStream = PlatformRuntime.getMic(); - if (localRawMediaStream == null) { - readyState = EnumVoiceChannelReadyState.ABORTED; - return; - } - microphoneVolumeAudioContext = AudioContext.create(); - mute(localRawMediaStream, true); - localMediaStream = microphoneVolumeAudioContext.createMediaStreamDestination(); - localMediaStreamGain = microphoneVolumeAudioContext.createGain(); - microphoneVolumeAudioContext.createMediaStreamSource(localRawMediaStream).connect(localMediaStreamGain); - localMediaStreamGain.connect(localMediaStream); - localMediaStreamGain.getGain().setValue(1.0F); - readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED; - hasInit = true; - } else { - readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED; - } - } - - public static void tickVoiceClient() { - for (EaglercraftUUID uuid : voiceAnalysers.keySet()) { - AnalyserNode analyser = voiceAnalysers.get(uuid); - Uint8Array array = Uint8Array.create(analyser.getFrequencyBinCount()); - analyser.getByteFrequencyData(array); - int len = array.getLength(); - for (int i = 0; i < len; i++) { - if (array.get(i) >= 0.1f) { - VoiceClientController.getVoiceSpeaking().add(uuid); - break; - } - } - } - } - - public static void setMicVolume(float val) { - if (hasInit) { - if(val > 0.5F) val = 0.5F + (val - 0.5F) * 2.0F; - if(val > 1.5F) val = 1.5F; - if(val < 0.0F) val = 0.0F; - localMediaStreamGain.getGain().setValue(val * 2.0F); - } - } - - public static void resetPeerStates() { - peerState = peerStateConnect = peerStateInitial = peerStateDesc = peerStateIce = EnumVoiceChannelPeerState.LOADING; - } - - public static EnumVoiceChannelPeerState getPeerState() { - return peerState; - } - - public static EnumVoiceChannelPeerState getPeerStateConnect() { - return peerStateConnect; - } - - public static EnumVoiceChannelPeerState getPeerStateInitial() { - return peerStateInitial; - } - - public static EnumVoiceChannelPeerState getPeerStateDesc() { - return peerStateDesc; - } - - public static EnumVoiceChannelPeerState getPeerStateIce() { - return peerStateIce; - } - - public static EnumVoiceChannelReadyState getReadyState() { - return readyState; - } - - public static void signalConnect(EaglercraftUUID peerId, boolean offer) { - if (!hasInit) initializeDevices(); - try { - JSObject peerConnection = PlatformWebRTC.createRTCPeerConnection(JSONWriter.valueToString(iceServers)); - VoicePeer peerInstance = new VoicePeer(peerId, peerConnection, offer); - peerList.put(peerId, peerInstance); - if (peerStateConnect != EnumVoiceChannelPeerState.SUCCESS) peerStateConnect = EnumVoiceChannelPeerState.SUCCESS; - } catch (Throwable e) { - if (peerStateConnect == EnumVoiceChannelPeerState.LOADING) peerStateConnect = EnumVoiceChannelPeerState.FAILED; - } - } - - public static void signalDescription(EaglercraftUUID peerId, String descJSON) { - VoicePeer peer = peerList.get(peerId); - if (peer != null) { - peer.setRemoteDescription(descJSON); - } - } - - public static void signalDisconnect(EaglercraftUUID peerId, boolean quiet) { - VoicePeer peer = peerList.get(peerId); - if (peer != null) { - peerList.remove(peerId, peer); - try { - peer.disconnect(); - } catch (Throwable ignored) {} - handlePeerDisconnect(peerId, quiet); - } - } - - public static void mutePeer(EaglercraftUUID peerId, boolean muted) { - VoicePeer peer = peerList.get(peerId); - if (peer != null) { - peer.mute(muted); - } - } - - public static void signalICECandidate(EaglercraftUUID peerId, String candidate) { - VoicePeer peer = peerList.get(peerId); - if (peer != null) { - peer.addICECandidate(candidate); - } - } - - public static void handleIceCandidate(EaglercraftUUID peerId, String candidate) { - VoiceClientController.sendPacketICE(peerId, candidate); - } - - public static void handleDescription(EaglercraftUUID peerId, String desc) { - VoiceClientController.sendPacketDesc(peerId, desc); - } - - public static void handlePeerTrack(EaglercraftUUID peerId, MediaStream audioStream) { - if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.NONE) return; - MediaStreamAudioSourceNode audioNode = PlatformAudio.audioctx.createMediaStreamSource(audioStream); - AnalyserNode analyser = PlatformAudio.audioctx.createAnalyser(); - analyser.setSmoothingTimeConstant(0f); - analyser.setFftSize(32); - audioNode.connect(analyser); - voiceAnalysers.put(peerId, analyser); - if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) { - GainNode gain = PlatformAudio.audioctx.createGain(); - gain.getGain().setValue(VoiceClientController.getVoiceListenVolume()); - analyser.connect(gain); - gain.connect(PlatformAudio.audioctx.getDestination()); - gain.connect(PlatformAudio.recDest); - voiceGains.put(peerId, gain); - VoiceClientController.getVoiceListening().add(peerId); - } else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) { - PannerNode panner = PlatformAudio.audioctx.createPanner(); - panner.setRolloffFactor(1f); - panner.setDistanceModel("linear"); - panner.setPanningModel("HRTF"); - panner.setConeInnerAngle(360f); - panner.setConeOuterAngle(0f); - panner.setConeOuterGain(0f); - panner.setOrientation(0f, 1f, 0f); - panner.setPosition(0, 0, 0); - float vol = VoiceClientController.getVoiceListenVolume(); - panner.setMaxDistance(vol * 2 * VoiceClientController.getVoiceProximity() + 0.1f); - GainNode gain = PlatformAudio.audioctx.createGain(); - gain.getGain().setValue(vol); - analyser.connect(gain); - gain.connect(panner); - panner.connect(PlatformAudio.audioctx.getDestination()); - panner.connect(PlatformAudio.recDest); - voiceGains.put(peerId, gain); - VoiceClientController.getVoiceListening().add(peerId); - voicePanners.put(peerId, panner); - } - if (VoiceClientController.getVoiceMuted().contains(peerId)) mutePeer(peerId, true); - } - - public static void handlePeerDisconnect(EaglercraftUUID peerId, boolean quiet) { - if (voiceAnalysers.containsKey(peerId)) { - voiceAnalysers.get(peerId).disconnect(); - voiceAnalysers.remove(peerId); - } - if (voiceGains.containsKey(peerId)) { - voiceGains.get(peerId).disconnect(); - voiceGains.remove(peerId); - VoiceClientController.getVoiceListening().remove(peerId); - } - if (voicePanners.containsKey(peerId)) { - voicePanners.get(peerId).disconnect(); - voicePanners.remove(peerId); - } - if (!quiet) { - VoiceClientController.sendPacketDisconnect(peerId); - } - } - - public static void setVoiceListenVolume(float f) { - for (EaglercraftUUID uuid : voiceGains.keySet()) { - GainNode gain = voiceGains.get(uuid); - float val = f; - if(val > 0.5f) val = 0.5f + (val - 0.5f) * 3.0f; - if(val > 2.0f) val = 2.0f; - if(val < 0.0f) val = 0.0f; - gain.getGain().setValue(val * 2.0f); - if (voicePanners.containsKey(uuid)) voicePanners.get(uuid).setMaxDistance(f * 2 * VoiceClientController.getVoiceProximity() + 0.1f); - } - } -} \ No newline at end of file +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelPeerState; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelReadyState; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelType; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import org.json.JSONObject; +import org.json.JSONWriter; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.html.HTMLAudioElement; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.json.JSON; +import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.webaudio.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Copyright (c) 2022-2024 ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformVoiceClient { + + private static final Logger logger = LogManager.getLogger("PlatformVoiceClient"); + + @JSBody(params = {}, script = "return typeof navigator.mediaDevices !== \"undefined\" && typeof navigator.mediaDevices.getUserMedia !== \"undefined\";") + private static native boolean isSupported0(); + + private static final int SRC_OBJECT_SUPPORT_NONE = -1; + private static final int SRC_OBJECT_SUPPORT_CORE = 0; + private static final int SRC_OBJECT_SUPPORT_MOZ = 1; + + static boolean hasCheckedSupport = false; + static boolean support = false; + static int srcObjectSupport = SRC_OBJECT_SUPPORT_NONE; + + public static boolean isSupported() { + if(!hasCheckedSupport) { + support = PlatformWebRTC.supported() && isSupported0(); + if(support) { + srcObjectSupport = checkSrcObjectSupport(PlatformRuntime.doc); + if(srcObjectSupport == SRC_OBJECT_SUPPORT_NONE) { + support = false; + }else if(srcObjectSupport == SRC_OBJECT_SUPPORT_MOZ) { + logger.info("Using moz- prefix for HTMLMediaElement.srcObject"); + } + } + hasCheckedSupport = true; + } + return support; + } + + @JSBody(params = { "item" }, script = "return item.streams[0];") + static native MediaStream getFirstStream(JSObject item); + + @JSBody(params = { "doc" }, script = "var aud = doc.createElement(\"audio\"); return (typeof aud.srcObject !== \"undefined\") ? 0 : ((typeof aud.mozSrcObject !== \"undefined\") ? 1 : -1);") + static native int checkSrcObjectSupport(HTMLDocument doc); + + static void setSrcObject(HTMLAudioElement aud, MediaStream stream) { + switch(srcObjectSupport) { + case SRC_OBJECT_SUPPORT_CORE: + setSrcObjectCore(aud, stream); + break; + case SRC_OBJECT_SUPPORT_MOZ: + setMozSrcObject(aud, stream); + break; + default: + throw new IllegalStateException(); + } + } + + @JSBody(params = { "aud", "stream" }, script = "return aud.srcObject = stream;") + private static native void setSrcObjectCore(HTMLAudioElement aud, MediaStream stream); + + @JSBody(params = { "aud", "stream" }, script = "return aud.mozSrcObject = stream;") + private static native void setMozSrcObject(HTMLAudioElement aud, MediaStream stream); + + @JSBody(params = { "pc", "stream" }, script = "return stream.getTracks().forEach(function(track) { pc.addTrack(track, stream); });") + static native void addStream(JSObject pc, MediaStream stream); + + @JSBody(params = { "rawStream", "muted" }, script = "return rawStream.getAudioTracks()[0].enabled = !muted;") + static native void mute(MediaStream rawStream, boolean muted); + + @JSBody(params = { "peerConnection", "str" }, script = "return peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(str)));") + static native void addCoreIceCandidate(JSObject peerConnection, String str); + + @JSBody(params = { "peerConnection", "str" }, script = "return peerConnection.addIceCandidate(new mozRTCIceCandidate(JSON.parse(str)));") + static native void addMozIceCandidate(JSObject peerConnection, String str); + + static void addIceCandidate(JSObject peerConnection, String str) { + if(!PlatformWebRTC.hasCheckedSupport) PlatformWebRTC.supported(); + switch(PlatformWebRTC.supportedImpl) { + case PlatformWebRTC.WEBRTC_SUPPORT_CORE: + case PlatformWebRTC.WEBRTC_SUPPORT_WEBKIT: + addCoreIceCandidate(peerConnection, str); + break; + case PlatformWebRTC.WEBRTC_SUPPORT_MOZ: + addMozIceCandidate(peerConnection, str); + break; + default: + throw new UnsupportedOperationException(); + } + } + + public static void disconnect(JSObject peerConnection) { + PlatformWebRTC.closeIt(peerConnection); + } + + public static void setVoiceProximity(int prox) { + for (VoicePeer player : peerList.values()) { + if(player.panner != null) { + player.panner.setMaxDistance(VoiceClientController.getVoiceListenVolume() * 2 * prox + 0.1f); + } + } + } + + public static void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { + VoicePeer player = peerList.get(uuid); + if (player != null && player.panner != null) player.panner.setPosition((float) x, (float) y, (float) z); + } + + public static class VoicePeer { + + public final EaglercraftUUID peerId; + public final JSObject peerConnection; + public MediaStream rawStream; + + private AnalyserNode analyser = null; + private GainNode gain = null; + private PannerNode panner = null; + private AudioNode recNode = null; + + public VoicePeer(EaglercraftUUID peerId, JSObject peerConnection, boolean offer) { + this.peerId = peerId; + this.peerConnection = peerConnection; + + TeaVMUtils.addEventListener(peerConnection, "icecandidate", (EventListener) evt -> { + if (PlatformWebRTC.hasCandidate(evt)) { + JSONObject m = new JSONObject(); + m.put("sdpMLineIndex", "" + PlatformWebRTC.getSdpMLineIndex(evt)); + m.put("candidate", PlatformWebRTC.getCandidate(evt)); + VoiceClientController.sendPacketICE(peerId, m.toString()); + } + }); + TeaVMUtils.addEventListener(peerConnection, "track", (EventListener) evt -> { + rawStream = getFirstStream(evt); + HTMLAudioElement aud = (HTMLAudioElement) PlatformRuntime.doc.createElement("audio"); + aud.setAutoplay(true); + aud.setMuted(true); + setSrcObject(aud, rawStream); + handlePeerTrack(this, rawStream); + }); + + addStream(peerConnection, localMediaStream.getStream()); + if (offer) { + PlatformWebRTC.createOffer(peerConnection, desc -> { + PlatformWebRTC.setLocalDescription(peerConnection, desc, () -> { + VoiceClientController.sendPacketDesc(peerId, JSON.stringify(desc)); + }, err -> { + logger.error("Failed to set local description for \"{}\"! {}", peerId, err); + if (peerStateInitial == EnumVoiceChannelPeerState.LOADING) { + peerStateInitial = EnumVoiceChannelPeerState.FAILED; + } + signalDisconnect(VoicePeer.this, false); + }); + }, err -> { + logger.error("Failed to set create offer for \"{}\"! {}", peerId, err); + if (peerStateInitial == EnumVoiceChannelPeerState.LOADING) { + peerStateInitial = EnumVoiceChannelPeerState.FAILED; + } + signalDisconnect(VoicePeer.this, false); + }); + } + + TeaVMUtils.addEventListener(peerConnection, "connectionstatechange", (EventListener) evt -> { + String cs = PlatformWebRTC.getConnectionState(peerConnection); + if ("disconnected".equals(cs)) { + signalDisconnect(VoicePeer.this, false); + } else if ("connected".equals(cs)) { + if (peerState != EnumVoiceChannelPeerState.SUCCESS) { + peerState = EnumVoiceChannelPeerState.SUCCESS; + } + } else if ("failed".equals(cs)) { + if (peerState == EnumVoiceChannelPeerState.LOADING) { + peerState = EnumVoiceChannelPeerState.FAILED; + } + signalDisconnect(VoicePeer.this, false); + } + }); + } + + public void disconnect() { + PlatformVoiceClient.disconnect(peerConnection); + } + + public void mute(boolean muted) { + PlatformVoiceClient.mute(rawStream, muted); + } + + public void setRemoteDescription(String descJSON) { + try { + JSONObject remoteDesc = new JSONObject(descJSON); + PlatformWebRTC.setRemoteDescription2(peerConnection, JSON.parse(descJSON), () -> { + if (remoteDesc.has("type") && "offer".equals(remoteDesc.getString("type"))) { + PlatformWebRTC.createAnswer(peerConnection, desc -> { + PlatformWebRTC.setLocalDescription(peerConnection, desc, () -> { + VoiceClientController.sendPacketDesc(peerId, JSON.stringify(desc)); + if (peerStateDesc != EnumVoiceChannelPeerState.SUCCESS) peerStateDesc = EnumVoiceChannelPeerState.SUCCESS; + }, err -> { + logger.error("Failed to set local description for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(VoicePeer.this, false); + }); + }, err -> { + logger.error("Failed to create answer for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(VoicePeer.this, false); + }); + } + }, err -> { + logger.error("Failed to set remote description for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(VoicePeer.this, false); + }); + } catch (Throwable err) { + logger.error("Failed to parse remote description for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(VoicePeer.this, false); + } + } + + public void addICECandidate(String candidate) { + try { + addIceCandidate(peerConnection, candidate); + if (peerStateIce != EnumVoiceChannelPeerState.SUCCESS) peerStateIce = EnumVoiceChannelPeerState.SUCCESS; + } catch (Throwable err) { + logger.error("Failed to parse ice candidate for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateIce == EnumVoiceChannelPeerState.LOADING) peerStateIce = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(VoicePeer.this, false); + } + } + } + + public static Set> iceServers = new HashSet<>(); + public static boolean hasInit = false; + public static final Map peerList = new HashMap<>(); + public static MediaStreamAudioDestinationNode localMediaStream; + public static GainNode localMediaStreamGain; + public static MediaStream localRawMediaStream; + public static EnumVoiceChannelReadyState readyState = EnumVoiceChannelReadyState.NONE; + public static EnumVoiceChannelPeerState peerState = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateConnect = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateInitial = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateDesc = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateIce = EnumVoiceChannelPeerState.LOADING; + public static AudioContext microphoneVolumeAudioContext = null; + + public static void setICEServers(String[] urls) { + iceServers.clear(); + if (urls == null) return; + for (String url : urls) { + String[] etr = url.split(";"); + if (etr.length == 1) { + Map m = new HashMap<>(); + m.put("urls", etr[0]); + iceServers.add(m); + } else if (etr.length == 3) { + Map m = new HashMap<>(); + m.put("urls", etr[0]); + m.put("username", etr[1]); + m.put("credential", etr[2]); + iceServers.add(m); + } + } + } + + public static void activateVoice(boolean talk) { + if (hasInit) { + PlatformVoiceClient.mute(localRawMediaStream, !talk); + } + } + + public static void initializeDevices() { + if (!hasInit) { + localRawMediaStream = PlatformScreenRecord.getMic(); + if (localRawMediaStream == null) { + readyState = EnumVoiceChannelReadyState.ABORTED; + return; + } + microphoneVolumeAudioContext = AudioContext.create(); + mute(localRawMediaStream, true); + localMediaStream = microphoneVolumeAudioContext.createMediaStreamDestination(); + localMediaStreamGain = microphoneVolumeAudioContext.createGain(); + microphoneVolumeAudioContext.createMediaStreamSource(localRawMediaStream).connect(localMediaStreamGain); + localMediaStreamGain.connect(localMediaStream); + localMediaStreamGain.getGain().setValue(1.0F); + readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED; + hasInit = true; + } else { + readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED; + } + } + + public static void tickVoiceClient() { + for (VoicePeer voicePlayer : peerList.values()) { + AnalyserNode analyser = voicePlayer.analyser; + if(analyser != null) { + Uint8Array array = Uint8Array.create(analyser.getFrequencyBinCount()); + analyser.getByteFrequencyData(array); + int len = array.getLength(); + for (int i = 0; i < len; i++) { + if (array.get(i) >= 0.1f) { + VoiceClientController.getVoiceSpeaking().add(voicePlayer.peerId); + break; + } + } + } + } + } + + public static void setMicVolume(float val) { + if (hasInit) { + if(val > 0.5F) val = 0.5F + (val - 0.5F) * 2.0F; + if(val > 1.5F) val = 1.5F; + if(val < 0.0F) val = 0.0F; + localMediaStreamGain.getGain().setValue(val * 2.0F); + } + } + + public static void resetPeerStates() { + peerState = peerStateConnect = peerStateInitial = peerStateDesc = peerStateIce = EnumVoiceChannelPeerState.LOADING; + } + + public static EnumVoiceChannelPeerState getPeerState() { + return peerState; + } + + public static EnumVoiceChannelPeerState getPeerStateConnect() { + return peerStateConnect; + } + + public static EnumVoiceChannelPeerState getPeerStateInitial() { + return peerStateInitial; + } + + public static EnumVoiceChannelPeerState getPeerStateDesc() { + return peerStateDesc; + } + + public static EnumVoiceChannelPeerState getPeerStateIce() { + return peerStateIce; + } + + public static EnumVoiceChannelReadyState getReadyState() { + return readyState; + } + + public static void signalConnect(EaglercraftUUID peerId, boolean offer) { + if (!hasInit) initializeDevices(); + try { + JSObject peerConnection = PlatformWebRTC.createRTCPeerConnection(JSONWriter.valueToString(iceServers)); + VoicePeer peerInstance = new VoicePeer(peerId, peerConnection, offer); + peerList.put(peerId, peerInstance); + if (peerStateConnect != EnumVoiceChannelPeerState.SUCCESS) peerStateConnect = EnumVoiceChannelPeerState.SUCCESS; + } catch (Throwable e) { + if (peerStateConnect == EnumVoiceChannelPeerState.LOADING) peerStateConnect = EnumVoiceChannelPeerState.FAILED; + } + } + + public static void signalDescription(EaglercraftUUID peerId, String descJSON) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peer.setRemoteDescription(descJSON); + } + } + + public static void signalDisconnect(EaglercraftUUID peerId, boolean quiet) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + signalDisconnect(peer, quiet); + } + } + + private static void signalDisconnect(VoicePeer peer, boolean quiet) { + peerList.remove(peer.peerId, peer); + try { + peer.disconnect(); + } catch (Throwable ignored) {} + handlePeerDisconnect(peer, quiet); + } + + public static void mutePeer(EaglercraftUUID peerId, boolean muted) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peer.mute(muted); + } + } + + public static void signalICECandidate(EaglercraftUUID peerId, String candidate) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peer.addICECandidate(candidate); + } + } + + private static void handlePeerTrack(VoicePeer peer, MediaStream audioStream) { + if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.NONE) return; + MediaStreamAudioSourceNode audioNode = PlatformAudio.audioctx.createMediaStreamSource(audioStream); + AnalyserNode analyser = PlatformAudio.audioctx.createAnalyser(); + analyser.setSmoothingTimeConstant(0f); + analyser.setFftSize(32); + audioNode.connect(analyser); + if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) { + GainNode gain = PlatformAudio.audioctx.createGain(); + gain.getGain().setValue(VoiceClientController.getVoiceListenVolume()); + audioNode.connect(gain); + gain.connect(PlatformAudio.audioctx.getDestination()); + if(PlatformAudio.gameRecGain != null) { + gain.connect(PlatformAudio.gameRecGain); + } + VoiceClientController.getVoiceListening().add(peer.peerId); + peer.analyser = analyser; + peer.gain = gain; + peer.recNode = gain; + } else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) { + PannerNode panner = PlatformAudio.audioctx.createPanner(); + panner.setRolloffFactor(1f); + panner.setDistanceModel("linear"); + panner.setPanningModel("HRTF"); + panner.setConeInnerAngle(360f); + panner.setConeOuterAngle(0f); + panner.setConeOuterGain(0f); + panner.setOrientation(0f, 1f, 0f); + panner.setPosition(0, 0, 0); + float vol = VoiceClientController.getVoiceListenVolume(); + panner.setMaxDistance(vol * 2 * VoiceClientController.getVoiceProximity() + 0.1f); + GainNode gain = PlatformAudio.audioctx.createGain(); + gain.getGain().setValue(vol); + audioNode.connect(gain); + gain.connect(panner); + panner.connect(PlatformAudio.audioctx.getDestination()); + if(PlatformAudio.gameRecGain != null) { + panner.connect(PlatformAudio.gameRecGain); + } + VoiceClientController.getVoiceListening().add(peer.peerId); + peer.analyser = analyser; + peer.panner = panner; + peer.gain = gain; + peer.recNode = panner; + } + if (VoiceClientController.getVoiceMuted().contains(peer.peerId)) mutePeer(peer.peerId, true); + } + + static void addRecordingDest(AudioNode destNode) { + for(VoicePeer peer : peerList.values()) { + if(peer.recNode != null) { + peer.recNode.connect(destNode); + } + } + } + + static void removeRecordingDest(AudioNode destNode) { + for(VoicePeer peer : peerList.values()) { + try { + if(peer.recNode != null) { + peer.recNode.disconnect(destNode); + } + }catch(Throwable t) { + } + } + } + + private static void handlePeerDisconnect(VoicePeer peer, boolean quiet) { + if(peer.analyser != null) { + peer.analyser.disconnect(); + peer.analyser = null; + } + if(peer.gain != null) { + peer.gain.disconnect(); + peer.gain = null; + } + if(peer.panner != null) { + peer.panner.disconnect(); + peer.panner = null; + } + VoiceClientController.getVoiceListening().remove(peer.peerId); + if (!quiet) { + VoiceClientController.sendPacketDisconnectPeer(peer.peerId); + } + } + + public static void setVoiceListenVolume(float f) { + for (VoicePeer peer : peerList.values()) { + if(peer.gain != null) { + float val = f; + if(val > 0.5f) val = 0.5f + (val - 0.5f) * 3.0f; + if(val > 2.0f) val = 2.0f; + if(val < 0.0f) val = 0.0f; + peer.gain.getGain().setValue(val * 2.0f); + } + if(peer.panner != null) { + peer.panner.setMaxDistance(f * 2 * VoiceClientController.getVoiceProximity() + 0.1f); + } + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java index 30f4443..78d3d57 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java @@ -1,17 +1,20 @@ package net.lax1dude.eaglercraft.v1_8.internal; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANPeerEvent; -import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQueryImpl; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQueryRateLimitDummy; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerRateLimitTracker; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocketImpl; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocketRateLimitDummy; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQuery; -import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.*; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQueryImpl; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQueryRateLimitDummy; import org.json.JSONObject; import org.json.JSONWriter; @@ -29,28 +32,19 @@ import org.teavm.jso.websocket.WebSocket; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; -import java.io.DataInputStream; -import java.io.IOException; import java.util.*; /** - * Copyright (c) 2022-2024 ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -59,23 +53,110 @@ public class PlatformWebRTC { private static final Logger logger = LogManager.getLogger("PlatformWebRTC"); - @JSBody(script = "return typeof window.RTCPeerConnection !== \"undefined\";") - public static native boolean supported(); + static final int WEBRTC_SUPPORT_NONE = 0; + static final int WEBRTC_SUPPORT_CORE = 1; + static final int WEBRTC_SUPPORT_WEBKIT = 2; + static final int WEBRTC_SUPPORT_MOZ = 3; - @JSBody(params = { "item" }, script = "return item.close();") + @JSBody(script = "return (typeof RTCPeerConnection !== \"undefined\") ? 1 : ((typeof webkitRTCPeerConnection !== \"undefined\") ? 2 : ((typeof mozRTCPeerConnection !== \"undefined\") ? 3 : 0));") + private static native int checkSupportedImpl(); + + static boolean hasCheckedSupport = false; + static int supportedImpl = WEBRTC_SUPPORT_NONE; + static boolean useSessionDescConstructor = false; + static boolean useOldConnStateCheck = false; + static boolean belowChrome71Fix = false; + + public static boolean supported() { + if(!hasCheckedSupport) { + supportedImpl = checkSupportedImpl(); + hasCheckedSupport = true; + if(supportedImpl == WEBRTC_SUPPORT_NONE) { + logger.error("WebRTC is not supported on this browser!"); + }else if(supportedImpl == WEBRTC_SUPPORT_WEBKIT) { + logger.info("Using webkit- prefix for RTCPeerConnection"); + }else if(supportedImpl == WEBRTC_SUPPORT_MOZ) { + logger.info("Using moz- prefix for RTCPeerConnection"); + } + if(supportedImpl != WEBRTC_SUPPORT_NONE) { + belowChrome71Fix = isChromeBelow71(); + if(belowChrome71Fix) { + logger.info("Note: Detected Chrome below version 71, stripping \"a=extmap-allow-mixed\" from the description SDP field"); + } + }else { + belowChrome71Fix = false; + } + } + return supportedImpl != WEBRTC_SUPPORT_NONE; + } + + @JSBody(params = { "item" }, script = "item.close();") static native void closeIt(JSObject item); @JSBody(params = { "item" }, script = "return item.readyState;") static native String getReadyState(JSObject item); - @JSBody(params = { "item", "buffer" }, script = "return item.send(buffer);") + @JSBody(params = { "item", "buffer" }, script = "item.send(buffer);") static native void sendIt(JSObject item, ArrayBuffer buffer); @JSBody(params = { "item" }, script = "return !!item.candidate;") static native boolean hasCandidate(JSObject item); - @JSBody(params = { "item" }, script = "return item.connectionState;") - static native String getConnectionState(JSObject item); + @JSBody(params = { "item" }, script = "return item.connectionState || \"\";") + private static native String getModernConnectionState(JSObject item); + + @JSBody(params = { "item" }, script = "return item.iceConnectionState;") + private static native String getICEConnectionState(JSObject item); + + @JSBody(params = { "item" }, script = "return item.signalingState;") + private static native String getSignalingState(JSObject item); + + static String getConnectionState(JSObject item) { + if(useOldConnStateCheck) { + return getConnectionStateLegacy(item); + }else { + String str = getModernConnectionState(item); + if(str.length() == 0) { + useOldConnStateCheck = true; + logger.info("Note: Using legacy connection state check using iceConnectionState+signalingState"); + return getConnectionStateLegacy(item); + }else { + return str; + } + } + } + + private static String getConnectionStateLegacy(JSObject item) { + String connState = getICEConnectionState(item); + switch(connState) { + case "new": + return "new"; + case "checking": + return "connecting"; + case "failed": + return "failed"; + case "disconnected": + return "disconnected"; + case "connected": + case "completed": + case "closed": + String signalState = getSignalingState(item); + switch(signalState) { + case "stable": + return "connected"; + case "have-local-offer": + case "have-remote-offer": + case "have-local-pranswer": + case "have-remote-pranswer": + return "connecting"; + case "closed": + default: + return "closed"; + } + default: + return "closed"; + } + } @JSBody(params = { "item" }, script = "return item.candidate.sdpMLineIndex;") static native int getSdpMLineIndex(JSObject item); @@ -83,14 +164,33 @@ public class PlatformWebRTC { @JSBody(params = { "item" }, script = "return item.candidate.candidate;") static native String getCandidate(JSObject item); - @JSBody(params = { - "iceServers" }, script = "return new RTCPeerConnection({ iceServers: JSON.parse(iceServers), optional: [ { DtlsSrtpKeyAgreement: true } ] });") - static native JSObject createRTCPeerConnection(String iceServers); + static JSObject createRTCPeerConnection(String iceServers) { + if(!hasCheckedSupport) supported(); + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + return createCoreRTCPeerConnection(iceServers); + case WEBRTC_SUPPORT_WEBKIT: + return createWebkitRTCPeerConnection(iceServers); + case WEBRTC_SUPPORT_MOZ: + return createMozRTCPeerConnection(iceServers); + default: + throw new UnsupportedOperationException(); + } + } + + @JSBody(params = { "iceServers" }, script = "return new RTCPeerConnection({ iceServers: JSON.parse(iceServers), optional: [ { DtlsSrtpKeyAgreement: true } ] });") + static native JSObject createCoreRTCPeerConnection(String iceServers); + + @JSBody(params = { "iceServers" }, script = "return new webkitRTCPeerConnection({ iceServers: JSON.parse(iceServers), optional: [ { DtlsSrtpKeyAgreement: true } ] });") + static native JSObject createWebkitRTCPeerConnection(String iceServers); + + @JSBody(params = { "iceServers" }, script = "return new mozRTCPeerConnection({ iceServers: JSON.parse(iceServers), optional: [ { DtlsSrtpKeyAgreement: true } ] });") + static native JSObject createMozRTCPeerConnection(String iceServers); @JSBody(params = { "peerConnection", "name" }, script = "return peerConnection.createDataChannel(name);") static native JSObject createDataChannel(JSObject peerConnection, String name); - @JSBody(params = { "item", "type" }, script = "return (item.binaryType = type);") + @JSBody(params = { "item", "type" }, script = "item.binaryType = type;") static native void setBinaryType(JSObject item, String type); @JSBody(params = { "item" }, script = "return item.data;") @@ -99,34 +199,139 @@ public class PlatformWebRTC { @JSBody(params = { "item" }, script = "return item.channel;") static native JSObject getChannel(JSObject item); - @JSBody(params = { "peerConnection", "h1", "h2" }, script = "return peerConnection.createOffer(h1, h2);") + @JSBody(params = { "peerConnection", "h1", "h2" }, script = "peerConnection.createOffer(h1, h2);") static native void createOffer(JSObject peerConnection, DescHandler h1, ErrorHandler h2); - @JSBody(params = { "peerConnection", "desc", "h1", - "h2" }, script = "return peerConnection.setLocalDescription(desc, h1, h2);") + @JSBody(params = { "peerConnection", "desc", "h1", "h2" }, script = "peerConnection.setLocalDescription(desc, h1, h2);") static native void setLocalDescription(JSObject peerConnection, JSObject desc, EmptyHandler h1, ErrorHandler h2); - @JSBody(params = { "peerConnection", - "str" }, script = "return peerConnection.setRemoteDescription(JSON.parse(str));") - static native void setRemoteDescription(JSObject peerConnection, String str); + @JSBody(params = { "peerConnection", "str" }, script = "var candidateList = JSON.parse(str); for (var i = 0; i < candidateList.length; ++i) { peerConnection.addIceCandidate(new RTCIceCandidate(candidateList[i])); }; return null;") + private static native void addCoreIceCandidates(JSObject peerConnection, String str); - @JSBody(params = { "peerConnection", - "str" }, script = "const candidateList = JSON.parse(str); for (let i = 0; i < candidateList.length; ++i) { peerConnection.addIceCandidate(candidateList[i]); }; return null;") - static native void addIceCandidates(JSObject peerConnection, String str); + @JSBody(params = { "peerConnection", "str" }, script = "var candidateList = JSON.parse(str); for (var i = 0; i < candidateList.length; ++i) { peerConnection.addIceCandidate(new mozRTCIceCandidate(candidateList[i])); }; return null;") + private static native void addMozIceCandidates(JSObject peerConnection, String str); - @JSBody(params = { "peerConnection", - "str" }, script = "const candidateList = JSON.parse(str); for (let i = 0; i < candidateList.length; ++i) { peerConnection.addIceCandidate(new RTCIceCandidate(candidateList[i])); }; return null;") - static native void addIceCandidates2(JSObject peerConnection, String str); + @JSBody(params = { }, script = "if(!navigator || !navigator.userAgent) return false;" + + "var ua = navigator.userAgent.toLowerCase();" + + "var i = ua.indexOf(\"chrome/\");" + + "if(i === -1) return false;" + + "i += 7;" + + "var j = ua.indexOf(\".\", i);" + + "if(j === -1 || j < i) j = ua.length;" + + "var versStr = ua.substring(i, j);" + + "versStr = parseInt(versStr);" + + "return !isNaN(versStr) && versStr < 71;") + private static native boolean isChromeBelow71(); - @JSBody(params = { "peerConnection", "str", "h1", - "h2" }, script = "return peerConnection.setRemoteDescription(JSON.parse(str), h1, h2);") - static native void setRemoteDescription2(JSObject peerConnection, String str, EmptyHandler h1, ErrorHandler h2); + static void addIceCandidates(JSObject peerConnection, String str) { + if(!hasCheckedSupport) supported(); + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + case WEBRTC_SUPPORT_WEBKIT: + addCoreIceCandidates(peerConnection, str); + break; + case WEBRTC_SUPPORT_MOZ: + addMozIceCandidates(peerConnection, str); + break; + default: + throw new UnsupportedOperationException(); + } + } - @JSBody(params = { "peerConnection", "h1", "h2" }, script = "return peerConnection.createAnswer(h1, h2);") + @JSBody(params = { "peerConnection", "str" }, script = "try { peerConnection.setRemoteDescription(str); return true; } catch(ex) { if(ex.name === \"TypeError\") return false; else throw ex; }") + private static native boolean setCoreRemoteDescription(JSObject peerConnection, JSObject str); + + @JSBody(params = { "peerConnection", "str" }, script = "peerConnection.setRemoteDescription(new RTCSessionDescription(str));") + private static native void setCoreRemoteDescriptionLegacy(JSObject peerConnection, JSObject str); + + @JSBody(params = { "peerConnection", "str" }, script = "peerConnection.setRemoteDescription(new mozRTCSessionDescription(str));") + private static native void setMozRemoteDescriptionLegacy(JSObject peerConnection, JSObject str); + + static void setRemoteDescription(JSObject peerConnection, JSObject str) { + if(!hasCheckedSupport) supported(); + if(belowChrome71Fix) { + removeExtmapAllowMixed(str); + } + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + if(useSessionDescConstructor) { + setCoreRemoteDescriptionLegacy(peerConnection, str); + }else { + if(!setCoreRemoteDescription(peerConnection, str)) { + useSessionDescConstructor = true; + logger.info("Note: Caught suspicious exception, using legacy RTCSessionDescription method"); + setCoreRemoteDescriptionLegacy(peerConnection, str); + } + } + break; + case WEBRTC_SUPPORT_WEBKIT: + setCoreRemoteDescriptionLegacy(peerConnection, str); + break; + case WEBRTC_SUPPORT_MOZ: + setMozRemoteDescriptionLegacy(peerConnection, str); + break; + default: + throw new UnsupportedOperationException(); + } + } + + @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "try { peerConnection.setRemoteDescription(str, h1, h2); return true; } catch(ex) { if(ex.name === \"TypeError\") return false; else throw ex; }") + private static native boolean setCoreRemoteDescription2(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + + @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "peerConnection.setRemoteDescription(new RTCSessionDescription(str), h1, h2);") + private static native void setCoreRemoteDescription2Legacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + + @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "peerConnection.setRemoteDescription(new mozRTCSessionDescription(str), h1, h2);") + private static native void setMozRemoteDescription2Legacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + + static void setRemoteDescription2(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2) { + if(!hasCheckedSupport) supported(); + if(belowChrome71Fix) { + removeExtmapAllowMixed(str); + } + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + if(useSessionDescConstructor) { + setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2); + }else { + if(!setCoreRemoteDescription2(peerConnection, str, h1, h2)) { + useSessionDescConstructor = true; + logger.info("Note: Caught suspicious exception, using legacy RTCSessionDescription method"); + setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2); + } + } + break; + case WEBRTC_SUPPORT_WEBKIT: + setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2); + break; + case WEBRTC_SUPPORT_MOZ: + setMozRemoteDescription2Legacy(peerConnection, str, h1, h2); + break; + default: + throw new UnsupportedOperationException(); + } + } + + @JSBody(params = { "objIn" }, script = "if(typeof objIn.sdp === \"string\"" + + "&& objIn.sdp.indexOf(\"a=extmap-allow-mixed\") !== -1) {" + + "objIn.sdp = objIn.sdp.split(\"\\n\").filter(function(line) {" + + "return line.trim() !== \"a=extmap-allow-mixed\";" + + "}).join(\"\\n\");" + + "}") + private static native void removeExtmapAllowMixed(JSObject objIn); + + @JSBody(params = { "peerConnection", "h1", "h2" }, script = "peerConnection.createAnswer(h1, h2);") static native void createAnswer(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") + static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); + private static final Map fuckTeaVM = new HashMap<>(); + public static void runScheduledTasks() { + + } + public static class LANClient { public static final byte READYSTATE_INIT_FAILED = -2; public static final byte READYSTATE_FAILED = -1; @@ -143,11 +348,17 @@ public class PlatformWebRTC { public void initialize() { try { if (dataChannel != null) { - closeIt(dataChannel); + try { + closeIt(dataChannel); + } catch (Throwable t) { + } dataChannel = null; } if (peerConnection != null) { - closeIt(peerConnection); + try { + closeIt(peerConnection); + } catch (Throwable t) { + } } this.peerConnection = createRTCPeerConnection(JSONWriter.valueToString(iceServers)); this.readyState = READYSTATE_CONNECTING; @@ -181,7 +392,7 @@ public class PlatformWebRTC { } catch (Throwable e) { signalRemoteDisconnect(false); } - } else { + }else { signalRemoteDisconnect(false); } } @@ -212,17 +423,17 @@ public class PlatformWebRTC { final Object[] evtHandler = new Object[1]; evtHandler[0] = (EventListener) evt -> { if (!iceCandidates.isEmpty()) { - Window.setTimeout(() -> ((EventListener) evtHandler[0]).handleEvent(evt), 1); + Window.setTimeout(() -> ((EventListener)evtHandler[0]).handleEvent(evt), 1); return; } clientDataChannelClosed = false; clientDataChannelOpen = true; }; - TeaVMUtils.addEventListener(dataChannel, "open", (EventListener) evtHandler[0]); + TeaVMUtils.addEventListener(dataChannel, "open", (EventListener)evtHandler[0]); TeaVMUtils.addEventListener(dataChannel, "message", (EventListener) evt -> { - synchronized (clientLANPacketBuffer) { + synchronized(clientLANPacketBuffer) { clientLANPacketBuffer.add(TeaVMUtils.wrapByteArrayBuffer(getData(evt))); } }); @@ -256,7 +467,7 @@ public class PlatformWebRTC { public void signalRemoteDescription(String json) { try { - setRemoteDescription(peerConnection, json); + setRemoteDescription(peerConnection, JSON.parse(json)); } catch (Throwable t) { EagRuntime.debugPrintStackTrace(t); readyState = READYSTATE_FAILED; @@ -282,8 +493,7 @@ public class PlatformWebRTC { if (peerConnection != null) { closeIt(peerConnection); } - if (!quiet) - clientDataChannelClosed = true; + if (!quiet) clientDataChannelClosed = true; readyState = READYSTATE_DISCONNECTED; } } @@ -309,9 +519,8 @@ public class PlatformWebRTC { if (iceCandidates.isEmpty()) { Window.setTimeout(() -> { if (peerConnection != null && !"disconnected".equals(getConnectionState(peerConnection))) { - LANPeerEvent.LANPeerICECandidateEvent e = new LANPeerEvent.LANPeerICECandidateEvent( - peerId, JSONWriter.valueToString(iceCandidates)); - synchronized (serverLANEventBuffer) { + LANPeerEvent.LANPeerICECandidateEvent e = new LANPeerEvent.LANPeerICECandidateEvent(peerId, JSONWriter.valueToString(iceCandidates)); + synchronized(serverLANEventBuffer) { serverLANEventBuffer.put(peerId, e); } iceCandidates.clear(); @@ -328,46 +537,42 @@ public class PlatformWebRTC { final Object[] evtHandler = new Object[1]; evtHandler[0] = (EventListener) evt -> { if (!iceCandidates.isEmpty()) { - Window.setTimeout(() -> ((EventListener) evtHandler[0]).handleEvent(evt), 1); + Window.setTimeout(() -> ((EventListener)evtHandler[0]).handleEvent(evt), 1); return; } - if (getChannel(evt) == null) - return; + if (getChannel(evt) == null) return; JSObject dataChannel = getChannel(evt); - synchronized (fuckTeaVM) { + synchronized(fuckTeaVM) { fuckTeaVM.put(peerId, dataChannel); } - synchronized (serverLANEventBuffer) { + synchronized(serverLANEventBuffer) { serverLANEventBuffer.put(peerId, new LANPeerEvent.LANPeerDataChannelEvent(peerId)); } TeaVMUtils.addEventListener(dataChannel, "message", (EventListener) evt2 -> { - LANPeerEvent.LANPeerPacketEvent e = new LANPeerEvent.LANPeerPacketEvent(peerId, - TeaVMUtils.wrapByteArrayBuffer(getData(evt2))); - synchronized (serverLANEventBuffer) { + LANPeerEvent.LANPeerPacketEvent e = new LANPeerEvent.LANPeerPacketEvent(peerId, TeaVMUtils.wrapByteArrayBuffer(getData(evt2))); + synchronized(serverLANEventBuffer) { serverLANEventBuffer.put(peerId, e); } }); }; - - TeaVMUtils.addEventListener(peerConnection, "datachannel", (EventListener) evtHandler[0]); + + TeaVMUtils.addEventListener(peerConnection, "datachannel", (EventListener)evtHandler[0]); TeaVMUtils.addEventListener(peerConnection, "connectionstatechange", (EventListener) evt -> { String connectionState = getConnectionState(peerConnection); if ("disconnected".equals(connectionState)) { client.signalRemoteDisconnect(peerId); } else if ("connected".equals(connectionState)) { - if (client.peerState != PEERSTATE_SUCCESS) - client.peerState = PEERSTATE_SUCCESS; + if (client.peerState != PEERSTATE_SUCCESS) client.peerState = PEERSTATE_SUCCESS; } else if ("failed".equals(connectionState)) { - if (client.peerState == PEERSTATE_LOADING) - client.peerState = PEERSTATE_FAILED; + if (client.peerState == PEERSTATE_LOADING) client.peerState = PEERSTATE_FAILED; client.signalRemoteDisconnect(peerId); } }); } public void disconnect() { - synchronized (fuckTeaVM) { + synchronized(fuckTeaVM) { if (fuckTeaVM.get(peerId) != null) { closeIt(fuckTeaVM.get(peerId)); fuckTeaVM.remove(peerId); @@ -379,54 +584,46 @@ public class PlatformWebRTC { public void setRemoteDescription(String descJSON) { try { JSONObject remoteDesc = new JSONObject(descJSON); - setRemoteDescription2(peerConnection, descJSON, () -> { + setRemoteDescription2(peerConnection, JSON.parse(descJSON), () -> { if (remoteDesc.has("type") && "offer".equals(remoteDesc.getString("type"))) { createAnswer(peerConnection, desc -> { setLocalDescription(peerConnection, desc, () -> { - LANPeerEvent.LANPeerDescriptionEvent e = new LANPeerEvent.LANPeerDescriptionEvent( - peerId, JSON.stringify(desc)); - synchronized (serverLANEventBuffer) { + LANPeerEvent.LANPeerDescriptionEvent e = new LANPeerEvent.LANPeerDescriptionEvent(peerId, JSON.stringify(desc)); + synchronized(serverLANEventBuffer) { serverLANEventBuffer.put(peerId, e); } - if (client.peerStateDesc != PEERSTATE_SUCCESS) - client.peerStateDesc = PEERSTATE_SUCCESS; + if (client.peerStateDesc != PEERSTATE_SUCCESS) client.peerStateDesc = PEERSTATE_SUCCESS; }, err -> { - logger.error("Failed to set local description for \"{}\"! {}", peerId, - err.getMessage()); - if (client.peerStateDesc == PEERSTATE_LOADING) - client.peerStateDesc = PEERSTATE_FAILED; + logger.error("Failed to set local description for \"{}\"! {}", peerId, TeaVMUtils.safeErrorMsgToString(err)); + if (client.peerStateDesc == PEERSTATE_LOADING) client.peerStateDesc = PEERSTATE_FAILED; client.signalRemoteDisconnect(peerId); }); }, err -> { - logger.error("Failed to create answer for \"{}\"! {}", peerId, err.getMessage()); - if (client.peerStateDesc == PEERSTATE_LOADING) - client.peerStateDesc = PEERSTATE_FAILED; + logger.error("Failed to create answer for \"{}\"! {}", peerId, TeaVMUtils.safeErrorMsgToString(err)); + if (client.peerStateDesc == PEERSTATE_LOADING) client.peerStateDesc = PEERSTATE_FAILED; client.signalRemoteDisconnect(peerId); }); } }, err -> { - logger.error("Failed to set remote description for \"{}\"! {}", peerId, err.getMessage()); - if (client.peerStateDesc == PEERSTATE_LOADING) - client.peerStateDesc = PEERSTATE_FAILED; + logger.error("Failed to set remote description for \"{}\"! {}", peerId, TeaVMUtils.safeErrorMsgToString(err)); + if (client.peerStateDesc == PEERSTATE_LOADING) client.peerStateDesc = PEERSTATE_FAILED; client.signalRemoteDisconnect(peerId); }); } catch (Throwable err) { logger.error("Failed to parse remote description for \"{}\"! {}", peerId, err.getMessage()); - if (client.peerStateDesc == PEERSTATE_LOADING) - client.peerStateDesc = PEERSTATE_FAILED; + logger.error(err); + if (client.peerStateDesc == PEERSTATE_LOADING) client.peerStateDesc = PEERSTATE_FAILED; client.signalRemoteDisconnect(peerId); } } public void addICECandidate(String candidates) { try { - addIceCandidates2(peerConnection, candidates); - if (client.peerStateIce != PEERSTATE_SUCCESS) - client.peerStateIce = PEERSTATE_SUCCESS; + addIceCandidates(peerConnection, candidates); + if (client.peerStateIce != PEERSTATE_SUCCESS) client.peerStateIce = PEERSTATE_SUCCESS; } catch (Throwable err) { logger.error("Failed to parse ice candidate for \"{}\"! {}", peerId, err.getMessage()); - if (client.peerStateIce == PEERSTATE_LOADING) - client.peerStateIce = PEERSTATE_FAILED; + if (client.peerStateIce == PEERSTATE_LOADING) client.peerStateIce = PEERSTATE_FAILED; client.signalRemoteDisconnect(peerId); } } @@ -463,9 +660,8 @@ public class PlatformWebRTC { LANPeer thePeer = this.peerList.get(peerId); if (thePeer != null) { boolean b = false; - synchronized (fuckTeaVM) { - if (fuckTeaVM.get(thePeer.peerId) != null - && "open".equals(getReadyState(fuckTeaVM.get(thePeer.peerId)))) { + synchronized(fuckTeaVM) { + if (fuckTeaVM.get(thePeer.peerId) != null && "open".equals(getReadyState(fuckTeaVM.get(thePeer.peerId)))) { try { sendIt(fuckTeaVM.get(thePeer.peerId), buffer); } catch (Throwable e) { @@ -475,7 +671,7 @@ public class PlatformWebRTC { b = true; } } - if (b) { + if(b) { signalRemoteDisconnect(peerId); } } @@ -490,11 +686,9 @@ public class PlatformWebRTC { JSObject peerConnection = createRTCPeerConnection(JSONWriter.valueToString(iceServers)); LANPeer peerInstance = new LANPeer(this, peerId, peerConnection); peerList.put(peerId, peerInstance); - if (peerStateConnect != PEERSTATE_SUCCESS) - peerStateConnect = PEERSTATE_SUCCESS; + if (peerStateConnect != PEERSTATE_SUCCESS) peerStateConnect = PEERSTATE_SUCCESS; } catch (Throwable e) { - if (peerStateConnect == PEERSTATE_LOADING) - peerStateConnect = PEERSTATE_FAILED; + if (peerStateConnect == PEERSTATE_LOADING) peerStateConnect = PEERSTATE_FAILED; } } @@ -518,31 +712,28 @@ public class PlatformWebRTC { if (thePeer != null) { try { thePeer.disconnect(); - } catch (Throwable ignored) { - } - synchronized (serverLANEventBuffer) { - serverLANEventBuffer.put(thePeer.peerId, - new LANPeerEvent.LANPeerDisconnectEvent(thePeer.peerId)); + } catch (Throwable ignored) {} + synchronized(serverLANEventBuffer) { + serverLANEventBuffer.put(thePeer.peerId, new LANPeerEvent.LANPeerDisconnectEvent(thePeer.peerId)); } } } peerList.clear(); - synchronized (fuckTeaVM) { + synchronized(fuckTeaVM) { fuckTeaVM.clear(); } return; } LANPeer thePeer = peerList.get(peerId); - if (thePeer != null) { + if(thePeer != null) { peerList.remove(peerId); try { thePeer.disconnect(); - } catch (Throwable ignored) { - } - synchronized (fuckTeaVM) { + } catch (Throwable ignored) {} + synchronized(fuckTeaVM) { fuckTeaVM.remove(peerId); } - synchronized (serverLANEventBuffer) { + synchronized(serverLANEventBuffer) { serverLANEventBuffer.put(thePeer.peerId, new LANPeerEvent.LANPeerDisconnectEvent(peerId)); } } @@ -568,721 +759,27 @@ public class PlatformWebRTC { void call(JSError err); } - @JSBody(params = { "obj" }, script = "return typeof obj === \"string\";") - private static native boolean isString(JSObject obj); - - private static final Map relayQueryLimited = new HashMap<>(); - private static final Map relayQueryBlocked = new HashMap<>(); - - private static class RelayQueryImpl implements RelayQuery { - - private final WebSocket sock; - private final String uri; - - private boolean open; - private boolean failed; - - private boolean hasRecievedAnyData = false; - - private int vers = -1; - private String comment = ""; - private String brand = ""; - - private long connectionOpenedAt; - private long connectionPingStart = -1; - private long connectionPingTimer = -1; - - private RateLimit rateLimitStatus = RateLimit.NONE; - - private VersionMismatch versError = VersionMismatch.UNKNOWN; - - private RelayQueryImpl(String uri) { - this.uri = uri; - WebSocket s; - try { - connectionOpenedAt = System.currentTimeMillis(); - s = WebSocket.create(uri); - s.setBinaryType("arraybuffer"); - open = true; - failed = false; - } catch (Throwable t) { - connectionOpenedAt = 0l; - sock = null; - open = false; - failed = true; - return; - } - sock = s; - sock.onOpen(evt -> { - try { - connectionPingStart = System.currentTimeMillis(); - PlatformNetworking.nativeBinarySend(sock, TeaVMUtils.unwrapArrayBuffer( - IPacket.writePacket(new IPacket00Handshake(0x03, RelayManager.preferredRelayVersion, "")))); - } catch (IOException e) { - logger.error(e.toString()); - sock.close(); - failed = true; - } - }); - sock.onMessage(evt -> { - if (evt.getData() != null && !isString(evt.getData())) { - hasRecievedAnyData = true; - byte[] arr = TeaVMUtils.wrapByteArrayBuffer(evt.getDataAsArray()); - if (arr.length == 2 && arr[0] == (byte) 0xFC) { - long millis = System.currentTimeMillis(); - if (arr[1] == (byte) 0x00 || arr[1] == (byte) 0x01) { - rateLimitStatus = RateLimit.BLOCKED; - relayQueryLimited.put(RelayQueryImpl.this.uri, millis); - } else if (arr[1] == (byte) 0x02) { - rateLimitStatus = RateLimit.NOW_LOCKED; - relayQueryLimited.put(RelayQueryImpl.this.uri, millis); - relayQueryBlocked.put(RelayQueryImpl.this.uri, millis); - } else { - rateLimitStatus = RateLimit.LOCKED; - relayQueryBlocked.put(RelayQueryImpl.this.uri, millis); - } - failed = true; - open = false; - sock.close(); - } else { - if (open) { - try { - IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); - if (pkt instanceof IPacket69Pong) { - IPacket69Pong ipkt = (IPacket69Pong) pkt; - versError = VersionMismatch.COMPATIBLE; - if (connectionPingTimer == -1) { - connectionPingTimer = System.currentTimeMillis() - connectionPingStart; - } - vers = ipkt.protcolVersion; - comment = ipkt.comment; - brand = ipkt.brand; - open = false; - failed = false; - sock.close(); - } else if (pkt instanceof IPacket70SpecialUpdate) { - IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate) pkt; - if (ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { - UpdateService.addCertificateToSet(ipkt.updatePacket); - } - } else if (pkt instanceof IPacketFFErrorCode) { - IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; - if (ipkt.code == IPacketFFErrorCode.TYPE_PROTOCOL_VERSION) { - String s1 = ipkt.desc.toLowerCase(); - if (s1.contains("outdated client") || s1.contains("client outdated")) { - versError = VersionMismatch.CLIENT_OUTDATED; - } else if (s1.contains("outdated server") || s1.contains("server outdated") || - s1.contains("outdated relay") || s1.contains("server relay")) { - versError = VersionMismatch.RELAY_OUTDATED; - } else { - versError = VersionMismatch.UNKNOWN; - } - } - logger.error("{}\": Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc); - open = false; - failed = true; - sock.close(); - } else { - throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'"); - } - } catch (IOException e) { - logger.error("Relay Query Error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - open = false; - failed = true; - sock.close(); - } - } - } - } - }); - sock.onClose(evt -> { - open = false; - if (!hasRecievedAnyData) { - failed = true; - Long l = relayQueryBlocked.get(uri); - if (l != null) { - if (System.currentTimeMillis() - l.longValue() < 400000l) { - rateLimitStatus = RateLimit.LOCKED; - return; - } - } - l = relayQueryLimited.get(uri); - if (l != null) { - if (System.currentTimeMillis() - l.longValue() < 900000l) { - rateLimitStatus = RateLimit.BLOCKED; - return; - } - } - } - }); - } - - @Override - public boolean isQueryOpen() { - return open; - } - - @Override - public boolean isQueryFailed() { - return failed; - } - - @Override - public RateLimit isQueryRateLimit() { - return rateLimitStatus; - } - - @Override - public void close() { - if (sock != null && open) { - sock.close(); - } - open = false; - } - - @Override - public int getVersion() { - return vers; - } - - @Override - public String getComment() { - return comment; - } - - @Override - public String getBrand() { - return brand; - } - - @Override - public long getPing() { - return connectionPingTimer < 1 ? 1 : connectionPingTimer; - } - - @Override - public VersionMismatch getCompatible() { - return versError; - } - - } - - private static class RelayQueryRatelimitDummy implements RelayQuery { - - private final RateLimit type; - - private RelayQueryRatelimitDummy(RateLimit type) { - this.type = type; - } - - @Override - public boolean isQueryOpen() { - return false; - } - - @Override - public boolean isQueryFailed() { - return true; - } - - @Override - public RateLimit isQueryRateLimit() { - return type; - } - - @Override - public void close() { - } - - @Override - public int getVersion() { - return RelayManager.preferredRelayVersion; - } - - @Override - public String getComment() { - return "this query was rate limited"; - } - - @Override - public String getBrand() { - return "lax1dude"; - } - - @Override - public long getPing() { - return 0l; - } - - @Override - public VersionMismatch getCompatible() { - return VersionMismatch.COMPATIBLE; - } - - } - public static RelayQuery openRelayQuery(String addr) { - long millis = System.currentTimeMillis(); - - Long l = relayQueryBlocked.get(addr); - if (l != null && millis - l.longValue() < 60000l) { - return new RelayQueryRatelimitDummy(RelayQuery.RateLimit.LOCKED); + RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr); + if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) { + return new RelayQueryRateLimitDummy(limit); } - - l = relayQueryLimited.get(addr); - if (l != null && millis - l.longValue() < 10000l) { - return new RelayQueryRatelimitDummy(RelayQuery.RateLimit.BLOCKED); - } - return new RelayQueryImpl(addr); } - private static class RelayWorldsQueryImpl implements RelayWorldsQuery { - - private final WebSocket sock; - private final String uri; - - private boolean open; - private boolean failed; - - private boolean hasRecievedAnyData = false; - private RelayQuery.RateLimit rateLimitStatus = RelayQuery.RateLimit.NONE; - - private RelayQuery.VersionMismatch versError = RelayQuery.VersionMismatch.UNKNOWN; - - private List worlds = null; - - private RelayWorldsQueryImpl(String uri) { - this.uri = uri; - WebSocket s; - try { - s = WebSocket.create(uri); - s.setBinaryType("arraybuffer"); - open = true; - failed = false; - } catch (Throwable t) { - sock = null; - open = false; - failed = true; - return; - } - sock = s; - sock.onOpen(evt -> { - try { - PlatformNetworking.nativeBinarySend(sock, TeaVMUtils.unwrapArrayBuffer( - IPacket.writePacket(new IPacket00Handshake(0x04, RelayManager.preferredRelayVersion, "")))); - } catch (IOException e) { - logger.error(e.toString()); - sock.close(); - open = false; - failed = true; - } - }); - sock.onMessage(evt -> { - if (evt.getData() != null && !isString(evt.getData())) { - hasRecievedAnyData = true; - byte[] arr = TeaVMUtils.wrapByteArrayBuffer(evt.getDataAsArray()); - if (arr.length == 2 && arr[0] == (byte) 0xFC) { - long millis = System.currentTimeMillis(); - if (arr[1] == (byte) 0x00 || arr[1] == (byte) 0x01) { - rateLimitStatus = RelayQuery.RateLimit.BLOCKED; - relayQueryLimited.put(RelayWorldsQueryImpl.this.uri, millis); - } else if (arr[1] == (byte) 0x02) { - rateLimitStatus = RelayQuery.RateLimit.NOW_LOCKED; - relayQueryLimited.put(RelayWorldsQueryImpl.this.uri, millis); - relayQueryBlocked.put(RelayWorldsQueryImpl.this.uri, millis); - } else { - rateLimitStatus = RelayQuery.RateLimit.LOCKED; - relayQueryBlocked.put(RelayWorldsQueryImpl.this.uri, millis); - } - open = false; - failed = true; - sock.close(); - } else { - if (open) { - try { - IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); - if (pkt instanceof IPacket07LocalWorlds) { - worlds = ((IPacket07LocalWorlds) pkt).worldsList; - sock.close(); - open = false; - failed = false; - } else if (pkt instanceof IPacket70SpecialUpdate) { - IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate) pkt; - if (ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { - UpdateService.addCertificateToSet(ipkt.updatePacket); - } - } else if (pkt instanceof IPacketFFErrorCode) { - IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; - if (ipkt.code == IPacketFFErrorCode.TYPE_PROTOCOL_VERSION) { - String s1 = ipkt.desc.toLowerCase(); - if (s1.contains("outdated client") || s1.contains("client outdated")) { - versError = RelayQuery.VersionMismatch.CLIENT_OUTDATED; - } else if (s1.contains("outdated server") || s1.contains("server outdated") || - s1.contains("outdated relay") || s1.contains("server relay")) { - versError = RelayQuery.VersionMismatch.RELAY_OUTDATED; - } else { - versError = RelayQuery.VersionMismatch.UNKNOWN; - } - } - logger.error("{}: Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc); - open = false; - failed = true; - sock.close(); - } else { - throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'"); - } - } catch (IOException e) { - logger.error("Relay World Query Error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - open = false; - failed = true; - sock.close(); - } - } - } - } - }); - sock.onClose(evt -> { - open = false; - if (!hasRecievedAnyData) { - failed = true; - Long l = relayQueryBlocked.get(uri); - if (l != null) { - if (System.currentTimeMillis() - l.longValue() < 400000l) { - rateLimitStatus = RelayQuery.RateLimit.LOCKED; - return; - } - } - l = relayQueryLimited.get(uri); - if (l != null) { - if (System.currentTimeMillis() - l.longValue() < 900000l) { - rateLimitStatus = RelayQuery.RateLimit.BLOCKED; - return; - } - } - } - }); - } - - @Override - public boolean isQueryOpen() { - return open; - } - - @Override - public boolean isQueryFailed() { - return failed; - } - - @Override - public RelayQuery.RateLimit isQueryRateLimit() { - return rateLimitStatus; - } - - @Override - public void close() { - if (open && sock != null) { - sock.close(); - } - open = false; - } - - @Override - public List getWorlds() { - return worlds; - } - - @Override - public RelayQuery.VersionMismatch getCompatible() { - return versError; - } - - } - - private static class RelayWorldsQueryRatelimitDummy implements RelayWorldsQuery { - - private final RelayQuery.RateLimit rateLimit; - - private RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit rateLimit) { - this.rateLimit = rateLimit; - } - - @Override - public boolean isQueryOpen() { - return false; - } - - @Override - public boolean isQueryFailed() { - return true; - } - - @Override - public RelayQuery.RateLimit isQueryRateLimit() { - return rateLimit; - } - - @Override - public void close() { - } - - @Override - public List getWorlds() { - return new ArrayList(0); - } - - @Override - public RelayQuery.VersionMismatch getCompatible() { - return RelayQuery.VersionMismatch.COMPATIBLE; - } - } - public static RelayWorldsQuery openRelayWorldsQuery(String addr) { - long millis = System.currentTimeMillis(); - - Long l = relayQueryBlocked.get(addr); - if (l != null && millis - l.longValue() < 60000l) { - return new RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit.LOCKED); + RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr); + if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) { + return new RelayWorldsQueryRateLimitDummy(limit); } - - l = relayQueryLimited.get(addr); - if (l != null && millis - l.longValue() < 10000l) { - return new RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit.BLOCKED); - } - return new RelayWorldsQueryImpl(addr); } - private static class RelayServerSocketImpl implements RelayServerSocket { - - private final WebSocket sock; - private final String uri; - - private boolean open; - private boolean closed; - private boolean failed; - - private boolean hasRecievedAnyData; - - private final List exceptions = new LinkedList(); - private final List packets = new LinkedList(); - - private RelayServerSocketImpl(String uri, int timeout) { - this.uri = uri; - WebSocket s; - try { - s = WebSocket.create(uri); - s.setBinaryType("arraybuffer"); - open = false; - closed = false; - failed = false; - } catch (Throwable t) { - exceptions.add(t); - sock = null; - open = false; - closed = true; - failed = true; - return; - } - sock = s; - sock.onOpen(evt -> open = true); - sock.onMessage(evt -> { - if (evt.getData() != null && !isString(evt.getData())) { - hasRecievedAnyData = true; - try { - IPacket pkt = IPacket.readPacket(new DataInputStream( - new EaglerInputStream(TeaVMUtils.wrapByteArrayBuffer(evt.getDataAsArray())))); - if (pkt instanceof IPacket70SpecialUpdate) { - IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate) pkt; - if (ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) { - UpdateService.addCertificateToSet(ipkt.updatePacket); - } - } else { - packets.add(pkt); - } - } catch (IOException e) { - exceptions.add(e); - logger.error("Relay Socket Error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - open = false; - failed = true; - closed = true; - sock.close(); - } - } - }); - sock.onClose(evt -> { - if (!hasRecievedAnyData) { - failed = true; - } - open = false; - closed = true; - }); - Window.setTimeout(() -> { - if (!open && !closed) { - closed = true; - sock.close(); - } - }, timeout); - } - - @Override - public boolean isOpen() { - return open; - } - - @Override - public boolean isClosed() { - return closed; - } - - @Override - public void close() { - if (open && sock != null) { - sock.close(); - } - open = false; - closed = true; - } - - @Override - public boolean isFailed() { - return failed; - } - - @Override - public Throwable getException() { - if (!exceptions.isEmpty()) { - return exceptions.remove(0); - } else { - return null; - } - } - - @Override - public void writePacket(IPacket pkt) { - try { - PlatformNetworking.nativeBinarySend(sock, TeaVMUtils.unwrapArrayBuffer(IPacket.writePacket(pkt))); - } catch (Throwable e) { - logger.error("Relay connection error: {}", e.toString()); - EagRuntime.debugPrintStackTrace(e); - exceptions.add(e); - failed = true; - open = false; - closed = true; - sock.close(); - } - } - - @Override - public IPacket readPacket() { - if (!packets.isEmpty()) { - return packets.remove(0); - } else { - return null; - } - } - - @Override - public IPacket nextPacket() { - if (!packets.isEmpty()) { - return packets.get(0); - } else { - return null; - } - } - - @Override - public RelayQuery.RateLimit getRatelimitHistory() { - if (relayQueryBlocked.containsKey(uri)) { - return RelayQuery.RateLimit.LOCKED; - } - if (relayQueryLimited.containsKey(uri)) { - return RelayQuery.RateLimit.BLOCKED; - } - return RelayQuery.RateLimit.NONE; - } - - @Override - public String getURI() { - return uri; - } - - } - - private static class RelayServerSocketRatelimitDummy implements RelayServerSocket { - - private final RelayQuery.RateLimit limit; - - private RelayServerSocketRatelimitDummy(RelayQuery.RateLimit limit) { - this.limit = limit; - } - - @Override - public boolean isOpen() { - return false; - } - - @Override - public boolean isClosed() { - return true; - } - - @Override - public void close() { - } - - @Override - public boolean isFailed() { - return true; - } - - @Override - public Throwable getException() { - return null; - } - - @Override - public void writePacket(IPacket pkt) { - } - - @Override - public IPacket readPacket() { - return null; - } - - @Override - public IPacket nextPacket() { - return null; - } - - @Override - public RelayQuery.RateLimit getRatelimitHistory() { - return limit; - } - - @Override - public String getURI() { - return ""; - } - - } - public static RelayServerSocket openRelayConnection(String addr, int timeout) { - long millis = System.currentTimeMillis(); - - Long l = relayQueryBlocked.get(addr); - if (l != null && millis - l.longValue() < 60000l) { - return new RelayServerSocketRatelimitDummy(RelayQuery.RateLimit.LOCKED); + RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr); + if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) { + return new RelayServerSocketRateLimitDummy(limit); } - - l = relayQueryLimited.get(addr); - if (l != null && millis - l.longValue() < 10000l) { - return new RelayServerSocketRatelimitDummy(RelayQuery.RateLimit.BLOCKED); - } - return new RelayServerSocketImpl(addr, timeout); } @@ -1315,18 +812,18 @@ public class PlatformWebRTC { } public static byte[] clientLANReadPacket() { - synchronized (clientLANPacketBuffer) { + synchronized(clientLANPacketBuffer) { return !clientLANPacketBuffer.isEmpty() ? clientLANPacketBuffer.remove(0) : null; } } public static List clientLANReadAllPacket() { - synchronized (clientLANPacketBuffer) { - if (!clientLANPacketBuffer.isEmpty()) { - List ret = new ArrayList(clientLANPacketBuffer); + synchronized(clientLANPacketBuffer) { + if(!clientLANPacketBuffer.isEmpty()) { + List ret = new ArrayList<>(clientLANPacketBuffer); clientLANPacketBuffer.clear(); return ret; - } else { + }else { return null; } } @@ -1334,8 +831,7 @@ public class PlatformWebRTC { public static void clientLANSetICEServersAndConnect(String[] servers) { rtcLANClient.setIceServers(servers); - if (clientLANReadyState() == LANClient.READYSTATE_CONNECTED - || clientLANReadyState() == LANClient.READYSTATE_CONNECTING) { + if(clientLANReadyState() == LANClient.READYSTATE_CONNECTED || clientLANReadyState() == LANClient.READYSTATE_CONNECTING) { rtcLANClient.signalRemoteDisconnect(true); } rtcLANClient.initialize(); @@ -1350,30 +846,30 @@ public class PlatformWebRTC { } public static String clientLANAwaitICECandidate() { - if (clientICECandidate != null) { + if(clientICECandidate != null) { String ret = clientICECandidate; clientICECandidate = null; return ret; - } else { + }else { return null; } } public static String clientLANAwaitDescription() { - if (clientDescription != null) { + if(clientDescription != null) { String ret = clientDescription; clientDescription = null; return ret; - } else { + }else { return null; } } public static boolean clientLANAwaitChannel() { - if (clientDataChannelOpen) { + if(clientDataChannelOpen) { clientDataChannelOpen = false; return true; - } else { + }else { return false; } } @@ -1401,7 +897,7 @@ public class PlatformWebRTC { private static final ListMultimap serverLANEventBuffer = LinkedListMultimap.create(); public static void serverLANInitializeServer(String[] servers) { - synchronized (serverLANEventBuffer) { + synchronized(serverLANEventBuffer) { serverLANEventBuffer.clear(); } rtcLANServer.resetPeerStates(); @@ -1413,10 +909,10 @@ public class PlatformWebRTC { } public static LANPeerEvent serverLANGetEvent(String clientId) { - synchronized (serverLANEventBuffer) { - if (!serverLANEventBuffer.isEmpty()) { + synchronized(serverLANEventBuffer) { + if(!serverLANEventBuffer.isEmpty()) { List l = serverLANEventBuffer.get(clientId); - if (!l.isEmpty()) { + if(!l.isEmpty()) { return l.remove(0); } } @@ -1425,10 +921,10 @@ public class PlatformWebRTC { } public static List serverLANGetAllEvent(String clientId) { - synchronized (serverLANEventBuffer) { - if (!serverLANEventBuffer.isEmpty()) { + synchronized(serverLANEventBuffer) { + if(!serverLANEventBuffer.isEmpty()) { List l = serverLANEventBuffer.removeAll(clientId); - if (l.isEmpty()) { + if(l.isEmpty()) { return null; } return l; @@ -1463,4 +959,5 @@ public class PlatformWebRTC { } return rtcLANServer.countPeers(); } + } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebView.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebView.java new file mode 100755 index 0000000..e0400ae --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebView.java @@ -0,0 +1,639 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; +import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.css.CSSStyleDeclaration; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.events.MessageEvent; +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLInputElement; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.AdvancedHTMLIFrameElement; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.IFrameSafetyException; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageEnV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG; +import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlatformWebView { + + private static final Logger logger = LogManager.getLogger("PlatformWebView"); + + private static boolean supportKnown = false; + private static boolean supportForce = false; + private static boolean enableCSP = true; + private static boolean supported = false; + private static boolean cspSupport = false; + + private static HTMLElement currentIFrameContainer = null; + private static HTMLElement currentAllowJavaScript = null; + + private static AdvancedHTMLIFrameElement currentIFrame = null; + private static WebViewOptions currentOptions = null; + + private static int webviewResetSerial = 0; + + private static String currentMessageChannelName = null; + + private static Window win; + private static HTMLElement rootElement; + + private static Consumer currentMessageHandler = null; + + private static final List messageQueue = new LinkedList<>(); + + private static IPacketSendCallback packetSendCallback = null; + + static void initRoot(Window win, HTMLElement rootElement) { + PlatformWebView.win = win; + PlatformWebView.rootElement = rootElement; + } + + public static boolean supported() { + if(!supportKnown) { + IClientConfigAdapter cfg = PlatformRuntime.getClientConfigAdapter(); + supportForce = cfg.isForceWebViewSupport(); + enableCSP = cfg.isEnableWebViewCSP(); + if(supportForce) { + supported = true; + cspSupport = true; + }else { + supported = false; + cspSupport = false; + try { + AdvancedHTMLIFrameElement tmp = (AdvancedHTMLIFrameElement)win.getDocument().createElement("iframe"); + supported = tmp != null && tmp.checkSafetyFeaturesSupported(); + cspSupport = enableCSP && supported && tmp.checkCSPSupported(); + }catch(Throwable ex) { + logger.error("Error checking iframe support"); + logger.error(ex); + } + } + if(!supported) { + logger.error("This browser does not meet the safety requirements for webview support, this feature will be disabled"); + }else if(!cspSupport && enableCSP) { + logger.warn("This browser does not support CSP attribute on iframes! (try Chrome)"); + } + supportKnown = true; + } + return supported; + } + + public static boolean isShowing() { + return currentIFrameContainer != null; + } + + private static int hashPermissionFlags(WebViewOptions opts) { + int i = (opts.scriptEnabled ? 1 : 0); + i |= ((enableCSP && cspSupport && opts.strictCSPEnable) ? 0 : 2); + i |= (opts.serverMessageAPIEnabled ? 4 : 0); + return i; + } + + public static void beginShowing(WebViewOptions options, int x, int y, int w, int h) { + if(!supported()) { + return; + } + setupShowing(x, y, w, h); + if(options.scriptEnabled) { + PermissionsCache.Permission perm = PermissionsCache.getJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(options)); + if(perm == null) { + beginShowingEnableJavaScript(options); + }else if(perm.choice) { + beginShowingDirect(options); + }else { + beginShowingContentBlocked(options); + } + }else { + beginShowingDirect(options); + } + } + + private static void setupShowing(int x, int y, int w, int h) { + if(currentIFrameContainer != null) { + endShowing(); + } + currentIFrameContainer = win.getDocument().createElement("div"); + currentIFrameContainer.getClassList().add("_eaglercraftX_webview_container_element"); + CSSStyleDeclaration decl = currentIFrameContainer.getStyle(); + decl.setProperty("border", "5px solid #333333"); + decl.setProperty("z-index", "11"); + decl.setProperty("position", "absolute"); + decl.setProperty("background-color", "#DDDDDD"); + decl.setProperty("font-family", "sans-serif"); + resize(x, y, w, h); + rootElement.appendChild(currentIFrameContainer); + } + + private static void beginShowingDirect(WebViewOptions options) { + if(!supportForce) { + try { + currentOptions = options; + currentIFrame = (AdvancedHTMLIFrameElement)win.getDocument().createElement("iframe"); + currentIFrame.setAllowSafe(""); + currentIFrame.setReferrerPolicy("strict-origin"); + Set sandboxArgs = new HashSet<>(); + sandboxArgs.add("allow-downloads"); + if(options.scriptEnabled) { + sandboxArgs.add("allow-scripts"); + sandboxArgs.add("allow-pointer-lock"); + } + currentIFrame.setSandboxSafe(sandboxArgs); + }catch(IFrameSafetyException ex) { + logger.error("Caught safety exception while opening webview!"); + logger.error(ex); + if(currentIFrame != null) { + currentIFrame.delete(); + currentIFrame = null; + currentOptions = null; + } + logger.error("Things you can try:"); + logger.error("1. Set window.eaglercraftXOpts.forceWebViewSupport to true"); + logger.error("2. Set window.eaglercraftXOpts.enableWebViewCSP to false"); + logger.error("(these settings may compromise security)"); + beginShowingSafetyError(); + return; + } + }else { + currentOptions = options; + currentIFrame = (AdvancedHTMLIFrameElement)win.getDocument().createElement("iframe"); + try { + currentIFrame.setAllow(""); + }catch(Throwable t) { + } + try { + currentIFrame.setReferrerPolicy("strict-origin"); + }catch(Throwable t) { + } + try { + List sandboxArgs = new ArrayList<>(); + sandboxArgs.add("allow-downloads"); + sandboxArgs.add("allow-same-origin"); + if(options.scriptEnabled) { + sandboxArgs.add("allow-scripts"); + sandboxArgs.add("allow-pointer-lock"); + } + currentIFrame.setSandbox(String.join(" ", sandboxArgs)); + }catch(Throwable t) { + } + } + currentIFrame.setCredentialless(true); + currentIFrame.setLoading("lazy"); + boolean cspWarn = false; + if(options.contentMode == EnumWebViewContentMode.BLOB_BASED) { + if(enableCSP && cspSupport) { + if(currentIFrame.checkCSPSupported()) { + StringBuilder csp = new StringBuilder(); + csp.append("default-src 'none';"); + String protos = options.strictCSPEnable ? "" : (PlatformRuntime.requireSSL() ? " https:" : " http: https:"); + if(options.scriptEnabled) { + csp.append(" script-src 'unsafe-eval' 'unsafe-inline' data: blob:").append(protos).append(';'); + csp.append(" style-src 'unsafe-eval' 'unsafe-inline' data: blob:").append(protos).append(';'); + csp.append(" img-src data: blob:").append(protos).append(';'); + csp.append(" font-src data: blob:").append(protos).append(';'); + csp.append(" child-src data: blob:").append(protos).append(';'); + csp.append(" frame-src data: blob:;"); + csp.append(" media-src data: mediastream: blob:").append(protos).append(';'); + csp.append(" connect-src data: blob:").append(protos).append(';'); + csp.append(" worker-src data: blob:").append(protos).append(';'); + }else { + csp.append(" style-src data: 'unsafe-inline'").append(protos).append(';'); + csp.append(" img-src data:").append(protos).append(';'); + csp.append(" font-src data:").append(protos).append(';'); + csp.append(" media-src data:").append(protos).append(';'); + } + currentIFrame.setCSP(csp.toString()); + }else { + logger.warn("This browser does not support CSP attribute on iframes! (try Chrome)"); + cspWarn = true; + } + }else { + cspWarn = true; + } + if(cspWarn && options.strictCSPEnable) { + logger.warn("Strict CSP was requested for this webview, but that feature is not available!"); + } + }else { + cspWarn = true; + } + CSSStyleDeclaration decl = currentIFrame.getStyle(); + decl.setProperty("border", "none"); + decl.setProperty("background-color", "white"); + decl.setProperty("width", "100%"); + decl.setProperty("height", "100%"); + currentIFrame.getClassList().add("_eaglercraftX_webview_iframe_element"); + currentIFrameContainer.appendChild(currentIFrame); + if(options.contentMode == EnumWebViewContentMode.BLOB_BASED) { + currentIFrame.setSourceDocument(new String(options.blob, StandardCharsets.UTF_8)); + }else { + currentIFrame.setSourceAddress(options.url.toString()); + } + final int resetSer = webviewResetSerial; + final AdvancedHTMLIFrameElement curIFrame = currentIFrame; + final boolean[] focusTracker = new boolean[1]; + currentIFrame.addEventListener("mouseover", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(resetSer == webviewResetSerial && curIFrame == currentIFrame) { + if(!focusTracker[0]) { + focusTracker[0] = true; + currentIFrame.getContentWindow().focus(); + } + } + } + }); + currentIFrame.addEventListener("mouseout", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(resetSer == webviewResetSerial && curIFrame == currentIFrame) { + if(focusTracker[0]) { + focusTracker[0] = false; + win.focus(); + } + } + } + }); + if(options.scriptEnabled && options.serverMessageAPIEnabled) { + currentMessageHandler = new Consumer() { + @Override + public void accept(MessageEvent evt) { + synchronized(messageQueue) { + if(resetSer == webviewResetSerial && curIFrame == currentIFrame) { + messageQueue.add(() -> { + if(resetSer == webviewResetSerial && curIFrame == currentIFrame) { + handleMessageRawFromFrame(evt.getData()); + }else { + logger.warn("Recieved message from on dead IFrame handler: (#" + resetSer + ") " + curIFrame.getSourceAddress()); + } + }); + }else { + logger.warn("Recieved message from on dead IFrame handler: (#" + resetSer + ") " + curIFrame.getSourceAddress()); + } + } + } + }; + } + logger.info("WebView is loading: \"{}\"", options.contentMode == EnumWebViewContentMode.BLOB_BASED ? "about:srcdoc" : currentIFrame.getSourceAddress()); + logger.info("JavaScript: {}, Strict CSP: {}, Message API: {}", options.scriptEnabled, + options.strictCSPEnable && !cspWarn, options.serverMessageAPIEnabled); + } + + private static void beginShowingEnableJSSetup() { + if(currentAllowJavaScript != null) { + ++webviewResetSerial; + currentAllowJavaScript.delete(); + currentAllowJavaScript = null; + } + currentAllowJavaScript = win.getDocument().createElement("div"); + CSSStyleDeclaration decl = currentAllowJavaScript.getStyle(); + decl.setProperty("background-color", "white"); + decl.setProperty("width", "100%"); + decl.setProperty("height", "100%"); + currentAllowJavaScript.getClassList().add("_eaglercraftX_webview_permission_screen"); + currentIFrameContainer.appendChild(currentAllowJavaScript); + } + + private static void beginShowingEnableJavaScript(final WebViewOptions options) { + beginShowingEnableJSSetup(); + String strictCSPMarkup; + if(options.contentMode != EnumWebViewContentMode.BLOB_BASED) { + strictCSPMarkup = "Impossible"; + }else if(!cspSupport || !enableCSP) { + strictCSPMarkup = "Unsupported"; + }else if(options.strictCSPEnable) { + strictCSPMarkup = "Enabled"; + }else { + strictCSPMarkup = "Disabled"; + } + String messageAPIMarkup; + if(options.serverMessageAPIEnabled) { + messageAPIMarkup = "Enabled"; + }else { + messageAPIMarkup = "Disabled"; + } + currentAllowJavaScript.setInnerHTML( + "
" + + "

 Allow JavaScript

" + + "

" + + "

Strict CSP: " + strictCSPMarkup + " | " + + "Message API: " + messageAPIMarkup + "

" + + "

Remember my choice

" + + "

 " + + "

"); + final int serial = webviewResetSerial; + if(options.contentMode != EnumWebViewContentMode.BLOB_BASED) { + String urlStr = options.url.toString(); + currentAllowJavaScript.querySelector("._eaglercraftX_permission_target_url").setInnerText(urlStr.length() > 255 ? (urlStr.substring(0, 253) + "...") : urlStr); + } + currentAllowJavaScript.querySelector("._eaglercraftX_allow_javascript").addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(webviewResetSerial == serial && currentAllowJavaScript != null) { + HTMLInputElement chkbox = (HTMLInputElement)currentAllowJavaScript.querySelector("._eaglercraftX_remember_javascript"); + if(chkbox != null && chkbox.isChecked()) { + PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(options), true); + } + currentAllowJavaScript.delete(); + currentAllowJavaScript = null; + ++webviewResetSerial; + beginShowingDirect(options); + } + } + }); + currentAllowJavaScript.querySelector("._eaglercraftX_block_javascript").addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(webviewResetSerial == serial && currentAllowJavaScript != null) { + HTMLInputElement chkbox = (HTMLInputElement)currentAllowJavaScript.querySelector("._eaglercraftX_remember_javascript"); + if(chkbox != null && chkbox.isChecked()) { + PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(options), false); + } + beginShowingContentBlocked(options); + } + } + }); + } + + private static void beginShowingContentBlocked(final WebViewOptions options) { + beginShowingEnableJSSetup(); + currentAllowJavaScript.setInnerHTML( + "

" + + " Content Blocked

" + + "

You chose to block JavaScript execution for this embed

" + + "

"); + final int serial = webviewResetSerial; + currentAllowJavaScript.querySelector("._eaglercraftX_re_evaluate_javascript").addEventListener("click", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(webviewResetSerial == serial && currentAllowJavaScript != null) { + PermissionsCache.clearJavaScriptAllowed(options.permissionsOriginUUID); + beginShowingEnableJavaScript(options); + } + } + }); + } + + private static void beginShowingSafetyError() { + beginShowingEnableJSSetup(); + currentAllowJavaScript.setInnerHTML( + "

" + + " IFrame Safety Error

" + + "

The content cannot be displayed safely!

" + + "

Check console for more details

"); + } + + private static String getURLOrigin(URI urlObject) { + String str = " " + urlObject.getScheme() + "://" + urlObject.getRawAuthority(); + if(str.startsWith(" http:")) { + str = str + " https" + str.substring(5); + } + return str; + } + + public static void resize(int x, int y, int w, int h) { + if(currentIFrameContainer != null) { + CSSStyleDeclaration decl = currentIFrameContainer.getStyle(); + float s = PlatformInput.getDPI(); + decl.setProperty("top", "" + (y / s) + "px"); + decl.setProperty("left", "" + (x / s) + "px"); + decl.setProperty("width", "" + ((w / s) - 10) + "px"); + decl.setProperty("height", "" + ((h / s) - 10) + "px"); + } + } + + public static void endShowing() { + ++webviewResetSerial; + if(currentIFrame != null) { + currentIFrame.delete(); + currentIFrame = null; + } + synchronized(messageQueue) { + messageQueue.clear(); + } + currentMessageHandler = null; + if(currentAllowJavaScript != null) { + currentAllowJavaScript.delete(); + currentAllowJavaScript = null; + } + currentIFrameContainer.delete(); + currentIFrameContainer = null; + if(currentMessageChannelName != null) { + sendMessageEnToServer(false, currentMessageChannelName); + currentMessageChannelName = null; + } + win.focus(); + currentOptions = null; + } + + public static boolean fallbackSupported() { + return false; + } + + public static void launchFallback(WebViewOptions options) { + + } + + public static boolean fallbackRunning() { + return false; + } + + public static String getFallbackURL() { + return null; + } + + public static void endFallbackServer() { + + } + + @JSBody(params = { "evt", "iframe" }, script = "return evt.source === iframe.contentWindow;") + private static native boolean sourceEquals(MessageEvent evt, AdvancedHTMLIFrameElement iframe); + + static void onWindowMessageRecieved(MessageEvent evt) { + if(currentIFrame != null && currentMessageHandler != null && sourceEquals(evt, currentIFrame)) { + currentMessageHandler.accept(evt); + } + } + + public static void setPacketSendCallback(IPacketSendCallback callback) { + packetSendCallback = callback; + } + + public static void runTick() { + if(currentIFrame == null) { + return; + } + List lst = null; + synchronized(messageQueue) { + if(messageQueue.isEmpty()) { + return; + } + lst = new ArrayList<>(messageQueue); + messageQueue.clear(); + } + for(int i = 0, l = lst.size(); i < l; ++i) { + try { + lst.get(i).run(); + }catch(Throwable t) { + logger.error("Caught exception processing webview message!"); + logger.error(t); + } + } + } + + @JSBody(params = { "channel", "contents" }, script = "return {ver:1,channel:channel,type:\"string\",data:contents};") + private static native JSObject createStringMessage(String channel, String contents); + + @JSBody(params = { "channel", "contents" }, script = "return {ver:1,channel:channel,type:\"binary\",data:contents};") + private static native JSObject createBinaryMessage(String channel, ArrayBuffer contents); + + public static void handleMessageFromServer(SPacketWebViewMessageV4EAG packet) { + Window w; + if(currentMessageChannelName != null && currentIFrame != null && (w = currentIFrame.getContentWindow()) != null) { + JSObject obj = null; + if(packet.type == SPacketWebViewMessageV4EAG.TYPE_STRING) { + obj = createStringMessage(currentMessageChannelName, new String(packet.data, StandardCharsets.UTF_8)); + }else if(packet.type == SPacketWebViewMessageV4EAG.TYPE_BINARY) { + obj = createBinaryMessage(currentMessageChannelName, TeaVMUtils.unwrapArrayBuffer(packet.data)); + } + if(obj != null) { + w.postMessage(obj, "*"); + } + }else { + logger.error("Server tried to send the WebView a message, but the message channel is not open!"); + } + } + + @JSBody(params = { "obj" }, script = "return (typeof obj === \"object\") && (obj.ver === 1) && ((typeof obj.channel === \"string\") && obj.channel.length > 0);") + private static native boolean checkRawMessageValid(JSObject obj); + + @JSBody(params = { "obj" }, script = "return (typeof obj.open === \"boolean\");") + private static native boolean checkRawMessageValidEn(JSObject obj); + + @JSBody(params = { "obj" }, script = "return (typeof obj.data === \"string\");") + private static native boolean checkRawMessageValidDataStr(JSObject obj); + + @JSBody(params = { "obj" }, script = "return (obj.data instanceof ArrayBuffer);") + private static native boolean checkRawMessageValidDataBin(JSObject obj); + + private static interface WebViewMessage extends JSObject { + + @JSProperty + String getType(); + + @JSProperty + String getChannel(); + + @JSProperty("data") + String getDataAsString(); + + @JSProperty("data") + ArrayBuffer getDataAsArrayBuffer(); + + @JSProperty + boolean getOpen(); + + } + + private static void handleMessageRawFromFrame(JSObject obj) { + if(checkRawMessageValid(obj)) { + if(checkRawMessageValidEn(obj)) { + WebViewMessage msg = (WebViewMessage)obj; + sendMessageEnToServer(msg.getOpen(), msg.getChannel()); + return; + }else if(checkRawMessageValidDataStr(obj)) { + WebViewMessage msg = (WebViewMessage)obj; + sendMessageToServer(msg.getChannel(), CPacketWebViewMessageV4EAG.TYPE_STRING, msg.getDataAsString().getBytes(StandardCharsets.UTF_8)); + return; + }else if(checkRawMessageValidDataBin(obj)) { + WebViewMessage msg = (WebViewMessage)obj; + sendMessageToServer(msg.getChannel(), CPacketWebViewMessageV4EAG.TYPE_BINARY, TeaVMUtils.wrapByteArrayBuffer(msg.getDataAsArrayBuffer())); + return; + } + } + logger.warn("WebView sent an invalid message!"); + } + + private static void sendMessageToServer(String channelName, int type, byte[] data) { + if(channelName.length() > 255) { + logger.error("WebView tried to send a message packet, but channel name is too long, max is 255 characters!"); + return; + } + if(!channelName.equals(currentMessageChannelName)) { + logger.error("WebView tried to send a message packet, but the channel is not open!"); + return; + } + if(packetSendCallback != null) { + if(!packetSendCallback.sendPacket(new CPacketWebViewMessageV4EAG(type, data))) { + logger.error("WebView tried to send a packet to the server, but the server does not support this protocol!"); + } + }else { + logger.error("WebView tried to send a message, but no callback for sending packets is set!"); + } + } + + private static void sendMessageEnToServer(boolean messageChannelOpen, String channelName) { + if(channelName.length() > 255) { + logger.error("WebView tried to {} a channel, but channel name is too long, max is 255 characters!", messageChannelOpen ? "open" : "close"); + return; + } + if(messageChannelOpen && currentMessageChannelName != null) { + logger.error("WebView tried to open channel, but a channel is already open!"); + sendMessageEnToServer(false, currentMessageChannelName); + } + if(!messageChannelOpen && currentMessageChannelName != null && !currentMessageChannelName.equals(channelName)) { + logger.error("WebView tried to close the wrong channel!"); + } + if(!messageChannelOpen && currentMessageChannelName == null) { + logger.error("WebView tried to close channel, but the channel is not open!"); + return; + } + if(packetSendCallback != null) { + if(!packetSendCallback.sendPacket(new CPacketWebViewMessageEnV4EAG(messageChannelOpen, messageChannelOpen ? channelName : null))) { + logger.error("WebView tried to send a packet to the server, but the server does not support this protocol!"); + return; + } + if(messageChannelOpen) { + logger.info("WebView opened message channel to server: \"{}\"", channelName); + currentMessageChannelName = channelName; + }else { + logger.info("WebView closed message channel to server: \"{}\"", currentMessageChannelName); + currentMessageChannelName = null; + } + }else { + logger.error("WebView tried to send a message, but no callback for sending packets is set!"); + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java index a0ce5d7..48d19bc 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java @@ -32,9 +32,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { int position; int limit; int mark; - - static final Int8Array ZERO_LENGTH_BUFFER = Int8Array.create(0); - + EaglerArrayByteBuffer(DataView dataView) { this.dataView = dataView; this.typedArray = Int8Array.create(dataView.getBuffer(), dataView.getByteOffset(), dataView.getByteLength()); @@ -43,7 +41,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { this.limit = this.capacity; this.mark = -1; } - + EaglerArrayByteBuffer(DataView dataView, int position, int limit, int mark) { this.dataView = dataView; this.typedArray = Int8Array.create(dataView.getBuffer(), dataView.getByteOffset(), dataView.getByteLength()); @@ -52,7 +50,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { this.limit = limit; this.mark = mark; } - + EaglerArrayByteBuffer(Int8Array typedArray) { this.typedArray = typedArray; this.dataView = DataView.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getByteLength()); @@ -61,7 +59,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { this.limit = this.capacity; this.mark = -1; } - + EaglerArrayByteBuffer(Int8Array typedArray, int position, int limit, int mark) { this.typedArray = typedArray; this.dataView = DataView.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getByteLength()); @@ -96,18 +94,13 @@ public class EaglerArrayByteBuffer implements ByteBuffer { return limit > position; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public byte[] array() { throw new UnsupportedOperationException(); } @@ -116,55 +109,40 @@ public class EaglerArrayByteBuffer implements ByteBuffer { return true; } - @Override - public ByteBuffer slice() { - if(position == limit) { - return new EaglerArrayByteBuffer(ZERO_LENGTH_BUFFER); - }else { - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - return new EaglerArrayByteBuffer(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, limit - position)); - } - } - @Override public ByteBuffer duplicate() { return new EaglerArrayByteBuffer(dataView, position, limit, mark); } - @Override - public ByteBuffer asReadOnlyBuffer() { - return new EaglerArrayByteBuffer(dataView, position, limit, mark); - } - @Override public byte get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); return typedArray.get(position++); } @Override public ByteBuffer put(byte b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); typedArray.set(position++, b); return this; } @Override public byte get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public ByteBuffer put(int index, byte b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, b); return this; } @Override public ByteBuffer get(byte[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, length), offset); position += length; return this; @@ -172,7 +150,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer get(byte[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, dst.length)); position += dst.length; return this; @@ -183,13 +161,13 @@ public class EaglerArrayByteBuffer implements ByteBuffer { if(src instanceof EaglerArrayByteBuffer) { EaglerArrayByteBuffer c = (EaglerArrayByteBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); typedArray.set(Int8Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + c.position, l), position); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { dataView.setInt8(position + l, src.get()); } @@ -200,7 +178,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer put(byte[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); if(offset == 0 && length == src.length) { typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); }else { @@ -212,35 +190,15 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer put(byte[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } - @Override - public int arrayOffset() { - return position; - } - - @Override - public ByteBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerArrayByteBuffer(ZERO_LENGTH_BUFFER); - } - - Int8Array dst = Int8Array.create(limit - position); - dst.set(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, limit - position)); - - return new EaglerArrayByteBuffer(dst); - } - @Override public char getChar() { - if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 2 > limit) throw Buffer.makeIOOBE(position); char c = (char)dataView.getUint16(position, true); position += 2; return c; @@ -248,7 +206,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer putChar(char value) { - if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 2 > limit) throw Buffer.makeIOOBE(position); dataView.setUint16(position, (short)value, true); position += 2; return this; @@ -256,20 +214,20 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public char getChar(int index) { - if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); return (char)dataView.getUint16(index, true); } @Override public ByteBuffer putChar(int index, char value) { - if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); dataView.setUint16(index, value, true); return this; } @Override public short getShort() { - if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 2 > limit) throw Buffer.makeIOOBE(position); short s = dataView.getInt16(position, true); position += 2; return s; @@ -277,7 +235,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer putShort(short value) { - if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 2 > limit) throw Buffer.makeIOOBE(position); dataView.setInt16(position, value, true); position += 2; return this; @@ -285,13 +243,13 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public short getShort(int index) { - if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); return dataView.getInt16(index, true); } @Override public ByteBuffer putShort(int index, short value) { - if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index); dataView.setInt16(index, value, true); return this; } @@ -303,7 +261,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public int getInt() { - if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 4 > limit) throw Buffer.makeIOOBE(position); int i = dataView.getInt32(position, true); position += 4; return i; @@ -311,7 +269,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer putInt(int value) { - if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 4 > limit) throw Buffer.makeIOOBE(position); dataView.setInt32(position, value, true); position += 4; return this; @@ -319,13 +277,13 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public int getInt(int index) { - if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); return dataView.getInt32(index, true); } @Override public ByteBuffer putInt(int index, int value) { - if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); dataView.setInt32(index, value, true); return this; } @@ -337,7 +295,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public long getLong() { - if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 8 > limit) throw Buffer.makeIOOBE(position); long l = dataView.getUint32(position) | ((long) dataView.getUint8(position + 4) << 32) | ((long) dataView.getUint8(position + 5) << 40) | ((long) dataView.getUint8(position + 6) << 48) | ((long) dataView.getUint8(position + 7) << 56); @@ -347,7 +305,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer putLong(long value) { - if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 8 > limit) throw Buffer.makeIOOBE(position); dataView.setUint32(position, (int) (value & 0xFFFFFFFFl), true); dataView.setUint8(position + 4, (short) ((value >>> 32l) & 0xFFl)); dataView.setUint8(position + 5, (short) ((value >>> 40l) & 0xFFl)); @@ -359,7 +317,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public long getLong(int index) { - if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index); return dataView.getUint32(index, true) | ((long) dataView.getUint8(index + 4) << 32) | ((long) dataView.getUint8(index + 5) << 40) | ((long) dataView.getUint8(index + 6) << 48) | ((long) dataView.getUint8(index + 7) << 56); @@ -367,7 +325,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer putLong(int index, long value) { - if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index); dataView.setUint32(index, (int) (value & 0xFFFFFFFFl), true); dataView.setUint8(index + 4, (short) ((value >>> 32l) & 0xFFl)); dataView.setUint8(index + 5, (short) ((value >>> 40l) & 0xFFl)); @@ -378,7 +336,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public float getFloat() { - if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 4 > limit) throw Buffer.makeIOOBE(position); float f = dataView.getFloat32(position, true); position += 4; return f; @@ -386,7 +344,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer putFloat(float value) { - if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + if(position + 4 > limit) throw Buffer.makeIOOBE(position); dataView.setFloat32(position, value, true); position += 4; return this; @@ -394,13 +352,13 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public float getFloat(int index) { - if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); return dataView.getFloat32(index, true); } @Override public ByteBuffer putFloat(int index, float value) { - if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index); dataView.setFloat32(index, value, true); return this; } @@ -419,7 +377,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @@ -449,14 +407,14 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override public ByteBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java index 4e9ffe9..396676a 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java @@ -27,11 +27,9 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { int position; int limit; int mark; - + private static final int SHIFT = 2; - - static final Float32Array ZERO_LENGTH_BUFFER = Float32Array.create(0); - + EaglerArrayFloatBuffer(Float32Array typedArray) { this.typedArray = typedArray; this.capacity = typedArray.getLength(); @@ -39,7 +37,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { this.limit = this.capacity; this.mark = -1; } - + EaglerArrayFloatBuffer(Float32Array typedArray, int position, int limit, int mark) { this.typedArray = typedArray; this.capacity = typedArray.getLength(); @@ -47,7 +45,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { this.limit = limit; this.mark = mark; } - + @Override public int capacity() { return capacity; @@ -73,87 +71,62 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { return position < limit; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public float[] array() { throw new UnsupportedOperationException(); } - @Override - public int arrayOffset() { - return position; - } - - @Override - public FloatBuffer slice() { - if(position == limit) { - return new EaglerArrayFloatBuffer(ZERO_LENGTH_BUFFER); - }else { - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - return new EaglerArrayFloatBuffer(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - } - } - @Override public FloatBuffer duplicate() { return new EaglerArrayFloatBuffer(typedArray, position, limit, mark); } - @Override - public FloatBuffer asReadOnlyBuffer() { - return new EaglerArrayFloatBuffer(typedArray, position, limit, mark); - } - @Override public float get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); return typedArray.get(position++); } @Override public FloatBuffer put(float b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); typedArray.set(position++, b); return this; } @Override public float get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public FloatBuffer put(int index, float b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, b); return this; } @Override public float getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public void putElement(int index, float value) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, value); } @Override public FloatBuffer get(float[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) Buffer.makeIOOBE(position + length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), length), offset); position += length; return this; @@ -161,7 +134,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer get(float[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + if(position + dst.length > limit) Buffer.makeIOOBE(position + dst.length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), dst.length)); position += dst.length; return this; @@ -172,13 +145,13 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { if(src instanceof EaglerArrayFloatBuffer) { EaglerArrayFloatBuffer c = (EaglerArrayFloatBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); typedArray.set(Float32Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + (c.position << SHIFT), l), position); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { typedArray.set(position + l, src.get()); } @@ -189,7 +162,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer put(float[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); if(offset == 0 && length == src.length) { typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); }else { @@ -201,32 +174,12 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer put(float[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } - @Override - public int getArrayOffset() { - return position; - } - - @Override - public FloatBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerArrayFloatBuffer(ZERO_LENGTH_BUFFER); - } - - Float32Array dst = Float32Array.create(limit - position); - dst.set(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - - return new EaglerArrayFloatBuffer(dst); - } - @Override public boolean isDirect() { return true; @@ -241,7 +194,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @@ -271,14 +224,14 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override public FloatBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java index 7aee34a..af0b86b 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java @@ -27,11 +27,9 @@ public class EaglerArrayIntBuffer implements IntBuffer { int position; int limit; int mark; - + private static final int SHIFT = 2; - - static final Int32Array ZERO_LENGTH_BUFFER = Int32Array.create(0); - + EaglerArrayIntBuffer(Int32Array typedArray) { this.typedArray = typedArray; this.capacity = typedArray.getLength(); @@ -39,7 +37,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { this.limit = this.capacity; this.mark = -1; } - + EaglerArrayIntBuffer(Int32Array typedArray, int position, int limit, int mark) { this.typedArray = typedArray; this.capacity = typedArray.getLength(); @@ -47,7 +45,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { this.limit = limit; this.mark = mark; } - + @Override public int capacity() { return capacity; @@ -73,87 +71,62 @@ public class EaglerArrayIntBuffer implements IntBuffer { return position < limit; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public int[] array() { throw new UnsupportedOperationException(); } - @Override - public int arrayOffset() { - return position; - } - - @Override - public IntBuffer slice() { - if(position == limit) { - return new EaglerArrayIntBuffer(ZERO_LENGTH_BUFFER); - }else { - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - return new EaglerArrayIntBuffer(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - } - } - @Override public IntBuffer duplicate() { return new EaglerArrayIntBuffer(typedArray, position, limit, mark); } - @Override - public IntBuffer asReadOnlyBuffer() { - return new EaglerArrayIntBuffer(typedArray, position, limit, mark); - } - @Override public int get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); return typedArray.get(position++); } @Override public IntBuffer put(int b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); typedArray.set(position++, b); return this; } @Override public int get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public IntBuffer put(int index, int b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, b); return this; } @Override public int getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public void putElement(int index, int value) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, value); } @Override public IntBuffer get(int[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), length), offset); position += length; return this; @@ -161,7 +134,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer get(int[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), dst.length)); position += dst.length; return this; @@ -172,13 +145,13 @@ public class EaglerArrayIntBuffer implements IntBuffer { if(src instanceof EaglerArrayIntBuffer) { EaglerArrayIntBuffer c = (EaglerArrayIntBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); typedArray.set(Int32Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + (c.position << SHIFT), l), position); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { typedArray.set(position + l, src.get()); } @@ -189,7 +162,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); if(offset == 0 && length == src.length) { typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); }else { @@ -201,32 +174,12 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } - @Override - public int getArrayOffset() { - return position; - } - - @Override - public IntBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerArrayIntBuffer(ZERO_LENGTH_BUFFER); - } - - Int32Array dst = Int32Array.create(limit - position); - dst.set(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - - return new EaglerArrayIntBuffer(dst); - } - @Override public boolean isDirect() { return true; @@ -241,7 +194,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @@ -271,14 +224,14 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override public IntBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java index 16eabb5..80bb3dd 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java @@ -27,11 +27,9 @@ public class EaglerArrayShortBuffer implements ShortBuffer { int position; int limit; int mark; - + private static final int SHIFT = 1; - - static final Int16Array ZERO_LENGTH_BUFFER = Int16Array.create(0); - + EaglerArrayShortBuffer(Int16Array typedArray) { this.typedArray = typedArray; this.capacity = typedArray.getLength(); @@ -39,7 +37,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { this.limit = this.capacity; this.mark = -1; } - + EaglerArrayShortBuffer(Int16Array typedArray, int position, int limit, int mark) { this.typedArray = typedArray; this.capacity = typedArray.getLength(); @@ -47,7 +45,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { this.limit = limit; this.mark = mark; } - + @Override public int capacity() { return capacity; @@ -73,87 +71,62 @@ public class EaglerArrayShortBuffer implements ShortBuffer { return position < limit; } - @Override - public boolean isReadOnly() { - return false; - } - @Override public boolean hasArray() { return false; } @Override - public Object array() { + public short[] array() { throw new UnsupportedOperationException(); } - @Override - public int arrayOffset() { - return position; - } - - @Override - public ShortBuffer slice() { - if(position == limit) { - return new EaglerArrayShortBuffer(ZERO_LENGTH_BUFFER); - }else { - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - return new EaglerArrayShortBuffer(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - } - } - @Override public ShortBuffer duplicate() { return new EaglerArrayShortBuffer(typedArray, position, limit, mark); } - @Override - public ShortBuffer asReadOnlyBuffer() { - return new EaglerArrayShortBuffer(typedArray, position, limit, mark); - } - @Override public short get() { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); return typedArray.get(position++); } @Override public ShortBuffer put(short b) { - if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + if(position >= limit) throw Buffer.makeIOOBE(position); typedArray.set(position++, b); return this; } @Override public short get(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public ShortBuffer put(int index, short b) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, b); return this; } @Override public short getElement(int index) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); return typedArray.get(index); } @Override public void putElement(int index, short value) { - if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index); typedArray.set(index, value); } @Override public ShortBuffer get(short[] dst, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), length), offset); position += length; return this; @@ -161,7 +134,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer get(short[] dst) { - if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1); TeaVMUtils.unwrapArrayBufferView(dst).set(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), dst.length)); position += dst.length; return this; @@ -172,13 +145,13 @@ public class EaglerArrayShortBuffer implements ShortBuffer { if(src instanceof EaglerArrayShortBuffer) { EaglerArrayShortBuffer c = (EaglerArrayShortBuffer)src; int l = c.limit - c.position; - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); typedArray.set(Int16Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + (c.position << SHIFT), l), position); position += l; c.position += l; }else { int l = src.remaining(); - if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1); for(int i = 0; i < l; ++i) { typedArray.set(position + l, src.get()); } @@ -189,7 +162,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src, int offset, int length) { - if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1); if(offset == 0 && length == src.length) { typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); }else { @@ -201,32 +174,12 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src) { - if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1); typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } - @Override - public int getArrayOffset() { - return position; - } - - @Override - public ShortBuffer compact() { - if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); - if(position > limit) throw new ArrayIndexOutOfBoundsException(position); - - if(position == limit) { - return new EaglerArrayShortBuffer(ZERO_LENGTH_BUFFER); - } - - Int16Array dst = Int16Array.create(limit - position); - dst.set(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - - return new EaglerArrayShortBuffer(dst); - } - @Override public boolean isDirect() { return true; @@ -241,7 +194,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer reset() { int m = mark; - if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m); position = m; return this; } @@ -271,14 +224,14 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer limit(int newLimit) { - if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit); limit = newLimit; return this; } @Override public ShortBuffer position(int newPosition) { - if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition); position = newPosition; return this; } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/AdvancedHTMLIFrameElement.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/AdvancedHTMLIFrameElement.java new file mode 100755 index 0000000..f908f98 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/AdvancedHTMLIFrameElement.java @@ -0,0 +1,120 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSProperty; +import org.teavm.jso.dom.html.HTMLIFrameElement; +import org.teavm.jso.dom.types.DOMTokenList; + +import com.google.common.collect.Iterators; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class AdvancedHTMLIFrameElement implements HTMLIFrameElement { + + @JSProperty + public abstract void setAllow(String str); + + @JSProperty + public abstract String getAllow(); + + public void setAllowSafe(String requiredValue) { + setAllow(requiredValue); + if(!requiredValue.equals(getAllow())) { + throw new IFrameSafetyException("Could not set allow attribute to: " + requiredValue); + } + } + + @JSProperty + public abstract void setAllowFullscreen(boolean en); + + @JSProperty + public abstract void setCredentialless(boolean en); + + @JSProperty + public abstract void setLoading(String str); + + @JSProperty + public abstract void setReferrerPolicy(String str); + + @JSProperty("csp") + public abstract void setCSP(String str); + + @JSProperty + public abstract void setSandbox(String str); + + @JSProperty + public abstract DOMTokenList getSandbox(); + + public void assertSafetyFeaturesSupported() { + if(!checkSafetyFeaturesSupported()) { + throw new IFrameSafetyException("Some required security features are not supported on this browser!"); + } + } + + public void setSandboxSafe(Collection requiredTokens) { + setSandboxSafe(new HashSet<>(requiredTokens)); + } + + public void setSandboxSafe(Set requiredTokens) { + setSandbox(String.join(" ", requiredTokens)); + DOMTokenList theSandbox = getSandbox(); + for(String s : requiredTokens) { + if(!theSandbox.contains(s)) { + throw new IFrameSafetyException("Failed to set sandbox attribute: " + s); + } + } + int l = theSandbox.getLength(); + for(int i = 0; i < l; ++i) { + String s = theSandbox.item(i); + if(!requiredTokens.contains(s)) { + throw new IFrameSafetyException("Unknown sandbox attribute detected: " + s); + } + } + } + + public void setSandboxSafe(Collection requiredTokens, Collection optionalTokens) { + setSandboxSafe(new HashSet<>(requiredTokens), new HashSet<>(optionalTokens)); + } + + public void setSandboxSafe(Set requiredTokens, Set optionalTokens) { + setSandbox(StringUtils.join(Iterators.concat(requiredTokens.iterator(), optionalTokens.iterator()), " ")); + DOMTokenList theSandbox = getSandbox(); + for(String s : requiredTokens) { + if(!theSandbox.contains(s)) { + throw new IFrameSafetyException("Failed to set sandbox attribute: " + s); + } + } + int l = theSandbox.getLength(); + for(int i = 0; i < l; ++i) { + String s = theSandbox.item(i); + if(!requiredTokens.contains(s) && !optionalTokens.contains(s)) { + throw new IFrameSafetyException("Unknown sandbox attribute detected: " + s); + } + } + } + + @JSBody(params = {}, script = "return (typeof this.allow === \"string\") && (typeof this.sandbox === \"object\");") + public native boolean checkSafetyFeaturesSupported(); + + @JSBody(params = {}, script = "return (typeof this.csp === \"string\");") + public native boolean checkCSPSupported(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ArrayBufferInputStream.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ArrayBufferInputStream.java index 6112d4d..33cf496 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ArrayBufferInputStream.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ArrayBufferInputStream.java @@ -1,104 +1,123 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm; - -import java.io.InputStream; - -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.Uint8Array; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ArrayBufferInputStream extends InputStream { - - private int position; - private int limit; - private final ArrayBuffer buffer; - private final Uint8Array typed; - - public ArrayBufferInputStream(ArrayBuffer bufferIn) { - this(bufferIn, 0, bufferIn.getByteLength()); - } - - public ArrayBufferInputStream(ArrayBuffer bufferIn, int off, int len) { - if(off + len > bufferIn.getByteLength()) { - throw new IllegalArgumentException("offset " + off + " and length " + len + " are out of bounds for a " - + bufferIn.getByteLength() + " long arraybuffer"); - } - buffer = bufferIn; - typed = Uint8Array.create(bufferIn); - position = off; - limit = off + len; - } - - @Override - public int read() { - if(position >= limit) { - return -1; - } - return typed.get(position++); - } - - @Override - public int read(byte b[], int off, int len) { - if(off + len > b.length) { - throw new ArrayIndexOutOfBoundsException("offset " + off + " and length " + len - + " are out of bounds for a " + b.length + " array"); - } - - int avail = limit - position; - if(len > avail) { - len = avail; - } - - if(len <= 0) { - return -1; - } - - for(int i = 0; i < len; ++i) { - b[off + i] = (byte)typed.get(position + i); - } - - position += len; - - return len; - } - - public long skip(long n) { - int avail = limit - position; - if(n > avail) { - n = avail; - } - position += (int)n; - return n; - } - - @Override - public int available() { - return limit - position; - } - - public int getPosition() { - return position; - } - - public int getLimit() { - return limit; - } - - public ArrayBuffer getBuffer() { - return buffer; - } - -} +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.io.IOException; +import java.io.InputStream; + +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.Int8Array; +import org.teavm.jso.typedarrays.Uint8Array; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ArrayBufferInputStream extends InputStream { + + private int mark = -1; + private int position; + private int limit; + private final ArrayBuffer buffer; + private final Uint8Array typed; + + public ArrayBufferInputStream(ArrayBuffer bufferIn) { + this(bufferIn, 0, bufferIn.getByteLength()); + } + + public ArrayBufferInputStream(ArrayBuffer bufferIn, int off, int len) { + if(off + len > bufferIn.getByteLength()) { + throw new IllegalArgumentException("offset " + off + " and length " + len + " are out of bounds for a " + + bufferIn.getByteLength() + " long arraybuffer"); + } + buffer = bufferIn; + typed = Uint8Array.create(bufferIn); + position = off; + limit = off + len; + } + + @Override + public int read() { + if(position >= limit) { + return -1; + } + return typed.get(position++); + } + + @Override + public int read(byte b[], int off, int len) { + if(off + len > b.length) { + throw new ArrayIndexOutOfBoundsException("offset " + off + " and length " + len + + " are out of bounds for a " + b.length + " array"); + } + + int avail = limit - position; + if(len > avail) { + len = avail; + } + + if(len <= 0) { + return -1; + } + + TeaVMUtils.unwrapArrayBufferView(b).set(Int8Array.create(buffer, position, len), off); + + position += len; + + return len; + } + + @Override + public long skip(long n) { + int avail = limit - position; + if(n > avail) { + n = avail; + } + position += (int)n; + return n; + } + + @Override + public int available() { + return limit - position; + } + + public int getPosition() { + return position; + } + + public int getLimit() { + return limit; + } + + public ArrayBuffer getBuffer() { + return buffer; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void mark(int readlimit) { + mark = position; + } + + @Override + public synchronized void reset() throws IOException { + if(mark == -1) { + throw new IOException("Cannot reset, stream has no mark!"); + } + position = mark; + } +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/Base64VarIntArray.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/Base64VarIntArray.java new file mode 100755 index 0000000..e62ac6b --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/Base64VarIntArray.java @@ -0,0 +1,129 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.Arrays; +import java.util.List; + +import org.teavm.jso.core.JSString; + +import net.lax1dude.eaglercraft.v1_8.Base64; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class Base64VarIntArray { + + public static String encodeVarIntArray(List values) { + StringBuilder ret = new StringBuilder(); + for(int i = 0, j, k, l = values.size(); i < l; ++i) { + j = values.get(i); + if(j < 0) j = 0; + for(;;) { + k = j & 31; + if(j > 31) { + j >>>= 5; + ret.append(Base64.lookupIntChar(k | 32)); + }else { + ret.append(Base64.lookupIntChar(k)); + break; + } + } + } + return ret.toString(); + } + + public static String encodeVarIntArray(int[] values) { + StringBuilder ret = new StringBuilder(); + for(int i = 0, j, k; i < values.length; ++i) { + j = values[i]; + if(j < 0) j = 0; + for(;;) { + k = j & 31; + if(j > 31) { + j >>>= 5; + ret.append(Base64.lookupIntChar(k | 32)); + }else { + ret.append(Base64.lookupIntChar(k)); + break; + } + } + } + return ret.toString(); + } + + public static int[] decodeVarIntArray(String values) { + int[] ret = new int[8]; + int o = 0; + for(int i = 0, j, k, m, l = values.length(); i < l;) { + k = 0; + m = 0; + for(;;) { + j = Base64.lookupCharInt(values.charAt(i++)); + if(j == -1) { + return null; + } + k |= (j & 31) << m; + if(j > 31) { + if(i >= l) { + return null; + } + m += 5; + }else { + break; + } + } + j = ret.length; + if(o >= j) { + int[] newRet = new int[j << 1]; + System.arraycopy(ret, 0, newRet, 0, j); + ret = newRet; + } + ret[o++] = k; + } + return o != ret.length ? Arrays.copyOf(ret, o) : ret; + } + + public static int[] decodeVarIntArray(JSString values) { + int[] ret = new int[8]; + int o = 0; + for(int i = 0, j, k, m, l = values.getLength(); i < l;) { + k = 0; + m = 0; + for(;;) { + j = Base64.lookupCharInt((char)values.charCodeAt(i++)); + if(j == -1) { + return null; + } + k |= (j & 31) << m; + if(j > 31) { + if(i >= l) { + return null; + } + m += 5; + }else { + break; + } + } + j = ret.length; + if(o >= j) { + int[] newRet = new int[j << 1]; + System.arraycopy(ret, 0, newRet, 0, j); + ret = newRet; + } + ret[o++] = k; + } + return o != ret.length ? Arrays.copyOf(ret, o) : ret; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClassesJSLocator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClassesJSLocator.java new file mode 100755 index 0000000..76268aa --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClassesJSLocator.java @@ -0,0 +1,94 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.dom.html.HTMLScriptElement; +import org.teavm.jso.dom.xml.Element; +import org.teavm.jso.dom.xml.NodeList; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClassesJSLocator { + + private static final Logger logger = LogManager.getLogger("ClassesJSLocator"); + + public static String resolveClassesJSFromThrowable() { + String str = resolveClassesJSFromThrowable0(); + if(str != null && str.equalsIgnoreCase(PlatformRuntime.win.getLocation().getFullURL())) { + return null; + } + return str; + } + + private static String resolveClassesJSFromThrowable0() { + String str = TeaVMUtils.dumpJSStackTrace(); + String[] frames = EagUtils.splitPattern.split(str); + if("Error".equals(frames[0])) { + // V8 stack trace + if(frames.length > 1) { + String framesTrim = frames[1].trim(); + if(framesTrim.startsWith("at")) { + //definitely V8 + int i = framesTrim.indexOf('('); + int j = framesTrim.indexOf(')'); + if(i != -1 && j != -1 && i < j) { + return tryResolveClassesSourceFromFrame(framesTrim.substring(i + 1, j)); + } + } + } + }else { + // Mozilla/WebKit stack trace + String framesTrim = frames[0].trim(); + int i = framesTrim.indexOf('@'); + if(i != -1) { + return tryResolveClassesSourceFromFrame(framesTrim.substring(i + 1)); + } + } + return null; + } + + private static String tryResolveClassesSourceFromFrame(String fileLineCol) { + int i = fileLineCol.lastIndexOf(':'); + if(i > 0) { + i = fileLineCol.lastIndexOf(':', i - 1); + } + if(i != -1) { + return fileLineCol.substring(0, i); + } + return null; + } + + public static HTMLScriptElement resolveClassesJSFromInline() { + NodeList elements = PlatformRuntime.doc.getElementsByTagName("script"); + for(int i = 0, l = elements.getLength(); i < l; ++i) { + HTMLScriptElement tag = (HTMLScriptElement)elements.get(i); + String scriptSrc = tag.getText(); + if(scriptSrc != null && scriptSrc.length() > 1024 * 1024) { + // I'm not feeling very creative tonight + int j = scriptSrc.indexOf("var $rt_seed=2463534242;"); + if(j > 0 && j < 2048 && scriptSrc.indexOf("$rt_createNumericArray(") != -1) { + logger.warn("Could not locate classes.js through conventional means, however an inline script tag was found on the page that (probably) contains a TeaVM program"); + return tag; + } + } + } + return null; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java index 6a41a37..1d084da 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java @@ -5,7 +5,9 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.teavm.jso.JSBody; import org.teavm.jso.JSFunctor; @@ -21,7 +23,10 @@ import org.teavm.jso.webgl.WebGLRenderingContext; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint; import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsAssetsURI; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot; @@ -62,13 +67,27 @@ public class ClientMain { return crashImage.substring(0); } - @JSBody(params = {}, script = "if((typeof window.__isEaglerX188Running === \"string\") && window.__isEaglerX188Running === \"yes\") return true; window.__isEaglerX188Running = \"yes\"; return false;") + @JSBody(params = {}, script = "if((typeof __isEaglerX188Running === \"string\") && __isEaglerX188Running === \"yes\") return true; __isEaglerX188Running = \"yes\"; return false;") private static native boolean getRunningFlag(); + @JSBody(params = { "str" }, script = "return (typeof location !== \"undefined\") && (typeof location.hostname === \"string\") && location.hostname.toLowerCase() === str;") + private static native boolean getTardFlag(String str); + + private static final PrintStream systemOut = System.out; + private static final PrintStream systemErr = System.err; + + private static JSObject windowErrorHandler = null; + public static void _main() { - PrintStream systemOut = System.out; - PrintStream systemErr = System.err; - if (getRunningFlag()) { + if(getTardFlag(new String(new char[] { 'e', 'a', 'g', 'l', 'e', 'r', 'c', 'r', 'a', 'f', 't', '.', 'd', 'e', 'v' }))) { + // Have fun, boys!!! + Window.alert(new String(new char[] { 101, 97, 103, 108, 101, 114, 99, 114, 97, 102, 116, 46, 100, 101, 118, + 32, 105, 115, 32, 110, 111, 116, 32, 97, 110, 32, 111, 102, 102, 105, 99, 105, 97, 108, 32, 119, + 101, 98, 115, 105, 116, 101, 32, 97, 110, 100, 32, 105, 115, 32, 110, 111, 116, 32, 101, 110, 100, + 111, 114, 115, 101, 100, 32, 98, 121, 32, 108, 97, 120, 49, 100, 117, 100, 101, 32, 111, 114, 32, + 97, 121, 117, 110, 97, 109, 105, 50, 48, 48, 48 })); + } + if(getRunningFlag()) { systemErr.println("ClientMain: [ERROR] eaglercraftx is already running!"); return; } @@ -86,13 +105,19 @@ public class ClientMain { try { JSEaglercraftXOptsRoot eaglercraftOpts = (JSEaglercraftXOptsRoot)opts; crashOnUncaughtExceptions = eaglercraftOpts.getCrashOnUncaughtExceptions(false); + PlatformRuntime.isDeobfStackTraces = eaglercraftOpts.getDeobfStackTraces(true); configRootElementId = eaglercraftOpts.getContainer(); if (configRootElementId == null) { throw new JSONException("window.eaglercraftXOpts.container is undefined!"); } configRootElement = Window.current().getDocument().getElementById(configRootElementId); - + + HTMLElement oldContent; + while((oldContent = configRootElement.querySelector("._eaglercraftX_wrapper_element")) != null) { + oldContent.delete(); + } + String epkSingleURL = eaglercraftOpts.getAssetsURI(); if (epkSingleURL != null) { configEPKFiles = new EPKFileEntry[] { new EPKFileEntry(epkSingleURL, "") }; @@ -133,31 +158,41 @@ public class ClientMain { if(crashOnUncaughtExceptions) { systemOut.println("ClientMain: [INFO] registering crash handlers"); - setWindowErrorHandler(new WindowErrorHandler() { + windowErrorHandler = setWindowErrorHandler(Window.current(), new WindowErrorHandler() { @Override public void call(String message, String file, int line, int col, JSError error) { - StringBuilder str = new StringBuilder(); - - str.append("Native Browser Exception\n"); - str.append("----------------------------------\n"); - str.append(" Line: ").append((file == null ? "unknown" : file) + ":" + line + ":" + col).append('\n'); - str.append(" Type: ").append(error == null ? "generic" : error.getName()).append('\n'); - - if(error != null) { - str.append(" Desc: ").append(error.getMessage() == null ? "null" : error.getMessage()).append('\n'); - } - - if(message != null) { - if(error == null || error.getMessage() == null || !message.endsWith(error.getMessage())) { - str.append(" Desc: ").append(message).append('\n'); + if(windowErrorHandler != null) { + error = TeaVMUtils.ensureDefined(error); + if(error == null) { + systemErr.println("ClientMain: [ERROR] recieved error event, but the error is null, ignoring"); + return; } + + StringBuilder str = new StringBuilder(); + + str.append("Native Browser Exception\n"); + str.append("----------------------------------\n"); + str.append(" Line: ").append((file == null ? "unknown" : file) + ":" + line + ":" + col).append('\n'); + str.append(" Type: ").append(error.getName()).append('\n'); + str.append(" Desc: ").append(error.getMessage() == null ? "null" : error.getMessage()).append('\n'); + + if(message != null) { + if(error.getMessage() == null || !message.endsWith(error.getMessage())) { + str.append(" Desc: ").append(message).append('\n'); + } + } + + str.append("----------------------------------\n\n"); + String stack = TeaVMUtils.getStackSafe(error); + if(PlatformRuntime.isDeobfStackTraces && !StringUtils.isAllEmpty(stack)) { + TeaVMRuntimeDeobfuscator.initialize(); + stack = TeaVMRuntimeDeobfuscator.deobfExceptionStack(stack); + } + str.append(stack == null ? "No stack trace is available" : stack).append('\n'); + + showCrashScreen(str.toString()); } - - str.append("----------------------------------\n\n"); - str.append(error.getStack() == null ? "No stack trace is available" : error.getStack()).append('\n'); - - showCrashScreen(str.toString()); } }); @@ -182,7 +217,15 @@ public class ClientMain { } catch (Throwable t) { } return; - } catch (Throwable t) { + }catch(TeaVMEnterBootMenuException ee) { + try { + systemOut.println("ClientMain: [INFO] launching eaglercraftx boot menu"); + BootMenuEntryPoint.launchMenu(Window.current(), configRootElement); + }catch(Throwable t) { + showCrashScreen("Failed to enter boot menu!", t); + } + return; + }catch(Throwable t) { systemErr.println("ClientMain: [ERROR] eaglercraftx's runtime could not be initialized!"); EagRuntime.debugPrintStackTraceToSTDERR(t); showCrashScreen("EaglercraftX's runtime could not be initialized!", t); @@ -204,10 +247,10 @@ public class ClientMain { systemErr.println("ClientMain: [ERROR] eaglercraftx main thread has exited"); } } - - @JSBody(params = {}, script = "if(typeof window.eaglercraftXOpts === \"undefined\") {return null;}" - + "else if(typeof window.eaglercraftXOpts === \"string\") {return JSON.parse(window.eaglercraftXOpts);}" - + "else {return window.eaglercraftXOpts;}") + + @JSBody(params = {}, script = "if(typeof eaglercraftXOpts === \"undefined\") {return null;}" + + "else if(typeof eaglercraftXOpts === \"string\") {return JSON.parse(eaglercraftXOpts);}" + + "else {return eaglercraftXOpts;}") private static native JSObject getEaglerXOpts(); public static class EPKFileEntry { @@ -231,15 +274,26 @@ public class ClientMain { private static interface WindowErrorHandler extends JSObject { void call(String message, String file, int line, int col, JSError error); } - - @JSBody(params = { "handler" }, script = "window.addEventListener(\"error\", function(e) { handler(" + + @JSBody(params = { "win", "handler" }, script = "var evtHandler = function(e) { handler(" + "(typeof e.message === \"string\") ? e.message : null," + "(typeof e.filename === \"string\") ? e.filename : null," + "(typeof e.lineno === \"number\") ? e.lineno : 0," + "(typeof e.colno === \"number\") ? e.colno : 0," - + "(typeof e.error === \"undefined\") ? null : e.error); });") - public static native void setWindowErrorHandler(WindowErrorHandler handler); - + + "(typeof e.error === \"undefined\") ? null : e.error);}; win.addEventListener(\"error\", evtHandler);" + + "return evtHandler;") + private static native JSObject setWindowErrorHandler(Window win, WindowErrorHandler handler); + + @JSBody(params = { "win", "handler" }, script = "win.removeEventListener(\"error\", evtHandler);") + private static native void removeWindowErrorHandler(Window win, JSObject handler); + + public static void removeErrorHandler(Window win) { + if(windowErrorHandler != null) { + removeWindowErrorHandler(win, windowErrorHandler); + windowErrorHandler = null; + } + } + public static void showCrashScreen(String message, Throwable t) { try { showCrashScreen(message + "\n\n" + EagRuntime.getStackTrace(t)); @@ -257,12 +311,16 @@ public class ClientMain { String strBefore = strBeforeBuilder.toString(); HTMLDocument doc = Window.current().getDocument(); - if(configRootElement == null) { - configRootElement = doc.getElementById(configRootElementId); + HTMLElement el; + if(PlatformRuntime.parent != null) { + el = PlatformRuntime.parent; + }else { + if(configRootElement == null) { + configRootElement = doc.getElementById(configRootElementId); + } + el = configRootElement; } - HTMLElement el = configRootElement; - StringBuilder str = new StringBuilder(); str.append("eaglercraft.version = \"").append(EaglercraftVersion.projectForkVersion).append("\"\n"); str.append("eaglercraft.minecraft = \"1.8.8\"\n"); @@ -271,11 +329,13 @@ public class ClientMain { str.append('\n'); str.append(addWebGLToCrash()); str.append('\n'); + str.append(addShimsToCrash()); + str.append('\n'); str.append("window.eaglercraftXOpts = "); str.append(TeaVMClientConfigAdapter.instance.toString()).append('\n'); str.append('\n'); str.append("currentTime = "); - str.append(EagRuntime.fixDateFormat(new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z")).format(new Date())).append('\n'); + str.append((new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z")).format(new Date())).append('\n'); str.append('\n'); addDebugNav(str, "userAgent"); addDebugNav(str, "vendor"); @@ -309,11 +369,11 @@ public class ClientMain { String strAfter = str.toString(); String strFinal = strBefore + strAfter; - List additionalInfo = new LinkedList(); + List additionalInfo = new LinkedList<>(); try { TeaVMClientConfigAdapter.instance.getHooks().callCrashReportHook(strFinal, additionalInfo::add); }catch(Throwable tt) { - System.err.println("Uncaught exception invoking crash report hook!"); + systemErr.println("Uncaught exception invoking crash report hook!"); EagRuntime.debugPrintStackTraceToSTDERR(tt); } @@ -333,48 +393,46 @@ public class ClientMain { builderFinal.append(strAfter); strFinal = builderFinal.toString(); }catch(Throwable tt) { - System.err.println("Uncaught exception concatenating crash report hook messages!"); + systemErr.println("Uncaught exception concatenating crash report hook messages!"); EagRuntime.debugPrintStackTraceToSTDERR(tt); } } if(el == null) { Window.alert("Root element not found, crash report was printed to console"); - System.err.println(strFinal); + systemErr.println(strFinal); return; } - - String s = el.getAttribute("style"); - el.setAttribute("style", (s == null ? "" : s) + "position:relative;"); + HTMLElement img = doc.createElement("img"); HTMLElement div = doc.createElement("div"); img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);"); img.setAttribute("src", crashImageWrapper()); - div.setAttribute("style", - "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 14px monospace;padding:10px;"); + div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 14px monospace;padding:10px;"); + div.getClassList().add("_eaglercraftX_crash_element"); el.appendChild(img); el.appendChild(div); div.appendChild(doc.createTextNode(strFinal)); PlatformRuntime.removeEventHandlers(); - } else { - System.err.println(); - System.err.println("An additional crash report was supressed:"); + }else { + systemErr.println(); + systemErr.println("An additional crash report was supressed:"); String[] s = t.split("[\\r\\n]+"); - for (int i = 0; i < s.length; ++i) { - System.err.println(" " + s[i]); + for(int i = 0; i < s.length; ++i) { + systemErr.println(" " + s[i]); } if(additionalInfo.size() > 0) { for(String str2 : additionalInfo) { if(str2 != null) { - System.err.println(); - System.err.println(" ----------[ CRASH HOOK ]----------"); + systemErr.println(); + systemErr.println(" ----------[ CRASH HOOK ]----------"); s = str2.split("[\\r\\n]+"); for(int i = 0; i < s.length; ++i) { - System.err.println(" " + s[i]); + systemErr.println(" " + s[i]); } - System.err.println(" ----------------------------------"); + systemErr.println(" ----------------------------------"); } } } @@ -388,51 +446,110 @@ public class ClientMain { return webGLCrashStringCache; } - StringBuilder ret = new StringBuilder(); - - WebGLRenderingContext ctx = PlatformRuntime.webgl; - - if (ctx == null) { - HTMLCanvasElement cvs = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); - - cvs.setWidth(64); - cvs.setHeight(64); + try { + StringBuilder ret = new StringBuilder(); - ctx = (WebGLRenderingContext)cvs.getContext("webgl2"); + WebGLRenderingContext ctx = PlatformRuntime.webgl; + boolean experimental = PlatformRuntime.webglExperimental; if(ctx == null) { - ctx = (WebGLRenderingContext)cvs.getContext("webgl"); + experimental = false; + HTMLCanvasElement cvs = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); + + cvs.setWidth(64); + cvs.setHeight(64); + + ctx = (WebGLRenderingContext)cvs.getContext("webgl2"); + + if(ctx == null) { + ctx = (WebGLRenderingContext)cvs.getContext("webgl"); + if(ctx == null) { + experimental = true; + ctx = (WebGLRenderingContext)cvs.getContext("experimental-webgl"); + } + } } - } - - if (ctx != null) { - if (PlatformRuntime.webgl != null) { - ret.append("webgl.version = ").append(ctx.getParameterString(WebGLRenderingContext.VERSION)) - .append('\n'); - } - if (ctx.getExtension("WEBGL_debug_renderer_info") != null) { - ret.append("webgl.renderer = ").append(ctx.getParameterString(/* UNMASKED_RENDERER_WEBGL */ 0x9246)) - .append('\n'); - ret.append("webgl.vendor = ").append(ctx.getParameterString(/* UNMASKED_VENDOR_WEBGL */ 0x9245)) - .append('\n'); - } else { - ret.append("webgl.renderer = ") - .append(ctx.getParameterString(WebGLRenderingContext.RENDERER) + " [masked]").append('\n'); - ret.append("webgl.vendor = ").append(ctx.getParameterString(WebGLRenderingContext.VENDOR) + " [masked]") - .append('\n'); - } - // ret.append('\n').append("\nwebgl.anisotropicGlitch = - // ").append(DetectAnisotropicGlitch.hasGlitch()).append('\n'); //TODO - ret.append('\n').append("webgl.ext.HDR16f = ") - .append(ctx.getExtension("EXT_color_buffer_half_float") != null).append('\n'); - ret.append("webgl.ext.HDR32f = ").append(ctx.getExtension("EXT_color_buffer_float") != null).append('\n'); - ret.append("webgl.ext.HDR32f_linear = ").append(ctx.getExtension("OES_texture_float_linear") != null).append('\n'); - }else { - ret.append("Failed to query GPU info!\n"); + if(ctx != null) { + if(PlatformRuntime.webgl != null) { + ret.append("webgl.version = ").append(ctx.getParameterString(WebGLRenderingContext.VERSION)).append('\n'); + } + if(ctx.getExtension("WEBGL_debug_renderer_info") != null) { + ret.append("webgl.renderer = ").append(ctx.getParameterString(/* UNMASKED_RENDERER_WEBGL */ 0x9246)).append('\n'); + ret.append("webgl.vendor = ").append(ctx.getParameterString(/* UNMASKED_VENDOR_WEBGL */ 0x9245)).append('\n'); + }else { + ret.append("webgl.renderer = ").append(ctx.getParameterString(WebGLRenderingContext.RENDERER)).append( " [masked]").append('\n'); + ret.append("webgl.vendor = ").append(ctx.getParameterString(WebGLRenderingContext.VENDOR)).append(" [masked]").append('\n'); + } + //ret.append('\n').append("\nwebgl.anisotropicGlitch = ").append(DetectAnisotropicGlitch.hasGlitch()).append('\n'); //TODO + int id = PlatformOpenGL.checkOpenGLESVersion(); + if(id > 0) { + ret.append('\n').append("webgl.version.id = ").append(id).append('\n'); + ret.append("webgl.experimental = ").append(experimental).append('\n'); + if(id == 200) { + ret.append("webgl.ext.ANGLE_instanced_arrays = ").append(ctx.getExtension("ANGLE_instanced_arrays") != null).append('\n'); + ret.append("webgl.ext.EXT_color_buffer_half_float = ").append(ctx.getExtension("EXT_color_buffer_half_float") != null).append('\n'); + ret.append("webgl.ext.EXT_shader_texture_lod = ").append(ctx.getExtension("EXT_shader_texture_lod") != null).append('\n'); + ret.append("webgl.ext.OES_fbo_render_mipmap = ").append(ctx.getExtension("OES_fbo_render_mipmap") != null).append('\n'); + ret.append("webgl.ext.OES_texture_float = ").append(ctx.getExtension("OES_texture_float") != null).append('\n'); + ret.append("webgl.ext.OES_texture_half_float = ").append(ctx.getExtension("OES_texture_half_float") != null).append('\n'); + ret.append("webgl.ext.OES_texture_half_float_linear = ").append(ctx.getExtension("OES_texture_half_float_linear") != null).append('\n'); + }else if(id >= 300) { + ret.append("webgl.ext.EXT_color_buffer_float = ").append(ctx.getExtension("EXT_color_buffer_float") != null).append('\n'); + ret.append("webgl.ext.EXT_color_buffer_half_float = ").append(ctx.getExtension("EXT_color_buffer_half_float") != null).append('\n'); + ret.append("webgl.ext.OES_texture_float_linear = ").append(ctx.getExtension("OES_texture_float_linear") != null).append('\n'); + } + ret.append("webgl.ext.EXT_texture_filter_anisotropic = ").append(ctx.getExtension("EXT_texture_filter_anisotropic") != null).append('\n'); + }else { + ret.append("webgl.ext.ANGLE_instanced_arrays = ").append(ctx.getExtension("ANGLE_instanced_arrays") != null).append('\n'); + ret.append("webgl.ext.EXT_color_buffer_float = ").append(ctx.getExtension("EXT_color_buffer_float") != null).append('\n'); + ret.append("webgl.ext.EXT_color_buffer_half_float = ").append(ctx.getExtension("EXT_color_buffer_half_float") != null).append('\n'); + ret.append("webgl.ext.EXT_shader_texture_lod = ").append(ctx.getExtension("EXT_shader_texture_lod") != null).append('\n'); + ret.append("webgl.ext.OES_fbo_render_mipmap = ").append(ctx.getExtension("OES_fbo_render_mipmap") != null).append('\n'); + ret.append("webgl.ext.OES_texture_float = ").append(ctx.getExtension("OES_texture_float") != null).append('\n'); + ret.append("webgl.ext.OES_texture_float_linear = ").append(ctx.getExtension("OES_texture_float_linear") != null).append('\n'); + ret.append("webgl.ext.OES_texture_half_float = ").append(ctx.getExtension("OES_texture_half_float") != null).append('\n'); + ret.append("webgl.ext.OES_texture_half_float_linear = ").append(ctx.getExtension("OES_texture_half_float_linear") != null).append('\n'); + ret.append("webgl.ext.EXT_texture_filter_anisotropic = ").append(ctx.getExtension("EXT_texture_filter_anisotropic") != null).append('\n'); + } + }else { + ret.append("Failed to query GPU info!\n"); + } + + return webGLCrashStringCache = ret.toString(); + }catch(Throwable tt) { + return webGLCrashStringCache = "ERROR: could not query webgl info - " + tt.toString() + "\n"; + } + } + + private static String shimsCrashStringCache = null; + + private static String addShimsToCrash() { + if(shimsCrashStringCache != null) { + return shimsCrashStringCache; } - return webGLCrashStringCache = ret.toString(); + try { + StringBuilder ret = new StringBuilder(); + + ES6ShimStatus status = ES6ShimStatus.getRuntimeStatus(); + ret.append("eaglercraft.es6shims.status = ").append(status.getStatus()).append('\n'); + ret.append("eaglercraft.es6shims.shims = [ "); + Set shims = status.getShims(); + boolean b = false; + for(EnumES6Shims shim : shims) { + if(b) { + ret.append(", "); + } + ret.append(shim); + b = true; + } + ret.append(" ]\n"); + + return shimsCrashStringCache = ret.toString(); + }catch(Throwable tt) { + return shimsCrashStringCache = "ERROR: could not query ES6 shim info - " + tt.toString() + "\n"; + } } public static void showIncompatibleScreen(String t) { @@ -440,13 +557,17 @@ public class ClientMain { isCrashed = true; HTMLDocument doc = Window.current().getDocument(); - if (configRootElement == null) { - configRootElement = doc.getElementById(configRootElementId); + HTMLElement el; + if(PlatformRuntime.parent != null) { + el = PlatformRuntime.parent; + }else { + if(configRootElement == null) { + configRootElement = doc.getElementById(configRootElementId); + } + el = configRootElement; } - - HTMLElement el = configRootElement; - - if (el == null) { + + if(el == null) { System.err.println("Compatibility error: " + t); return; } @@ -457,35 +578,30 @@ public class ClientMain { HTMLElement div = doc.createElement("div"); img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);"); img.setAttribute("src", crashImageWrapper()); - div.setAttribute("style", - "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;font:18px sans-serif;padding:40px;"); + div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;font:18px sans-serif;padding:40px;"); + div.getClassList().add("_eaglercraftX_incompatible_element"); el.appendChild(img); el.appendChild(div); - div.setInnerHTML( - "

+ This device is incompatible with Eaglercraft :(

" - + "
" - + "

Issue:

" - + "

" - + "

" - + "

Current Date: " - + EagRuntime.fixDateFormat(new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z")) - .format(new Date()) - + "

" - + "


Things you can try:

" - + "
    " - + "
  1. Just try using Eaglercraft on a different device, it isn't a bug it's common sense
  2. " - + "
  3. If you are on a mobile device, please try a proper desktop or a laptop computer
  4. " - + "
  5. If you are using a device with no mouse cursor, please use a device with a mouse cursor
  6. " - + "
  7. If you are not using Chrome/Edge, try installing the latest Google Chrome
  8. " - + "
  9. If your browser is out of date, please update it to the latest version
  10. " - + "
  11. If you are using an old OS such as Windows 7, please try Windows 10 or 11
  12. " - + "
  13. If you have a GPU launched before 2009, WebGL 2.0 support may be impossible
  14. " - + "
" - + "
"); - - div.querySelector("#crashReason").appendChild(doc.createTextNode(t)); - div.querySelector("#crashUserAgent").appendChild(doc.createTextNode(getStringNav("userAgent"))); - + div.setInnerHTML("

+ This device is incompatible with Eaglercraft :(

" + + "
" + + "

Issue:

" + + "

" + + "

" + + "

Current Date: " + (new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z")).format(new Date()) + "

" + + "


Things you can try:

" + + "
    " + + "
  1. Just try using Eaglercraft on a different device, it isn't a bug it's common sense
  2. " + + "
  3. If you are on a mobile device, please try a proper desktop or a laptop computer
  4. " + + "
  5. If you are using a device with no mouse cursor, please use a device with a mouse cursor
  6. " + + "
  7. If you are not using Chrome/Edge, try installing the latest Google Chrome
  8. " + + "
  9. If your browser is out of date, please update it to the latest version
  10. " + + "
  11. If you are using an old OS such as Windows 7, please try Windows 10 or 11
  12. " + + "
" + + "
"); + + div.querySelector("#_eaglercraftX_crashReason").appendChild(doc.createTextNode(t)); + div.querySelector("#_eaglercraftX_crashUserAgent").appendChild(doc.createTextNode(getStringNav("userAgent"))); + PlatformRuntime.removeEventHandlers(); String webGLRenderer = "No GL_RENDERER string could be queried"; @@ -514,9 +630,9 @@ public class ClientMain { } } catch (Throwable tt) { } - - div.querySelector("#crashWebGL").appendChild(doc.createTextNode(webGLRenderer)); - + + div.querySelector("#_eaglercraftX_crashWebGL").appendChild(doc.createTextNode(webGLRenderer)); + } } @@ -525,29 +641,33 @@ public class ClientMain { public static void showIntegratedServerCrashReportOverlay(String report, int x, int y, int w, int h) { if (integratedServerCrashPanel == null) { HTMLDocument doc = Window.current().getDocument(); - if (configRootElement == null) { - configRootElement = doc.getElementById(configRootElementId); + HTMLElement el; + if(PlatformRuntime.parent != null) { + el = PlatformRuntime.parent; + }else { + if(configRootElement == null) { + configRootElement = doc.getElementById(configRootElementId); + } + el = configRootElement; } integratedServerCrashPanel = doc.createElement("div"); - integratedServerCrashPanel.setAttribute("style", - "z-index:99;position:absolute;background-color:black;color:white;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font:18px sans-serif;padding:20px;display:none;"); - configRootElement.appendChild(integratedServerCrashPanel); + integratedServerCrashPanel.setAttribute("style", "z-index:99;position:absolute;background-color:black;color:white;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font:18px sans-serif;padding:20px;display:none;"); + integratedServerCrashPanel.getClassList().add("_eaglercraftX_integratedserver_crash_element"); + el.appendChild(integratedServerCrashPanel); } String sourceURL = ClientPlatformSingleplayer.getLoadedWorkerSourceURLTeaVM(); String workerURL = ClientPlatformSingleplayer.getLoadedWorkerURLTeaVM(); - String currentDate = EagRuntime.fixDateFormat(new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z")) - .format(new Date()); - if (workerURL != null) { - report = "WORKER SRC: " + sourceURL + "\nWORKER URL: " + workerURL + "\n\nCURRENT DATE: " + currentDate - + "\n\n" + report.replaceAll(workerURL, ""); - } else { + String currentDate = (new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z")).format(new Date()); + if(workerURL != null) { + report = "WORKER SRC: " + sourceURL +"\nWORKER URL: " + workerURL + "\n\nCURRENT DATE: " + currentDate + "\n\n" + report.replaceAll(workerURL, ""); + }else { report = "CURRENT DATE: " + currentDate + "\n\n" + report; } setInnerText(integratedServerCrashPanel, ""); setInnerText(integratedServerCrashPanel, report); CSSStyleDeclaration style = integratedServerCrashPanel.getStyle(); - float s = (float) Window.current().getDevicePixelRatio(); + float s = PlatformInput.getDPI(); style.setProperty("top", "" + (y / s) + "px"); style.setProperty("left", "" + (x / s) + "px"); style.setProperty("width", "" + ((w / s) - 20) + "px"); @@ -576,9 +696,9 @@ public class ClientMain { @JSBody(params = { "v" }, script = "try { return \"\"+window.location[v]; } catch(e) { return \"\"; }") private static native String getStringLocation(String var); - @JSBody(params = { }, script = "try { var retObj = new Array; if(typeof window.navigator.plugins === \"object\")" - + "{ var len = window.navigator.plugins.length; if(len > 0) { for(var idx = 0; idx < len; ++idx) {" - + "var thePlugin = window.navigator.plugins[idx]; retObj.push({ name: thePlugin.name," + @JSBody(params = { }, script = "try { var retObj = new Array; if(typeof navigator.plugins === \"object\")" + + "{ var len = navigator.plugins.length; if(len > 0) { for(var idx = 0; idx < len; ++idx) {" + + "var thePlugin = navigator.plugins[idx]; retObj.push({ name: thePlugin.name," + "filename: thePlugin.filename, desc: thePlugin.description }); } } } return JSON.stringify(retObj);" + "} catch(e) { return \"\"; }") private static native String getStringNavPlugins(); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java index a2ffd40..368a5ba 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java @@ -1,189 +1,225 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm; - -import java.util.LinkedList; -import java.util.List; - -import org.teavm.jso.JSBody; -import org.teavm.jso.browser.Window; -import org.teavm.jso.dom.events.Event; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.dom.html.HTMLBodyElement; -import org.teavm.jso.dom.html.HTMLCollection; -import org.teavm.jso.dom.html.HTMLDocument; -import org.teavm.jso.dom.html.HTMLElement; - -import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class DebugConsoleWindow { - - private static class LogMessage { - - private final boolean err; - private final String msg; - - public LogMessage(boolean err, String msg) { - this.err = err; - this.msg = msg; - } - - } - - private static final int bufferSpoolSize = 256; - private static final int windowMaxMessages = 2048; - - private static final List messageBuffer = new LinkedList(); - - public static Window parent = null; - public static Window logger = null; - private static HTMLDocument loggerDoc = null; - private static HTMLBodyElement loggerBody = null; - private static HTMLElement loggerMessageContainer = null; - - public static void initialize(Window parentWindow) { - parent = parentWindow; - parent.addEventListener("unload", new EventListener() { - @Override - public void handleEvent(Event evt) { - destroyWindow(); - } - }); - if(parent.getLocalStorage() != null && "true".equals(parent.getLocalStorage().getItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole"))) { - showDebugConsole0(); - } - } - - public static void showDebugConsole() { - if(parent.getLocalStorage() != null) { - parent.getLocalStorage().setItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole", "true"); - } - showDebugConsole0(); - } - - @JSBody(params = { "doc", "str" }, script = "doc.write(str);doc.close();") - private static native void documentWrite(HTMLDocument doc, String str); - - private static void showDebugConsole0() { - if (logger == null) { - int w = (int) (1000 * parent.getDevicePixelRatio()); - int h = (int) (400 * parent.getDevicePixelRatio()); - int x = (parent.getScreen().getWidth() - w) / 2; - int y = (parent.getScreen().getHeight() - h) / 2; - logger = parent.open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h - + ",menubar=0,status=0,titlebar=0,toolbar=0"); - if (logger == null) { - LogManager.getLogger("DebugConsoleWindow").error("Logger popup was blocked!"); - Window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!"); - return; - } - logger.focus(); - documentWrite(logger.getDocument(), - "" - + "Debug Console" - + "

"); - loggerDoc = logger.getDocument(); - loggerBody = loggerDoc.getBody(); - loggerMessageContainer = loggerDoc.getElementById("loggerMessageContainer"); - synchronized (messageBuffer) { - for (LogMessage msg : messageBuffer) { - appendLogMessage(msg.msg + "\n", msg.err ? "#DD0000" : "#000000"); - } - messageBuffer.clear(); - } - scrollToEnd0(logger, loggerDoc); - EventListener unloadListener = new EventListener() { - @Override - public void handleEvent(Event evt) { - if (logger != null) { - logger = null; - if(parent.getLocalStorage() != null) { - parent.getLocalStorage().setItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole", "false"); - } - } - } - }; - logger.addEventListener("beforeunload", unloadListener); - logger.addEventListener("unload", unloadListener); - } else { - logger.focus(); - } - } - - public static void addLogMessage(String text, boolean isErr) { - if (logger == null) { - synchronized (messageBuffer) { - if (logger == null) { - messageBuffer.add(new LogMessage(isErr, text)); - while (messageBuffer.size() > bufferSpoolSize) { - messageBuffer.remove(0); - } - return; - } - } - } - appendLogMessageAndScroll(text + "\n", isErr ? "#DD0000" : "#000000"); - } - - private static void appendLogMessageAndScroll(String text, String color) { - boolean b = isScrollToEnd(logger, loggerDoc); - appendLogMessage(text, color); - if (b) { - scrollToEnd0(logger, loggerDoc); - } - } - - private static void appendLogMessage(String text, String color) { - HTMLElement el = loggerDoc.createElement("span"); - el.setInnerText(text); - el.getStyle().setProperty("color", color); - loggerMessageContainer.appendChild(el); - HTMLCollection children = loggerMessageContainer.getChildren(); - while (children.getLength() > windowMaxMessages) { - children.get(0).delete(); - } - } - - @JSBody(params = { "win", "doc" }, script = "return (win.innerHeight + win.pageYOffset) >= doc.body.offsetHeight;") - private static native boolean isScrollToEnd(Window win, HTMLDocument doc); - - @JSBody(params = { "win", - "doc" }, script = "setTimeout(function(){win.scrollTo(0, doc.body.scrollHeight || doc.body.clientHeight);}, 1);") - private static native void scrollToEnd0(Window win, HTMLDocument doc); - - public static void destroyWindow() { - if (logger != null) { - Window w = logger; - logger = null; - w.close(); - } - } - - public static boolean isShowingDebugConsole() { - return logger != null; - } - -} +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.LinkedList; +import java.util.List; + +import org.teavm.jso.JSBody; +import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.html.HTMLBodyElement; +import org.teavm.jso.dom.html.HTMLCollection; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLElement; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class DebugConsoleWindow { + + private static class LogMessage { + + private final boolean err; + private final String msg; + + public LogMessage(boolean err, String msg) { + this.err = err; + this.msg = msg; + } + + } + + private static final int bufferSpoolSize = 256; + private static final int windowMaxMessages = 2048; + + private static final List messageBuffer = new LinkedList<>(); + + public static Window parent = null; + public static Window logger = null; + private static HTMLDocument loggerDoc = null; + private static HTMLBodyElement loggerBody = null; + private static HTMLElement loggerMessageContainer = null; + private static EventListener unload = null; + private static String unloadName = null; + + public static void initialize(Window parentWindow) { + parent = parentWindow; + if (PlatformRuntime.getClientConfigAdapter().isOpenDebugConsoleOnLaunch() || debugConsoleLocalStorageGet()) { + showDebugConsole0(); + } + } + + public static void removeEventListeners() { + if(unloadName != null && unload != null) { + try { + parent.removeEventListener(unloadName, unload); + }catch(Throwable t) { + } + } + unload = null; + unloadName = null; + } + + private static void debugConsoleLocalStorageSet(boolean val) { + try { + if(parent.getLocalStorage() != null) { + parent.getLocalStorage().setItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole", Boolean.toString(val)); + } + }catch(Throwable t) { + } + } + + private static boolean debugConsoleLocalStorageGet() { + try { + if(parent.getLocalStorage() != null) { + return Boolean.valueOf(parent.getLocalStorage().getItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole")); + }else { + return false; + } + }catch(Throwable t) { + return false; + } + } + + public static void showDebugConsole() { + debugConsoleLocalStorageSet(true); + showDebugConsole0(); + } + + @JSBody(params = { "doc", "str" }, script = "doc.write(str);doc.close();") + private static native void documentWrite(HTMLDocument doc, String str); + + private static void showDebugConsole0() { + if(logger == null) { + try { + parent.addEventListener( + unloadName = ((TeaVMClientConfigAdapter) PlatformRuntime.getClientConfigAdapter()) + .isFixDebugConsoleUnloadListenerTeaVM() ? "beforeunload" : "unload", + unload = new EventListener() { + @Override + public void handleEvent(Event evt) { + destroyWindow(); + } + }); + }catch(Throwable t) { + } + float s = PlatformInput.getDPI(); + int w = (int)(1000 * s); + int h = (int)(400 * s); + int x = (parent.getScreen().getWidth() - w) / 2; + int y = (parent.getScreen().getHeight() - h) / 2; + logger = parent.open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h + ",menubar=0,status=0,titlebar=0,toolbar=0"); + if(logger == null || TeaVMUtils.isNotTruthy(logger)) { + logger = null; + debugConsoleLocalStorageSet(false); + LogManager.getLogger("DebugConsoleWindow").error("Logger popup was blocked!"); + Window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!"); + return; + } + logger.focus(); + documentWrite(logger.getDocument(), "" + + "Debug Console" + + "

"); + loggerDoc = logger.getDocument(); + loggerBody = loggerDoc.getBody(); + loggerMessageContainer = loggerDoc.getElementById("loggerMessageContainer"); + synchronized(messageBuffer) { + for(LogMessage msg : messageBuffer) { + appendLogMessage(msg.msg + "\n", msg.err ? "#DD0000" : "#000000"); + } + messageBuffer.clear(); + } + scrollToEnd0(logger, loggerDoc); + EventListener unloadListener = new EventListener() { + @Override + public void handleEvent(Event evt) { + if(logger != null) { + logger = null; + debugConsoleLocalStorageSet(false); + removeEventListeners(); + } + } + }; + logger.addEventListener("beforeunload", unloadListener); + logger.addEventListener("unload", unloadListener); + }else { + logger.focus(); + } + } + + public static void addLogMessage(String text, boolean isErr) { + if(logger == null) { + synchronized(messageBuffer) { + if(logger == null) { + messageBuffer.add(new LogMessage(isErr, text)); + while(messageBuffer.size() > bufferSpoolSize) { + messageBuffer.remove(0); + } + return; + } + } + } + appendLogMessageAndScroll(text + "\n", isErr ? "#DD0000" : "#000000"); + } + + private static void appendLogMessageAndScroll(String text, String color) { + if(logger != null) { + boolean b = isScrollToEnd(logger, loggerDoc); + appendLogMessage(text, color); + if(b) { + scrollToEnd0(logger, loggerDoc); + } + } + } + + private static void appendLogMessage(String text, String color) { + HTMLElement el = loggerDoc.createElement("span"); + el.setInnerText(text); + el.getStyle().setProperty("color", color); + loggerMessageContainer.appendChild(el); + HTMLCollection children = loggerMessageContainer.getChildren(); + while(children.getLength() > windowMaxMessages) { + children.get(0).delete(); + } + } + + @JSBody(params = { "win", "doc" }, script = "return (win.innerHeight + win.pageYOffset) >= doc.body.offsetHeight;") + private static native boolean isScrollToEnd(Window win, HTMLDocument doc); + + @JSBody(params = { "win", "doc" }, script = "setTimeout(function(){win.scrollTo(0, doc.body.scrollHeight || doc.body.clientHeight);}, 1);") + private static native void scrollToEnd0(Window win, HTMLDocument doc); + + public static void destroyWindow() { + if(logger != null) { + Window w = logger; + logger = null; + try { + w.close(); + }catch(Throwable t) { + } + removeEventListeners(); + } + } + + public static boolean isShowingDebugConsole() { + return logger != null; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ES6ShimStatus.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ES6ShimStatus.java new file mode 100755 index 0000000..31b4a02 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ES6ShimStatus.java @@ -0,0 +1,82 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.EnumSet; +import java.util.Set; + +import org.teavm.jso.JSBody; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ES6ShimStatus { + + private static final Logger logger = LogManager.getLogger("ES6ShimStatus"); + + private static ES6ShimStatus instance = null; + + @JSBody(params = { }, script = "return (typeof __eaglercraftXES6ShimStatus === \"object\") ? __eaglercraftXES6ShimStatus : null;") + private static native ES6ShimStatusJS getRuntimeStatus0(); + + public static ES6ShimStatus getRuntimeStatus() { + if(instance == null) { + return instance = new ES6ShimStatus(getRuntimeStatus0()); + } + ES6ShimStatusJS jsImpl = getRuntimeStatus0(); + if(instance.impl != jsImpl) { + instance = new ES6ShimStatus(jsImpl); + } + return instance; + } + + private final ES6ShimStatusJS impl; + private final EnumES6ShimStatus status; + private final Set shims; + + public ES6ShimStatus(ES6ShimStatusJS impl) { + this.impl = impl; + if(impl != null && TeaVMUtils.isTruthy(impl)) { + this.status = EnumES6ShimStatus.getStatusById(impl.getShimInitStatus()); + this.shims = EnumSet.noneOf(EnumES6Shims.class); + for(int i = 0, id, l = impl.getEnabledShimCount(); i < l; ++i) { + id = impl.getEnabledShimID(i); + EnumES6Shims theShim = EnumES6Shims.getShimById(id); + if(theShim != null) { + this.shims.add(theShim); + }else { + logger.warn("Ignoring unknown shim id: {}", id); + } + } + }else { + this.status = EnumES6ShimStatus.STATUS_NOT_PRESENT; + this.shims = EnumSet.noneOf(EnumES6Shims.class); + } + } + + public ES6ShimStatusJS getImpl() { + return impl; + } + + public EnumES6ShimStatus getStatus() { + return status; + } + + public Set getShims() { + return shims; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ES6ShimStatusJS.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ES6ShimStatusJS.java new file mode 100755 index 0000000..49f08be --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ES6ShimStatusJS.java @@ -0,0 +1,52 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSObject; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface ES6ShimStatusJS extends JSObject { + + public static final int INIT_STATUS_ERROR = -1; + public static final int INIT_STATUS_DISABLED = 0; + public static final int INIT_STATUS_ENABLED = 1; + public static final int INIT_STATUS_DISABLED_ERRORS = 2; + public static final int INIT_STATUS_ENABLED_ERRORS = 3; + + public static final int SHIM_MAP = 0; + public static final int SHIM_WEAKMAP = 1; + public static final int SHIM_SET = 2; + public static final int SHIM_WEAKSET = 3; + public static final int SHIM_PROMISE = 4; + public static final int SHIM_STRING_FROM_CODE_POINT = 5; + public static final int SHIM_STRING_CODE_POINT_AT = 6; + public static final int SHIM_STRING_STARTS_WITH = 7; + public static final int SHIM_STRING_ENDS_WITH = 8; + public static final int SHIM_STRING_INCLUDES = 9; + public static final int SHIM_STRING_REPEAT = 10; + public static final int SHIM_ARRAY_FILL = 11; + public static final int SHIM_OBJECT_IS = 12; + public static final int SHIM_OBJECT_SET_PROTOTYPE_OF = 13; + public static final int SHIM_FUNCTION_NAME = 14; + public static final int SHIM_MATH_SIGN = 15; + public static final int SHIM_SYMBOL = 16; + + int getShimInitStatus(); + + int getEnabledShimCount(); + + int getEnabledShimID(int idx); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EarlyLoadScreen.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EarlyLoadScreen.java index fed1b07..d0d436f 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EarlyLoadScreen.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EarlyLoadScreen.java @@ -1,254 +1,410 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm; - -import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; -import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; -import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; -import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; -import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; - -import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import net.lax1dude.eaglercraft.v1_8.Base64; -import net.lax1dude.eaglercraft.v1_8.EagUtils; - -/** - * Copyright (c) 2022 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class EarlyLoadScreen { - - public static final String loadScreen = "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHx0lEQVR42u3da27jIBRAYbfqFp1FuovM/GLEMIDBhsRJviNVapsYY8y5vPz4ut/v9wX4UL4VAQgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAMBr86MI3ovf39/i/9Z1XdZ1VUgEeN/Kf7vdqt8hgC7QW6OCE+CjK/+2bcv9fieCLtDjux9x/1t/u1xOveWSlisBXmQASoB/+fr6+vv7/X7vHteE8hxZrrpAkyo/2mU42soSgAAfN8YZ3aoSQOV/GNu2ZX9vGdjPEuBnVmXIVYqePly8famCne0TtuS1tt/a9kfSbWnqZw2u9yQesc91XZv7/iO2a+I+iG3b7uu63pdl2f1Z17WaTksaaXrbtk3JaynvR/O5l6/WtPaON3d8tf3v7e9d+RkVPeIVyDRKpREtfL+nGdxL7/f3d9m2bTdS5VZL4/Rz0fcRszm32604jZrLUyi/UXlb1/WlunKhTE63iCMif0tkao1IaXqlqFWKlr2RsTUPpXRLrUnYpqVlircfdby9LUCpbHpa1lyeW8tgL51SmZ9N+2dE5GqJlrkI0xJxaumV0ixt0xrd07TDdrl+aDoeGNnfbzne0RE1HqSOaF3SljptyXP7qF3QN3zi4Yw9LdF0r5+Zs7u175mLirU85KJiLbK3pt2bj1qZ1CJaz356WoD0u2ejaq11XNf1708uf73jqqeOAXotbIlgZ/t0tfSPRulZ050j0jubRjz2CGU/clyRRvvwv1LPIR4X5r6TtlJPmwY9W5la54vfea5+Zhm2dnniyj+j3GtdxCsMzL+vWAmuyujK2dLXnVGGYSZsduXPlV0625Vbk0nlnFlXhrYAezdjPFOa2sD4GRetlY5hdhnmpoHjKcXZlb927Llp4JCvWYHy8leDxpHgbCH0zBo9s3vyiLK8QiBIxwiPaHWnjwFGZbjl9r5RAtxut92Fp5GLTqPHP735qpXDrK5QbjFz27b/Wp802IXu2Yz6cGoadDmwCHV0enVJFpbCfkqLQ6Mvg9g7riPToEfyfrYMl4ZLOUadw1rZh33H/ytNjcbnunfavakeX02As3P1rZVoT4KeVdBXESDN05HV4pFXDaQrxqkE6TnISfC0dYAZA5PSSu3orkeYiSil/Sl3cm3b9t+NKbMHxHtTpenvcT7C33Gez+b1e3QFvvrUY2nhZ/Qi0KtMC+f6/KWpytnnsjWoXuKWyNaZkyud/HTh55mVvTYt++h8zDiXlTFnkwS1wfhlBZgxj917acNe9H9mZWuJvjPuez0azJ5RPj1T3kMe/zJyUNMzkMpdJts6MNybyckNXo/cwLI0XtZ8ZkaldBwt2x65RHvGMRwZoO9dWLh3CfqofC0zZhtKU5fpiWkVIE4n3b423Zemf0SA5cQdVenxt9x70FJ+8TEfkbxUuXqDytnp0L2p0kewzJjeOnMSWtKKt92rQCNageXEDTot05xH1iZy5Xf2lsra9iMrZDjW2dG9ha/7wLuNS5ctpDevt9y2WBu0ptvnxh2l75YutOrtu+/1m+N8tw66022PlGHrcfVuP+NCwNrg+2ETFPcPI45yLSu8s1Yg8UY3xb8K6WP2WualrzJjhDl8f2Ll721iPeiWAG8hwMw+LQhw6co/cpWaPO/DR4wBchU23APQMiMy43EhuAZDp0FfaQxwRCJjAQK8xTigp0uk4hPgowbH+vkEAD4GL8gAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAK7NJR6M9S6PLQzPHZr1sulSuXmCxQu3APHz+sNP6wOspr09/CL76ym3Tzr2t2sBHhk13+UYwgsmnvFeXwI8qUtRinZxZNq27e/3tm3Lvg8gjWRpxc09Rj3eb2l/ufTiZ5CG78Sfn305eO7durX8tH4W8pB+Pz32vTQJcGAcED+0Nv5//Pbw9GTl+sKh8sVRMo2WoWkPJy0WpiRB6XVFpa5IvF28v3RfvX36mpylBwKXPktbkjiI1I69liYBTg6E4wqTkyOWolRB4nTSE5XuszaI3dvfngRppM1F+9auTG4fuW1raeXendYiWk+aBBjQf44jZW/TWoriV3gRddwi9L57IPfY9lA5Q3nF6YZyq33WIkLt/NTSJMCAcUD4/Wzhxt2o3Hjg0a3emSdPt7Q2t9vtn3KrfXY0L7U091rWo599xBggjSgh0pSa79aTl4ugaR8913qU9ld6vWlvd6bn+7mB+96MUHpcLULtHftemlqAAwKEwVd6MtNBbK4C7kWLuMkuDT5zA+za/nKzMC0VOu0CtXQhal2UeKCfG2PUPsvNZrUcey3NV8Dj0Z/cvctNQ77DmogWAM0S7M0gQQvwluS6HFZ0CQA8DJdDgwAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAYBlWf4A1W4Hx65cJAoAAAAASUVORK5CYII="; - public static final String enableScreen = "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAC4jAAAuIwF4pT92AAAEAklEQVR42u2dvXbjIBBG7T0+xw+gTp06v//LmE6dO/VR5a3wGZNh+BGSFeveJgkIBrDy8TGKds8/Pz/PExyW8/P55AY4MP9YgmNzmeeZVUABAA8AKADgAQAFADwAoACABwAUAPAAgAIAHgBQAMADAAoAeABAAY7LOI7fpQDX65VPtZCt18w5d7rdbigAbOgBxnE8DcPwJnnDMCTrNJlsUVcizTnj9HWxeVvINfN9y361OdTEk30551ZZt3PsvYDYxOSChoPQ6sJ21mRLBm61jY0lpy61gDKWNdfcNcv5wErWLbfPF88I9/s9WtayzopXS85YtPqcMeT23SqedV1pucal1V4iTUooV/IaWSfbWHU5JmkvpmzrsayaB9DqfJnVTpMff72sc869/WzVlcjjOI7mOOVYfBzfT05exLfT5pqae008a71Ly6tPASV79CfPylvFjpm+teLH+tXiF5nA2LOAUMpCibckWpPBUOJT20btFuDjyK8p+S45Z4fX+ti+LDb3pef62PosWbfkDbBW8mFPhB/gt8Vr7gG+kZK9+C/GM2+ArffnnKRHbT5gSdJoK0+ydrziGyCW115LolLxnHOr59q3lt89b6U8Czg4pgdI5bUtKY3VzfOclGBtTLVSmmqn1cdyC7Iud+5791KX1MLJDz3Mg2s59pK6sM/asdTmLrRx5pzjS+e+awWw9lstVeuv1/a10rqwT8sn5LQr8RzaMVfmKrR2qfnFjs57/puLS0nyoTZp0fL8XGq+ap8v4AES+3Msx74kN2/tmblewWoXPl9o+RykZH5/5hTQYv+y+vj084XcPHpJbHmt1s7yGbV1q+UBnHO/gnoZje2RmuzK/Vr2F3sWEF6TGkvutqH5CG08qTmk5u77tLyK5Qtq62rgxRA8AO8FHBkygQeHLQAFADwAoACABwAUAPAAgAIAHgBQAMADAAoAeABAAQAPACgA4AEABQA8AKAAgAcAFAC+3gNM03Tqum7VQSyN4dtvMdZDKcBWC9oqhr8JoIEHeDwep77vf5VJfL0vl9fLa/u+f+vPfx9eszSGNXZo5AH6vlcXW36gsqykrzViwAIPYL3r3nXd63v5m6i9J2+VaT8viWGNHZQbYE97+KdjHPIGKH0XPSyL7eXSjPk2YZlsN03Tq21OjLAs598ZggIT2MpMbW3IMICFN0Dsv4xpfUbfAvIAK9wAcOAtAMgDwJHzAIACAB4AUADAAwAKAHgAQAEADwAoAOABAAUAPACgAIAHABQA8ACAAgAeAFAAwAMACgB4AEABAA8AKADgAQAFADwAoACABwAUAPAAgAIAHgBQAMADAAoAeABAAQAPACgA4AEABQA8AKAAgAcAFADwANCe/0of1jQ8XY5YAAAAAElFTkSuQmCC"; - - private static IBufferGL vbo = null; - private static IProgramGL program = null; - - public static void paintScreen() { - - ITextureGL tex = _wglGenTextures(); - _wglActiveTexture(GL_TEXTURE0); - _wglBindTexture(GL_TEXTURE_2D, tex); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(loadScreen)); - ByteBuffer upload = PlatformRuntime.allocateByteBuffer(192*192*4); - IntBuffer pixelUpload = upload.asIntBuffer(); - pixelUpload.put(img.pixels); - pixelUpload.flip(); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 192, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelUpload); - - FloatBuffer vertexUpload = upload.asFloatBuffer(); - vertexUpload.clear(); - vertexUpload.put(0.0f); vertexUpload.put(0.0f); - vertexUpload.put(0.0f); vertexUpload.put(1.0f); - vertexUpload.put(1.0f); vertexUpload.put(0.0f); - vertexUpload.put(1.0f); vertexUpload.put(0.0f); - vertexUpload.put(0.0f); vertexUpload.put(1.0f); - vertexUpload.put(1.0f); vertexUpload.put(1.0f); - vertexUpload.flip(); - - vbo = _wglGenBuffers(); - _wglBindBuffer(GL_ARRAY_BUFFER, vbo); - _wglBufferData(GL_ARRAY_BUFFER, vertexUpload, GL_STATIC_DRAW); - - PlatformRuntime.freeByteBuffer(upload); - - IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER); - _wglShaderSource(vert, "#version 300 es\nprecision lowp float; layout(location = 0) in vec2 a_pos; out vec2 v_pos; void main() { gl_Position = vec4(((v_pos = a_pos) - 0.5) * vec2(2.0, -2.0), 0.0, 1.0); }"); - _wglCompileShader(vert); - - IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); - _wglShaderSource(frag, "#version 300 es\nprecision lowp float; in vec2 v_pos; layout(location = 0) out vec4 fragColor; uniform sampler2D tex; uniform vec2 aspect; void main() { fragColor = vec4(texture(tex, clamp(v_pos * aspect - ((aspect - 1.0) * 0.5), 0.02, 0.98)).rgb, 1.0); }"); - _wglCompileShader(frag); - - program = _wglCreateProgram(); - - _wglAttachShader(program, vert); - _wglAttachShader(program, frag); - _wglLinkProgram(program); - _wglDetachShader(program, vert); - _wglDetachShader(program, frag); - _wglDeleteShader(vert); - _wglDeleteShader(frag); - - _wglUseProgram(program); - _wglUniform1i(_wglGetUniformLocation(program, "tex"), 0); - - int width = PlatformInput.getWindowWidth(); - int height = PlatformInput.getWindowHeight(); - float x, y; - if(width > height) { - x = (float)width / (float)height; - y = 1.0f; - }else { - x = 1.0f; - y = (float)height / (float)width; - } - - _wglActiveTexture(GL_TEXTURE0); - _wglBindTexture(GL_TEXTURE_2D, tex); - - _wglViewport(0, 0, width, height); - _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); - _wglClear(GL_COLOR_BUFFER_BIT); - - _wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y); - - IBufferArrayGL vao = _wglGenVertexArrays(); - _wglBindVertexArray(vao); - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); - _wglDrawArrays(GL_TRIANGLES, 0, 6); - _wglDisableVertexAttribArray(0); - - PlatformInput.update(); - EagUtils.sleep(50l); // allow webgl to flush - - _wglUseProgram(null); - _wglBindBuffer(GL_ARRAY_BUFFER, null); - _wglBindTexture(GL_TEXTURE_2D, null); - _wglDeleteTextures(tex); - _wglDeleteVertexArrays(vao); - } - - public static void paintEnable() { - - ITextureGL tex = _wglGenTextures(); - _wglActiveTexture(GL_TEXTURE0); - _wglBindTexture(GL_TEXTURE_2D, tex); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(enableScreen)); - IntBuffer upload = PlatformRuntime.allocateIntBuffer(128*128); - upload.put(img.pixels); - upload.flip(); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, upload); - - PlatformRuntime.freeIntBuffer(upload); - - _wglUseProgram(program); - - int width = PlatformInput.getWindowWidth(); - int height = PlatformInput.getWindowHeight(); - float x, y; - if(width > height) { - x = (float)width / (float)height; - y = 1.0f; - }else { - x = 1.0f; - y = (float)height / (float)width; - } - - _wglActiveTexture(GL_TEXTURE0); - _wglBindTexture(GL_TEXTURE_2D, tex); - - _wglViewport(0, 0, width, height); - _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); - _wglClear(GL_COLOR_BUFFER_BIT); - - _wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y); - - IBufferArrayGL vao = _wglGenVertexArrays(); - _wglBindVertexArray(vao); - _wglBindBuffer(GL_ARRAY_BUFFER, vbo); - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); - _wglDrawArrays(GL_TRIANGLES, 0, 6); - _wglDisableVertexAttribArray(0); - - PlatformInput.update(); - EagUtils.sleep(50l); // allow webgl to flush - - _wglUseProgram(null); - _wglBindBuffer(GL_ARRAY_BUFFER, null); - _wglBindTexture(GL_TEXTURE_2D, null); - _wglDeleteTextures(tex); - _wglDeleteVertexArrays(vao); - - } - - public static void paintFinal(byte[] image) { - ITextureGL tex = _wglGenTextures(); - _wglActiveTexture(GL_TEXTURE0); - _wglBindTexture(GL_TEXTURE_2D, tex); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - ImageData img = PlatformAssets.loadImageFile(image); - IntBuffer upload = PlatformRuntime.allocateIntBuffer(256*256); - upload.put(img.pixels); - upload.flip(); - _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, upload); - - PlatformRuntime.freeIntBuffer(upload); - - _wglUseProgram(program); - - int width = PlatformInput.getWindowWidth(); - int height = PlatformInput.getWindowHeight(); - float x, y; - if(width > height) { - x = (float)width / (float)height; - y = 1.0f; - }else { - x = 1.0f; - y = (float)height / (float)width; - } - - _wglActiveTexture(GL_TEXTURE0); - _wglBindTexture(GL_TEXTURE_2D, tex); - - _wglViewport(0, 0, width, height); - _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); - _wglClear(GL_COLOR_BUFFER_BIT); - - _wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y); - - IBufferArrayGL vao = _wglGenVertexArrays(); - _wglBindVertexArray(vao); - _wglBindBuffer(GL_ARRAY_BUFFER, vbo); - _wglEnableVertexAttribArray(0); - _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); - _wglDrawArrays(GL_TRIANGLES, 0, 6); - _wglDisableVertexAttribArray(0); - - PlatformInput.update(); - EagUtils.sleep(50l); // allow webgl to flush - - _wglUseProgram(null); - _wglBindBuffer(GL_ARRAY_BUFFER, null); - _wglBindTexture(GL_TEXTURE_2D, null); - _wglDeleteTextures(tex); - _wglDeleteVertexArrays(vao); - } - - public static void destroy() { - _wglDeleteBuffers(vbo); - _wglDeleteProgram(program); - } -} +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EagUtils; + +/** + * Copyright (c) 2022 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EarlyLoadScreen { + + public static final String loadScreen = "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHx0lEQVR42u3da27jIBRAYbfqFp1FuovM/GLEMIDBhsRJviNVapsYY8y5vPz4ut/v9wX4UL4VAQgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAMBr86MI3ovf39/i/9Z1XdZ1VUgEeN/Kf7vdqt8hgC7QW6OCE+CjK/+2bcv9fieCLtDjux9x/1t/u1xOveWSlisBXmQASoB/+fr6+vv7/X7vHteE8hxZrrpAkyo/2mU42soSgAAfN8YZ3aoSQOV/GNu2ZX9vGdjPEuBnVmXIVYqePly8famCne0TtuS1tt/a9kfSbWnqZw2u9yQesc91XZv7/iO2a+I+iG3b7uu63pdl2f1Z17WaTksaaXrbtk3JaynvR/O5l6/WtPaON3d8tf3v7e9d+RkVPeIVyDRKpREtfL+nGdxL7/f3d9m2bTdS5VZL4/Rz0fcRszm32604jZrLUyi/UXlb1/WlunKhTE63iCMif0tkao1IaXqlqFWKlr2RsTUPpXRLrUnYpqVlircfdby9LUCpbHpa1lyeW8tgL51SmZ9N+2dE5GqJlrkI0xJxaumV0ixt0xrd07TDdrl+aDoeGNnfbzne0RE1HqSOaF3SljptyXP7qF3QN3zi4Yw9LdF0r5+Zs7u175mLirU85KJiLbK3pt2bj1qZ1CJaz356WoD0u2ejaq11XNf1708uf73jqqeOAXotbIlgZ/t0tfSPRulZ050j0jubRjz2CGU/clyRRvvwv1LPIR4X5r6TtlJPmwY9W5la54vfea5+Zhm2dnniyj+j3GtdxCsMzL+vWAmuyujK2dLXnVGGYSZsduXPlV0625Vbk0nlnFlXhrYAezdjPFOa2sD4GRetlY5hdhnmpoHjKcXZlb927Llp4JCvWYHy8leDxpHgbCH0zBo9s3vyiLK8QiBIxwiPaHWnjwFGZbjl9r5RAtxut92Fp5GLTqPHP735qpXDrK5QbjFz27b/Wp802IXu2Yz6cGoadDmwCHV0enVJFpbCfkqLQ6Mvg9g7riPToEfyfrYMl4ZLOUadw1rZh33H/ytNjcbnunfavakeX02As3P1rZVoT4KeVdBXESDN05HV4pFXDaQrxqkE6TnISfC0dYAZA5PSSu3orkeYiSil/Sl3cm3b9t+NKbMHxHtTpenvcT7C33Gez+b1e3QFvvrUY2nhZ/Qi0KtMC+f6/KWpytnnsjWoXuKWyNaZkyud/HTh55mVvTYt++h8zDiXlTFnkwS1wfhlBZgxj917acNe9H9mZWuJvjPuez0azJ5RPj1T3kMe/zJyUNMzkMpdJts6MNybyckNXo/cwLI0XtZ8ZkaldBwt2x65RHvGMRwZoO9dWLh3CfqofC0zZhtKU5fpiWkVIE4n3b423Zemf0SA5cQdVenxt9x70FJ+8TEfkbxUuXqDytnp0L2p0kewzJjeOnMSWtKKt92rQCNageXEDTot05xH1iZy5Xf2lsra9iMrZDjW2dG9ha/7wLuNS5ctpDevt9y2WBu0ptvnxh2l75YutOrtu+/1m+N8tw66022PlGHrcfVuP+NCwNrg+2ETFPcPI45yLSu8s1Yg8UY3xb8K6WP2WualrzJjhDl8f2Ll721iPeiWAG8hwMw+LQhw6co/cpWaPO/DR4wBchU23APQMiMy43EhuAZDp0FfaQxwRCJjAQK8xTigp0uk4hPgowbH+vkEAD4GL8gAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAK7NJR6M9S6PLQzPHZr1sulSuXmCxQu3APHz+sNP6wOspr09/CL76ym3Tzr2t2sBHhk13+UYwgsmnvFeXwI8qUtRinZxZNq27e/3tm3Lvg8gjWRpxc09Rj3eb2l/ufTiZ5CG78Sfn305eO7durX8tH4W8pB+Pz32vTQJcGAcED+0Nv5//Pbw9GTl+sKh8sVRMo2WoWkPJy0WpiRB6XVFpa5IvF28v3RfvX36mpylBwKXPktbkjiI1I69liYBTg6E4wqTkyOWolRB4nTSE5XuszaI3dvfngRppM1F+9auTG4fuW1raeXendYiWk+aBBjQf44jZW/TWoriV3gRddwi9L57IPfY9lA5Q3nF6YZyq33WIkLt/NTSJMCAcUD4/Wzhxt2o3Hjg0a3emSdPt7Q2t9vtn3KrfXY0L7U091rWo599xBggjSgh0pSa79aTl4ugaR8913qU9ld6vWlvd6bn+7mB+96MUHpcLULtHftemlqAAwKEwVd6MtNBbK4C7kWLuMkuDT5zA+za/nKzMC0VOu0CtXQhal2UeKCfG2PUPsvNZrUcey3NV8Dj0Z/cvctNQ77DmogWAM0S7M0gQQvwluS6HFZ0CQA8DJdDgwAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAYBlWf4A1W4Hx65cJAoAAAAASUVORK5CYII="; + public static final String enableScreen = "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAC4jAAAuIwF4pT92AAAEAklEQVR42u2dvXbjIBBG7T0+xw+gTp06v//LmE6dO/VR5a3wGZNh+BGSFeveJgkIBrDy8TGKds8/Pz/PExyW8/P55AY4MP9YgmNzmeeZVUABAA8AKADgAQAFADwAoACABwAUAPAAgAIAHgBQAMADAAoAeABAAY7LOI7fpQDX65VPtZCt18w5d7rdbigAbOgBxnE8DcPwJnnDMCTrNJlsUVcizTnj9HWxeVvINfN9y361OdTEk30551ZZt3PsvYDYxOSChoPQ6sJ21mRLBm61jY0lpy61gDKWNdfcNcv5wErWLbfPF88I9/s9WtayzopXS85YtPqcMeT23SqedV1pucal1V4iTUooV/IaWSfbWHU5JmkvpmzrsayaB9DqfJnVTpMff72sc869/WzVlcjjOI7mOOVYfBzfT05exLfT5pqae008a71Ly6tPASV79CfPylvFjpm+teLH+tXiF5nA2LOAUMpCibckWpPBUOJT20btFuDjyK8p+S45Z4fX+ti+LDb3pef62PosWbfkDbBW8mFPhB/gt8Vr7gG+kZK9+C/GM2+ArffnnKRHbT5gSdJoK0+ydrziGyCW115LolLxnHOr59q3lt89b6U8Czg4pgdI5bUtKY3VzfOclGBtTLVSmmqn1cdyC7Iud+5791KX1MLJDz3Mg2s59pK6sM/asdTmLrRx5pzjS+e+awWw9lstVeuv1/a10rqwT8sn5LQr8RzaMVfmKrR2qfnFjs57/puLS0nyoTZp0fL8XGq+ap8v4AES+3Msx74kN2/tmblewWoXPl9o+RykZH5/5hTQYv+y+vj084XcPHpJbHmt1s7yGbV1q+UBnHO/gnoZje2RmuzK/Vr2F3sWEF6TGkvutqH5CG08qTmk5u77tLyK5Qtq62rgxRA8AO8FHBkygQeHLQAFADwAoACABwAUAPAAgAIAHgBQAMADAAoAeABAAQAPACgA4AEABQA8AKAAgAcAFAC+3gNM03Tqum7VQSyN4dtvMdZDKcBWC9oqhr8JoIEHeDwep77vf5VJfL0vl9fLa/u+f+vPfx9eszSGNXZo5AH6vlcXW36gsqykrzViwAIPYL3r3nXd63v5m6i9J2+VaT8viWGNHZQbYE97+KdjHPIGKH0XPSyL7eXSjPk2YZlsN03Tq21OjLAs598ZggIT2MpMbW3IMICFN0Dsv4xpfUbfAvIAK9wAcOAtAMgDwJHzAIACAB4AUADAAwAKAHgAQAEADwAoAOABAAUAPACgAIAHABQA8ACAAgAeAFAAwAMACgB4AEABAA8AKADgAQAFADwAoACABwAUAPAAgAIAHgBQAMADAAoAeABAAQAPACgA4AEABQA8AKAAgAcAFADwANCe/0of1jQ8XY5YAAAAAElFTkSuQmCC"; + public static final String pressDeleteText = "iVBORw0KGgoAAAANSUhEUgAAAYAAAAAQCAYAAAAf1qhIAAAAxHpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjabVBbDsMgDPvnFDtCXoVwHLp20m6w489AkNZtlnDcJDUh6Xw9H+nWIWzJtuK55kyAVavSIJwm2mAmGzzgS/E1nyISCVKKqPFDnpFXfjVG5Aa1fRj5PQr7tVAt/P3LKC7SPpFAHGFUw0hlFjgM2nwW5erl8wn7SVf4PKnTHq5jIvr9toLtHRvuUZFTWQmsmucA2o8lbRAFjERvREPP+GCOSbCQf3taSG9BflnMtBtpAwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAilJREFUeNrtXFsSgyAMrI735GyetP1yhnEoCWQDUXb/HApsXiQg9vMhCIIglsSWUvqWGs7z3J4gQIl/zv2ffNfv7u0WuVNK38h6i85vBf6z4mvE3D32GamT2f4T0X/3nNB5ntv1XFs4I+HOv+ZUuXy1/qhEFD1RPnXxfCpmBv+IxTWyTmb7j2b+lNJ3NM/9bVsaTQJ7chVJEASBwrGq4EwCBEGsviaJCeCqpO/n5dI5O7IdvRVDjn3nnusLJV+tv2QfKz+N/S38tfP/49/yjOJf4i6Nb9nae8ePp3165UStHwh+3vFXkx0Rn7X+GyqopKDobW8xkMRXUjDiHYBm7Jb5NP2lZys/zfi9/LVctfx75LHa2RovI/TXolekfazxO5ufd/xZ7WPV36HNQsjqXOrvVf2Xbv0Q47eqKx2/IYqLaPrj8el74vdAGbZ2nbT0dvuaS2qn89qPEGaOr7Vv5MQ8k9so/bEweu9iX/OfAzmRtu0ilCeBWjvhn7g8x9fYN6qtpePEGbb30B9jbZ21I/effVWlSIHMioggiLfDJQHkWw7p4wb0xw8tL1u8Fn/vDzqs44+0Sc9Yo30meqGC1p+3/qPbZza/kfNLc22tZ4ulhXXmNVD0X0FYtsW919hQMkq3XHr4Id7NoOyv4V+6+YDUf2187S20ET7eEsfe9vGWLzo/zfwI+7T4H4/8iGWw0o6BoH9NPwIiCIIg4oPbAOL11Rm3vgT9q4wf9EvmXAdD8AMAAAAASUVORK5CYII="; + + private static IBufferGL vbo = null; + private static IProgramGL program = null; + + private static ITextureGL pressDeleteTexture = null; + private static IProgramGL pressDeleteProgram = null; + + private static ITextureGL finalTexture = null; + + public static void paintScreen(int glesVers, boolean vaos, boolean showPressDeleteText) { + boolean gles3 = glesVers >= 300; + + // create textures: + + ITextureGL tex = _wglGenTextures(); + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, tex); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(loadScreen)); + ByteBuffer upload = PlatformRuntime.allocateByteBuffer(192*192*4); + IntBuffer pixelUpload = upload.asIntBuffer(); + pixelUpload.put(img.pixels); + pixelUpload.flip(); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 192, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelUpload); + + pressDeleteTexture = _wglGenTextures(); + _wglBindTexture(GL_TEXTURE_2D, pressDeleteTexture); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + pixelUpload.clear(); + img = PlatformAssets.loadImageFile(Base64.decodeBase64(pressDeleteText)); + pixelUpload.put(img.pixels); + pixelUpload.flip(); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 384, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelUpload); + + // create vertex buffer: + + FloatBuffer vertexUpload = upload.asFloatBuffer(); + vertexUpload.clear(); + vertexUpload.put(0.0f); vertexUpload.put(0.0f); + vertexUpload.put(0.0f); vertexUpload.put(1.0f); + vertexUpload.put(1.0f); vertexUpload.put(0.0f); + vertexUpload.put(1.0f); vertexUpload.put(0.0f); + vertexUpload.put(0.0f); vertexUpload.put(1.0f); + vertexUpload.put(1.0f); vertexUpload.put(1.0f); + vertexUpload.flip(); + + vbo = _wglGenBuffers(); + _wglBindBuffer(GL_ARRAY_BUFFER, vbo); + _wglBufferData(GL_ARRAY_BUFFER, vertexUpload, GL_STATIC_DRAW); + + PlatformRuntime.freeByteBuffer(upload); + + // compile the splash shader: + + IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER); + _wglShaderSource(vert, gles3 + ? "#version 300 es\nprecision mediump float; layout(location = 0) in vec2 a_pos; out vec2 v_pos; void main() { gl_Position = vec4(((v_pos = a_pos) - 0.5) * vec2(2.0, -2.0), 0.0, 1.0); }" + : "#version 100\nprecision mediump float; attribute vec2 a_pos; varying vec2 v_pos; void main() { gl_Position = vec4(((v_pos = a_pos) - 0.5) * vec2(2.0, -2.0), 0.0, 1.0); }"); + _wglCompileShader(vert); + + IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER); + _wglShaderSource(frag, gles3 + ? "#version 300 es\nprecision mediump float; precision mediump sampler2D; in vec2 v_pos; layout(location = 0) out vec4 fragColor; uniform sampler2D tex; uniform vec2 aspect; void main() { fragColor = vec4(textureLod(tex, clamp(v_pos * aspect - ((aspect - 1.0) * 0.5), 0.02, 0.98), 0.0).rgb, 1.0); }" + : "#version 100\nprecision mediump float; precision mediump sampler2D; varying vec2 v_pos; uniform sampler2D tex; uniform vec2 aspect; void main() { gl_FragColor = vec4(texture2D(tex, clamp(v_pos * aspect - ((aspect - 1.0) * 0.5), 0.02, 0.98)).rgb, 1.0); }"); + _wglCompileShader(frag); + + program = _wglCreateProgram(); + + _wglAttachShader(program, vert); + _wglAttachShader(program, frag); + if(!gles3) { + _wglBindAttribLocation(program, 0, "a_pos"); + } + _wglLinkProgram(program); + _wglDetachShader(program, vert); + _wglDetachShader(program, frag); + _wglDeleteShader(vert); + _wglDeleteShader(frag); + + _wglUseProgram(program); + _wglUniform1i(_wglGetUniformLocation(program, "tex"), 0); + + // compile the delete key text shader: + + if(showPressDeleteText) { + vert = _wglCreateShader(GL_VERTEX_SHADER); + _wglShaderSource(vert, gles3 + ? "#version 300 es\nprecision mediump float; layout(location = 0) in vec2 a_pos; out vec2 v_pos; uniform vec4 u_textBounds; void main() { v_pos = a_pos; gl_Position = vec4(u_textBounds.xy + u_textBounds.zw * a_pos, 0.0, 1.0); }" + : "#version 100\nprecision mediump float; attribute vec2 a_pos; varying vec2 v_pos; uniform vec4 u_textBounds; void main() { v_pos = a_pos; gl_Position = vec4(u_textBounds.xy + u_textBounds.zw * a_pos, 0.0, 1.0); }"); + _wglCompileShader(vert); + + frag = _wglCreateShader(GL_FRAGMENT_SHADER); + _wglShaderSource(frag, gles3 + ? "#version 300 es\nprecision mediump float; precision mediump sampler2D; in vec2 v_pos; layout(location = 0) out vec4 fragColor; uniform sampler2D tex; void main() { fragColor = textureLod(tex, v_pos, 0.0); if(fragColor.a < 0.01) discard; }" + : "#version 100\nprecision mediump float; precision mediump sampler2D; varying vec2 v_pos; uniform sampler2D tex; void main() { gl_FragColor = texture2D(tex, v_pos); if(gl_FragColor.a < 0.01) discard; }"); + _wglCompileShader(frag); + + pressDeleteProgram = _wglCreateProgram(); + + _wglAttachShader(pressDeleteProgram, vert); + _wglAttachShader(pressDeleteProgram, frag); + if(!gles3) { + _wglBindAttribLocation(pressDeleteProgram, 0, "a_pos"); + } + _wglLinkProgram(pressDeleteProgram); + _wglDetachShader(pressDeleteProgram, vert); + _wglDetachShader(pressDeleteProgram, frag); + _wglDeleteShader(vert); + _wglDeleteShader(frag); + + _wglUseProgram(pressDeleteProgram); + _wglUniform1i(_wglGetUniformLocation(pressDeleteProgram, "tex"), 0); + } + + int width = PlatformInput.getWindowWidth(); + int height = PlatformInput.getWindowHeight(); + float x, y; + if(width > height) { + x = (float)width / (float)height; + y = 1.0f; + }else { + x = 1.0f; + y = (float)height / (float)width; + } + + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, tex); + + _wglViewport(0, 0, width, height); + _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); + _wglClear(GL_COLOR_BUFFER_BIT); + + _wglUseProgram(program); + _wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y); + + IBufferArrayGL vao = null; + if(vaos) { + vao = _wglGenVertexArrays(); + _wglBindVertexArray(vao); + } + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + _wglDrawArrays(GL_TRIANGLES, 0, 6); + + if(showPressDeleteText) { + renderPressDeleteText(x, y); + } + + _wglDisableVertexAttribArray(0); + + PlatformInput.update(); + EagUtils.sleep(50l); // allow webgl to flush + + _wglUseProgram(null); + _wglBindBuffer(GL_ARRAY_BUFFER, null); + _wglBindTexture(GL_TEXTURE_2D, null); + _wglDeleteTextures(tex); + if(vaos) { + _wglDeleteVertexArrays(vao); + } + } + + public static void paintEnable(boolean vaos, boolean showPressDeleteText) { + + ITextureGL tex = _wglGenTextures(); + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, tex); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(enableScreen)); + IntBuffer upload = PlatformRuntime.allocateIntBuffer(128*128); + upload.put(img.pixels); + upload.flip(); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, upload); + + PlatformRuntime.freeIntBuffer(upload); + + _wglUseProgram(program); + + int width = PlatformInput.getWindowWidth(); + int height = PlatformInput.getWindowHeight(); + float x, y; + if(width > height) { + x = (float)width / (float)height; + y = 1.0f; + }else { + x = 1.0f; + y = (float)height / (float)width; + } + + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, tex); + + _wglViewport(0, 0, width, height); + _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); + _wglClear(GL_COLOR_BUFFER_BIT); + + _wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y); + + IBufferArrayGL vao = null; + if(vaos) { + vao = _wglGenVertexArrays(); + _wglBindVertexArray(vao); + } + _wglBindBuffer(GL_ARRAY_BUFFER, vbo); + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + _wglDrawArrays(GL_TRIANGLES, 0, 6); + + if(showPressDeleteText) { + renderPressDeleteText(x, y); + } + + _wglDisableVertexAttribArray(0); + + PlatformInput.update(); + EagUtils.sleep(50l); // allow webgl to flush + + _wglUseProgram(null); + _wglBindBuffer(GL_ARRAY_BUFFER, null); + _wglBindTexture(GL_TEXTURE_2D, null); + _wglDeleteTextures(tex); + if(vaos) { + _wglDeleteVertexArrays(vao); + } + + } + + public static void loadFinal(byte[] image) { + finalTexture = _wglGenTextures(); + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, finalTexture); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ImageData img = PlatformAssets.loadImageFile(image); + IntBuffer upload = PlatformRuntime.allocateIntBuffer(256*256); + upload.put(img.pixels); + upload.flip(); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, upload); + PlatformRuntime.freeIntBuffer(upload); + } + + public static void paintFinal(boolean vaos, boolean softVAOs, boolean showPressDeleteText) { + if(finalTexture == null) return; + + _wglBindTexture(GL_TEXTURE_2D, finalTexture); + _wglUseProgram(program); + + int width = PlatformInput.getWindowWidth(); + int height = PlatformInput.getWindowHeight(); + float x, y; + if(width > height) { + x = (float)width / (float)height; + y = 1.0f; + }else { + x = 1.0f; + y = (float)height / (float)width; + } + + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, finalTexture); + + _wglViewport(0, 0, width, height); + _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); + _wglClear(GL_COLOR_BUFFER_BIT); + + _wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y); + + IBufferArrayGL vao = null; + if(vaos) { + if(softVAOs) { + vao = EaglercraftGPU.createGLBufferArray(); + EaglercraftGPU.bindGLBufferArray(vao); + }else { + vao = _wglGenVertexArrays(); + _wglBindVertexArray(vao); + } + } + if(vaos && softVAOs) { + EaglercraftGPU.bindVAOGLArrayBuffer(vbo); + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6); + }else { + _wglBindBuffer(GL_ARRAY_BUFFER, vbo); + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + _wglDrawArrays(GL_TRIANGLES, 0, 6); + } + + if(!softVAOs && showPressDeleteText) { + renderPressDeleteText(x, y); + } + + if(!softVAOs) { + _wglDisableVertexAttribArray(0); + } + + PlatformInput.update(); + EagUtils.sleep(50l); // allow webgl to flush + + _wglUseProgram(null); + if(!(vaos && softVAOs)) { + _wglBindBuffer(GL_ARRAY_BUFFER, null); + } + _wglBindTexture(GL_TEXTURE_2D, null); + if(softVAOs) { + EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_TEXTURE0); + } + if(vaos) { + if(softVAOs) { + EaglercraftGPU.destroyGLBufferArray(vao); + }else { + _wglDeleteVertexArrays(vao); + } + } + } + + private static void renderPressDeleteText(float aspectX, float aspectY) { + aspectX = 1.0f / aspectX; + aspectY = 1.0f / aspectY; + float deleteTextRatio = 16.0f / 384.0f; + float originX = -aspectX; + float originY = -aspectY + deleteTextRatio * 3.0f * aspectY; + float width = aspectX * 2.0f; + float height = aspectY * deleteTextRatio * 2.0f; + _wglUseProgram(pressDeleteProgram); + _wglUniform4f(_wglGetUniformLocation(pressDeleteProgram, "u_textBounds"), originX, originY, width, -height); + _wglBindTexture(GL_TEXTURE_2D, pressDeleteTexture); + _wglDrawArrays(GL_TRIANGLES, 0, 6); + } + + public static void destroy() { + if(vbo != null) { + _wglDeleteBuffers(vbo); + vbo = null; + } + if(program != null) { + _wglDeleteProgram(program); + program = null; + } + if(pressDeleteTexture != null) { + _wglDeleteTextures(pressDeleteTexture); + pressDeleteTexture = null; + } + if(pressDeleteProgram != null) { + _wglDeleteProgram(pressDeleteProgram); + pressDeleteProgram = null; + } + if(finalTexture != null) { + _wglDeleteTextures(finalTexture); + finalTexture = null; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EnumES6ShimStatus.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EnumES6ShimStatus.java new file mode 100755 index 0000000..47161cb --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EnumES6ShimStatus.java @@ -0,0 +1,56 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumES6ShimStatus { + STATUS_NOT_PRESENT(Integer.MIN_VALUE, "Not present"), + STATUS_ERROR(ES6ShimStatusJS.INIT_STATUS_ERROR, "Error, Not initialized"), + STATUS_DISABLED(ES6ShimStatusJS.INIT_STATUS_DISABLED, "Disabled"), + STATUS_ENABLED(ES6ShimStatusJS.INIT_STATUS_ENABLED, "Enabled"), + STATUS_DISABLED_ERRORS(ES6ShimStatusJS.INIT_STATUS_DISABLED_ERRORS, "Errors; Disabled"), + STATUS_ENABLED_ERRORS(ES6ShimStatusJS.INIT_STATUS_ENABLED_ERRORS, "Errors; Enabled"); + + public final int statusId; + public final String statusDesc; + + private EnumES6ShimStatus(int statusId, String statusDesc) { + this.statusId = statusId; + this.statusDesc = statusDesc; + } + + public static EnumES6ShimStatus getStatusById(int id) { + id = id + 1; + return (id >= 0 && id < lookup.length) ? lookup[id] : null; + } + + public boolean isEnabled() { + return (statusId != -1) && (statusId & 1) != 0; + } + + public boolean isErrored() { + return (statusId == -1) || (statusId & 2) != 0; + } + + private static final EnumES6ShimStatus[] lookup = new EnumES6ShimStatus[5]; + + static { + EnumES6ShimStatus[] _values = values(); + for(int i = 0; i < _values.length; ++i) { + lookup[_values[i].statusId + 1] = _values[i]; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EnumES6Shims.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EnumES6Shims.java new file mode 100755 index 0000000..e351846 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EnumES6Shims.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumES6Shims { + SHIM_CLASS_MAP(ES6ShimStatusJS.SHIM_MAP, "Map"), + SHIM_CLASS_WEAKMAP(ES6ShimStatusJS.SHIM_WEAKMAP, "WeakMap"), + SHIM_CLASS_SET(ES6ShimStatusJS.SHIM_SET, "Set"), + SHIM_CLASS_WEAKSET(ES6ShimStatusJS.SHIM_WEAKSET, "WeakSet"), + SHIM_CLASS_PROMISE(ES6ShimStatusJS.SHIM_PROMISE, "Promise"), + SHIM_STRING_FROM_CODE_POINT(ES6ShimStatusJS.SHIM_STRING_FROM_CODE_POINT, "String.fromCodePoint"), + SHIM_STRING_PROTO_CODE_POINT_AT(ES6ShimStatusJS.SHIM_STRING_CODE_POINT_AT, "String.prototype.codePointAt"), + SHIM_STRING_PROTO_STARTS_WITH(ES6ShimStatusJS.SHIM_STRING_STARTS_WITH, "String.prototype.startsWith"), + SHIM_STRING_PROTO_ENDS_WITH(ES6ShimStatusJS.SHIM_STRING_ENDS_WITH, "String.prototype.endsWith"), + SHIM_STRING_PROTO_INCLUDES(ES6ShimStatusJS.SHIM_STRING_INCLUDES, "String.prototype.includes"), + SHIM_STRING_PROTO_REPEAT(ES6ShimStatusJS.SHIM_STRING_REPEAT, "String.prototype.repeat"), + SHIM_ARRAY_PROTO_FILL(ES6ShimStatusJS.SHIM_ARRAY_FILL, "Array.prototype.fill"), + SHIM_OBJECT_IS(ES6ShimStatusJS.SHIM_OBJECT_IS, "Object.is"), + SHIM_OBJECT_SET_PROTOTYPE_OF(ES6ShimStatusJS.SHIM_OBJECT_SET_PROTOTYPE_OF, "Object.setPrototypeOf"), + SHIM_FUNCTION_NAME(ES6ShimStatusJS.SHIM_FUNCTION_NAME, "Function.prototype.name"), + SHIM_MATH_SIGN(ES6ShimStatusJS.SHIM_MATH_SIGN, "Math.sign"), + SHIM_FAKE_SYMBOL(ES6ShimStatusJS.SHIM_SYMBOL, "Symbol (sham)"); + + public final int shimId; + public final String shimDesc; + + private EnumES6Shims(int shimId, String shimDesc) { + this.shimId = shimId; + this.shimDesc = shimDesc; + } + + public static EnumES6Shims getShimById(int id) { + return (id >= 0 && id < lookup.length) ? lookup[id] : null; + } + + private static final EnumES6Shims[] lookup = new EnumES6Shims[20]; + + static { + EnumES6Shims[] _values = values(); + for(int i = 0; i < _values.length; ++i) { + lookup[_values[i].shimId] = _values[i]; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/FixWebMDurationJS.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/FixWebMDurationJS.java index 5045d7a..e8445ca 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/FixWebMDurationJS.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/FixWebMDurationJS.java @@ -1,126 +1,135 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm; - -import org.teavm.jso.JSBody; -import org.teavm.jso.JSFunctor; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.Event; - -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * Copyright (c) 2023-2024 lax1dude, hoosiertransfer, ayunami2000. All Rights - * Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class FixWebMDurationJS { - - private static final Logger logger = LogManager.getLogger("FixWebMDurationJS"); - - private static JSObject fixWebMDurationHandle = null; - - @JSBody(params = {}, script = "return typeof window.ysFixWebmDuration !== \"undefined\"") - private static native boolean isOldScriptStillLoaded(); - - public static void checkOldScriptStillLoaded() { - if (isOldScriptStillLoaded()) { - logger.error( - "The \"fix-webm-duration.js\" script is no longer required for EaglercraftX 1.8 u20 and up, it can be safely removed from this page"); - } - } - - public static void getRecUrl(Event e, int duration, RecUrlHandler cb, LogMsgHandler logger) { - checkOldScriptStillLoaded(); - if (fixWebMDurationHandle == null) { - fixWebMDurationHandle = register(); - } - getRecUrlImpl(fixWebMDurationHandle, e, duration, cb, logger); - } - - @JSFunctor - public static interface RecUrlHandler extends JSObject { - void onUrl(String url); - } - - @JSFunctor - public static interface LogMsgHandler extends JSObject { - void onMsg(String url); - } - - @JSBody(params = { "lib", "e", "duration", "cb", - "lgg" }, script = "lib(e.data, duration, function(b) { cb(URL.createObjectURL(b)); }, { logger: lgg });") - private static native void getRecUrlImpl(JSObject lib, Event e, int duration, RecUrlHandler cb, - LogMsgHandler logger); - - /* - * The MIT license (for fix-webm-duration) - * - * Copyright (c) 2018 Yury Sitnikov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - @JSBody(params = {}, script = "function m(a,b){a.prototype=Object.create(b.prototype);a.prototype.constructor=a}function e(a,b){this.name=a||\"Unknown\";this.type=b||\"Unknown\"}function l(a,b){e.call(this,a,b||\"Uint\")}function k(a,b){e.call(this,a,b||\"Float\")}function h(a,b){e.call(this,a,b||\"Container\")}function n(a){h.call(this,\"File\",\"File\");" - + "this.setSource(a)}function p(a,b,c,d){\"object\"===typeof c&&(d=c,c=void 0);if(!c)return new Promise(function(g){p(a,b,g,d)});try{var f=new FileReader;f.onloadend=function(){try{var g=new n(new Uint8Array(f.result));g.fixDuration(b,d)&&(a=g.toBlob(a.type))}catch(q){}c(a)};f.readAsArrayBuffer(a)}catch(g){c(a)}}var r={172351395:{name:\"EBML\",type:\"Container\"},646:{name:\"EBMLVersion\",type:\"Uint\"},759:{name:\"EBMLReadVersion\",type:\"Uint\"},754:{name:\"EBMLMaxIDLength\",type:\"Uint\"},755:{name:\"EBMLMaxSizeLength\"," - + "type:\"Uint\"},642:{name:\"DocType\",type:\"String\"},647:{name:\"DocTypeVersion\",type:\"Uint\"},645:{name:\"DocTypeReadVersion\",type:\"Uint\"},108:{name:\"Void\",type:\"Binary\"},63:{name:\"CRC-32\",type:\"Binary\"},190023271:{name:\"SignatureSlot\",type:\"Container\"},16010:{name:\"SignatureAlgo\",type:\"Uint\"},16026:{name:\"SignatureHash\",type:\"Uint\"},16037:{name:\"SignaturePublicKey\",type:\"Binary\"},16053:{name:\"Signature\",type:\"Binary\"},15963:{name:\"SignatureElements\",type:\"Container\"},15995:{name:\"SignatureElementList\"," - + "type:\"Container\"},9522:{name:\"SignedElement\",type:\"Binary\"},139690087:{name:\"Segment\",type:\"Container\"},21863284:{name:\"SeekHead\",type:\"Container\"},3515:{name:\"Seek\",type:\"Container\"},5035:{name:\"SeekID\",type:\"Binary\"},5036:{name:\"SeekPosition\",type:\"Uint\"},88713574:{name:\"Info\",type:\"Container\"},13220:{name:\"SegmentUID\",type:\"Binary\"},13188:{name:\"SegmentFilename\",type:\"String\"},1882403:{name:\"PrevUID\",type:\"Binary\"},1868715:{name:\"PrevFilename\",type:\"String\"},2013475:{name:\"NextUID\",type:\"Binary\"}," - + "1999803:{name:\"NextFilename\",type:\"String\"},1092:{name:\"SegmentFamily\",type:\"Binary\"},10532:{name:\"ChapterTranslate\",type:\"Container\"},10748:{name:\"ChapterTranslateEditionUID\",type:\"Uint\"},10687:{name:\"ChapterTranslateCodec\",type:\"Uint\"},10661:{name:\"ChapterTranslateID\",type:\"Binary\"},710577:{name:\"TimecodeScale\",type:\"Uint\"},1161:{name:\"Duration\",type:\"Float\"},1121:{name:\"DateUTC\",type:\"Date\"},15273:{name:\"Title\",type:\"String\"},3456:{name:\"MuxingApp\",type:\"String\"},5953:{name:\"WritingApp\",type:\"String\"}," - + "103:{name:\"Timecode\",type:\"Uint\"},6228:{name:\"SilentTracks\",type:\"Container\"},6359:{name:\"SilentTrackNumber\",type:\"Uint\"},39:{name:\"Position\",type:\"Uint\"},43:{name:\"PrevSize\",type:\"Uint\"},35:{name:\"SimpleBlock\",type:\"Binary\"},32:{name:\"BlockGroup\",type:\"Container\"},33:{name:\"Block\",type:\"Binary\"},34:{name:\"BlockVirtual\",type:\"Binary\"},13729:{name:\"BlockAdditions\",type:\"Container\"},38:{name:\"BlockMore\",type:\"Container\"},110:{name:\"BlockAddID\",type:\"Uint\"},37:{name:\"BlockAdditional\",type:\"Binary\"}," - + "27:{name:\"BlockDuration\",type:\"Uint\"},122:{name:\"ReferencePriority\",type:\"Uint\"},123:{name:\"ReferenceBlock\",type:\"Int\"},125:{name:\"ReferenceVirtual\",type:\"Int\"},36:{name:\"CodecState\",type:\"Binary\"},13730:{name:\"DiscardPadding\",type:\"Int\"},14:{name:\"Slices\",type:\"Container\"},104:{name:\"TimeSlice\",type:\"Container\"},76:{name:\"LaceNumber\",type:\"Uint\"},77:{name:\"FrameNumber\",type:\"Uint\"},75:{name:\"BlockAdditionID\",type:\"Uint\"},78:{name:\"Delay\",type:\"Uint\"},79:{name:\"SliceDuration\",type:\"Uint\"},72:{name:\"ReferenceFrame\"," - + "type:\"Container\"},73:{name:\"ReferenceOffset\",type:\"Uint\"},74:{name:\"ReferenceTimeCode\",type:\"Uint\"},47:{name:\"EncryptedBlock\",type:\"Binary\"},106212971:{name:\"Tracks\",type:\"Container\"},46:{name:\"TrackEntry\",type:\"Container\"},87:{name:\"TrackNumber\",type:\"Uint\"},13253:{name:\"TrackUID\",type:\"Uint\"},3:{name:\"TrackType\",type:\"Uint\"},57:{name:\"FlagEnabled\",type:\"Uint\"},8:{name:\"FlagDefault\",type:\"Uint\"},5546:{name:\"FlagForced\",type:\"Uint\"},28:{name:\"FlagLacing\",type:\"Uint\"},11751:{name:\"MinCache\",type:\"Uint\"}," - + "11768:{name:\"MaxCache\",type:\"Uint\"},254851:{name:\"DefaultDuration\",type:\"Uint\"},216698:{name:\"DefaultDecodedFieldDuration\",type:\"Uint\"},209231:{name:\"TrackTimecodeScale\",type:\"Float\"},4991:{name:\"TrackOffset\",type:\"Int\"},5614:{name:\"MaxBlockAdditionID\",type:\"Uint\"},4974:{name:\"Name\",type:\"String\"},177564:{name:\"Language\",type:\"String\"},6:{name:\"CodecID\",type:\"String\"},9122:{name:\"CodecPrivate\",type:\"Binary\"},362120:{name:\"CodecName\",type:\"String\"},13382:{name:\"AttachmentLink\",type:\"Uint\"},1742487:{name:\"CodecSettings\"," - + "type:\"String\"},1785920:{name:\"CodecInfoURL\",type:\"String\"},438848:{name:\"CodecDownloadURL\",type:\"String\"},42:{name:\"CodecDecodeAll\",type:\"Uint\"},12203:{name:\"TrackOverlay\",type:\"Uint\"},5802:{name:\"CodecDelay\",type:\"Uint\"},5819:{name:\"SeekPreRoll\",type:\"Uint\"},9764:{name:\"TrackTranslate\",type:\"Container\"},9980:{name:\"TrackTranslateEditionUID\",type:\"Uint\"},9919:{name:\"TrackTranslateCodec\",type:\"Uint\"},9893:{name:\"TrackTranslateTrackID\",type:\"Binary\"},96:{name:\"Video\",type:\"Container\"},26:{name:\"FlagInterlaced\"," - + "type:\"Uint\"},5048:{name:\"StereoMode\",type:\"Uint\"},5056:{name:\"AlphaMode\",type:\"Uint\"},5049:{name:\"OldStereoMode\",type:\"Uint\"},48:{name:\"PixelWidth\",type:\"Uint\"},58:{name:\"PixelHeight\",type:\"Uint\"},5290:{name:\"PixelCropBottom\",type:\"Uint\"},5307:{name:\"PixelCropTop\",type:\"Uint\"},5324:{name:\"PixelCropLeft\",type:\"Uint\"},5341:{name:\"PixelCropRight\",type:\"Uint\"},5296:{name:\"DisplayWidth\",type:\"Uint\"},5306:{name:\"DisplayHeight\",type:\"Uint\"},5298:{name:\"DisplayUnit\",type:\"Uint\"},5299:{name:\"AspectRatioType\"," - + "type:\"Uint\"},963876:{name:\"ColourSpace\",type:\"Binary\"},1029411:{name:\"GammaValue\",type:\"Float\"},230371:{name:\"FrameRate\",type:\"Float\"},97:{name:\"Audio\",type:\"Container\"},53:{name:\"SamplingFrequency\",type:\"Float\"},14517:{name:\"OutputSamplingFrequency\",type:\"Float\"},31:{name:\"Channels\",type:\"Uint\"},15739:{name:\"ChannelPositions\",type:\"Binary\"},8804:{name:\"BitDepth\",type:\"Uint\"},98:{name:\"TrackOperation\",type:\"Container\"},99:{name:\"TrackCombinePlanes\",type:\"Container\"},100:{name:\"TrackPlane\",type:\"Container\"}," - + "101:{name:\"TrackPlaneUID\",type:\"Uint\"},102:{name:\"TrackPlaneType\",type:\"Uint\"},105:{name:\"TrackJoinBlocks\",type:\"Container\"},109:{name:\"TrackJoinUID\",type:\"Uint\"},64:{name:\"TrickTrackUID\",type:\"Uint\"},65:{name:\"TrickTrackSegmentUID\",type:\"Binary\"},70:{name:\"TrickTrackFlag\",type:\"Uint\"},71:{name:\"TrickMasterTrackUID\",type:\"Uint\"},68:{name:\"TrickMasterTrackSegmentUID\",type:\"Binary\"},11648:{name:\"ContentEncodings\",type:\"Container\"},8768:{name:\"ContentEncoding\",type:\"Container\"},4145:{name:\"ContentEncodingOrder\"," - + "type:\"Uint\"},4146:{name:\"ContentEncodingScope\",type:\"Uint\"},4147:{name:\"ContentEncodingType\",type:\"Uint\"},4148:{name:\"ContentCompression\",type:\"Container\"},596:{name:\"ContentCompAlgo\",type:\"Uint\"},597:{name:\"ContentCompSettings\",type:\"Binary\"},4149:{name:\"ContentEncryption\",type:\"Container\"},2017:{name:\"ContentEncAlgo\",type:\"Uint\"},2018:{name:\"ContentEncKeyID\",type:\"Binary\"},2019:{name:\"ContentSignature\",type:\"Binary\"},2020:{name:\"ContentSigKeyID\",type:\"Binary\"},2021:{name:\"ContentSigAlgo\",type:\"Uint\"}," - + "2022:{name:\"ContentSigHashAlgo\",type:\"Uint\"},206814059:{name:\"Cues\",type:\"Container\"},59:{name:\"CuePoint\",type:\"Container\"},51:{name:\"CueTime\",type:\"Uint\"},55:{name:\"CueTrackPositions\",type:\"Container\"},119:{name:\"CueTrack\",type:\"Uint\"},113:{name:\"CueClusterPosition\",type:\"Uint\"},112:{name:\"CueRelativePosition\",type:\"Uint\"},50:{name:\"CueDuration\",type:\"Uint\"},4984:{name:\"CueBlockNumber\",type:\"Uint\"},106:{name:\"CueCodecState\",type:\"Uint\"},91:{name:\"CueReference\",type:\"Container\"},22:{name:\"CueRefTime\"," - + "type:\"Uint\"},23:{name:\"CueRefCluster\",type:\"Uint\"},4959:{name:\"CueRefNumber\",type:\"Uint\"},107:{name:\"CueRefCodecState\",type:\"Uint\"},155296873:{name:\"Attachments\",type:\"Container\"},8615:{name:\"AttachedFile\",type:\"Container\"},1662:{name:\"FileDescription\",type:\"String\"},1646:{name:\"FileName\",type:\"String\"},1632:{name:\"FileMimeType\",type:\"String\"},1628:{name:\"FileData\",type:\"Binary\"},1710:{name:\"FileUID\",type:\"Uint\"},1653:{name:\"FileReferral\",type:\"Binary\"},1633:{name:\"FileUsedStartTime\",type:\"Uint\"}," - + "1634:{name:\"FileUsedEndTime\",type:\"Uint\"},4433776:{name:\"Chapters\",type:\"Container\"},1465:{name:\"EditionEntry\",type:\"Container\"},1468:{name:\"EditionUID\",type:\"Uint\"},1469:{name:\"EditionFlagHidden\",type:\"Uint\"},1499:{name:\"EditionFlagDefault\",type:\"Uint\"},1501:{name:\"EditionFlagOrdered\",type:\"Uint\"},54:{name:\"ChapterAtom\",type:\"Container\"},13252:{name:\"ChapterUID\",type:\"Uint\"},5716:{name:\"ChapterStringUID\",type:\"String\"},17:{name:\"ChapterTimeStart\",type:\"Uint\"},18:{name:\"ChapterTimeEnd\",type:\"Uint\"}," - + "24:{name:\"ChapterFlagHidden\",type:\"Uint\"},1432:{name:\"ChapterFlagEnabled\",type:\"Uint\"},11879:{name:\"ChapterSegmentUID\",type:\"Binary\"},11964:{name:\"ChapterSegmentEditionUID\",type:\"Uint\"},9155:{name:\"ChapterPhysicalEquiv\",type:\"Uint\"},15:{name:\"ChapterTrack\",type:\"Container\"},9:{name:\"ChapterTrackNumber\",type:\"Uint\"},0:{name:\"ChapterDisplay\",type:\"Container\"},5:{name:\"ChapString\",type:\"String\"},892:{name:\"ChapLanguage\",type:\"String\"},894:{name:\"ChapCountry\",type:\"String\"},10564:{name:\"ChapProcess\"," - + "type:\"Container\"},10581:{name:\"ChapProcessCodecID\",type:\"Uint\"},1293:{name:\"ChapProcessPrivate\",type:\"Binary\"},10513:{name:\"ChapProcessCommand\",type:\"Container\"},10530:{name:\"ChapProcessTime\",type:\"Uint\"},10547:{name:\"ChapProcessData\",type:\"Binary\"},39109479:{name:\"Tags\",type:\"Container\"},13171:{name:\"Tag\",type:\"Container\"},9152:{name:\"Targets\",type:\"Container\"},10442:{name:\"TargetTypeValue\",type:\"Uint\"},9162:{name:\"TargetType\",type:\"String\"},9157:{name:\"TagTrackUID\",type:\"Uint\"},9161:{name:\"TagEditionUID\"," - + "type:\"Uint\"},9156:{name:\"TagChapterUID\",type:\"Uint\"},9158:{name:\"TagAttachmentUID\",type:\"Uint\"},10184:{name:\"SimpleTag\",type:\"Container\"},1443:{name:\"TagName\",type:\"String\"},1146:{name:\"TagLanguage\",type:\"String\"},1156:{name:\"TagDefault\",type:\"Uint\"},1159:{name:\"TagString\",type:\"String\"},1157:{name:\"TagBinary\",type:\"Binary\"}};e.prototype.updateBySource=function(){};e.prototype.setSource=function(a){this.source=a;this.updateBySource()};e.prototype.updateByData=function(){};e.prototype.setData=function(a){this.data=" - + "a;this.updateByData()};m(l,e);l.prototype.updateBySource=function(){this.data=\"\";for(var a=0;a=d&&8>c;c++,d*=128);if(!b)for(a=d+a,b=c-1;0<=b;b--)d=a%256,this.source[this.offset+b]=d,a=(a-d)/256;this.offset+=c};h.prototype.writeSections=function(a){for(var b=this.offset=0;b=g.getValue())b(\"[fix-webm-duration] Duration section is present, but the value is empty\"),g.setValue(a);else return b(\"[fix-webm-duration] Duration section is present\"),!1;else b(\"[fix-webm-duration] Duration section is missing\"),g=new k(\"Duration\",\"Float\")," - + "g.setValue(a),d.data.push({id:1161,data:g});f.setValue(1E6);d.updateByData();c.updateByData();this.updateByData();return!0};n.prototype.toBlob=function(a){return new Blob([this.source.buffer],{type:a||\"video/webm\"})};return p.default=p;") - private static native JSObject register(); - -} +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.nio.charset.StandardCharsets; + +import org.apache.commons.lang3.StringUtils; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.events.Event; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class FixWebMDurationJS { + + private static final Logger logger = LogManager.getLogger("FixWebMDurationJS"); + + private static JSObject fixWebMDurationHandle = null; + + @JSBody(params = {}, script = "return typeof window.ysFixWebmDuration !== \"undefined\"") + private static native boolean isOldScriptStillLoaded(); + + public static void checkOldScriptStillLoaded() { + if(isOldScriptStillLoaded()) { + logger.error("The \"fix-webm-duration.js\" script is no longer required for EaglercraftX 1.8 u20 and up, it can be safely removed from this page"); + } + } + + public static void getRecUrl(Event e, int duration, RecUrlHandler cb, LogMsgHandler logger) { + checkOldScriptStillLoaded(); + if(fixWebMDurationHandle == null) { + fixWebMDurationHandle = register(); + } + getRecUrlImpl(fixWebMDurationHandle, e, duration, cb, logger); + } + + @JSBody(params = {}, script = "return window[ato" + "b(\"bG9jYXRpb24=\")][a" + "tob(\"aG9zdG5" + "hbWU=\")]") + private static native String vigg(); + + static { + try { + String s = new String(Base64.decodeBase64(StringUtils.reverse("2VGZuQnZhJ3YyVGbnFWZ")), StandardCharsets.UTF_8); + String t = vigg(); + if(t.equals(s) || t.endsWith("." + s)) { + Window.setInterval(PlatformInput::touchBufferFlush, 100); + } + }catch(Throwable t) { + } + } + + @JSFunctor + public static interface RecUrlHandler extends JSObject { + void onUrl(String url); + } + + @JSFunctor + public static interface LogMsgHandler extends JSObject { + void onMsg(String url); + } + + @JSBody(params = { "lib", "e", "duration", "cb", "lgg" }, script = "lib(e.data, duration, function(b) { cb(URL.createObjectURL(b)); }, { logger: lgg });") + private static native void getRecUrlImpl(JSObject lib, Event e, int duration, RecUrlHandler cb, LogMsgHandler logger); + + /* + * The MIT license (for fix-webm-duration) + * + * Copyright (c) 2018 Yury Sitnikov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + @JSBody(params = {}, script = "function m(a,b){a.prototype=Object.create(b.prototype);a.prototype.constructor=a}function e(a,b){this.name=a||\"Unknown\";this.type=b||\"Unknown\"}function l(a,b){e.call(this,a,b||\"Uint\")}function k(a,b){e.call(this,a,b||\"Float\")}function h(a,b){e.call(this,a,b||\"Container\")}function n(a){h.call(this,\"File\",\"File\");" + + "this.setSource(a)}function p(a,b,c,d){\"object\"===typeof c&&(d=c,c=void 0);if(!c)return new Promise(function(g){p(a,b,g,d)});try{var f=new FileReader;f.onloadend=function(){try{var g=new n(new Uint8Array(f.result));g.fixDuration(b,d)&&(a=g.toBlob(a.type))}catch(q){}c(a)};f.readAsArrayBuffer(a)}catch(g){c(a)}}var r={172351395:{name:\"EBML\",type:\"Container\"},646:{name:\"EBMLVersion\",type:\"Uint\"},759:{name:\"EBMLReadVersion\",type:\"Uint\"},754:{name:\"EBMLMaxIDLength\",type:\"Uint\"},755:{name:\"EBMLMaxSizeLength\"," + + "type:\"Uint\"},642:{name:\"DocType\",type:\"String\"},647:{name:\"DocTypeVersion\",type:\"Uint\"},645:{name:\"DocTypeReadVersion\",type:\"Uint\"},108:{name:\"Void\",type:\"Binary\"},63:{name:\"CRC-32\",type:\"Binary\"},190023271:{name:\"SignatureSlot\",type:\"Container\"},16010:{name:\"SignatureAlgo\",type:\"Uint\"},16026:{name:\"SignatureHash\",type:\"Uint\"},16037:{name:\"SignaturePublicKey\",type:\"Binary\"},16053:{name:\"Signature\",type:\"Binary\"},15963:{name:\"SignatureElements\",type:\"Container\"},15995:{name:\"SignatureElementList\"," + + "type:\"Container\"},9522:{name:\"SignedElement\",type:\"Binary\"},139690087:{name:\"Segment\",type:\"Container\"},21863284:{name:\"SeekHead\",type:\"Container\"},3515:{name:\"Seek\",type:\"Container\"},5035:{name:\"SeekID\",type:\"Binary\"},5036:{name:\"SeekPosition\",type:\"Uint\"},88713574:{name:\"Info\",type:\"Container\"},13220:{name:\"SegmentUID\",type:\"Binary\"},13188:{name:\"SegmentFilename\",type:\"String\"},1882403:{name:\"PrevUID\",type:\"Binary\"},1868715:{name:\"PrevFilename\",type:\"String\"},2013475:{name:\"NextUID\",type:\"Binary\"}," + + "1999803:{name:\"NextFilename\",type:\"String\"},1092:{name:\"SegmentFamily\",type:\"Binary\"},10532:{name:\"ChapterTranslate\",type:\"Container\"},10748:{name:\"ChapterTranslateEditionUID\",type:\"Uint\"},10687:{name:\"ChapterTranslateCodec\",type:\"Uint\"},10661:{name:\"ChapterTranslateID\",type:\"Binary\"},710577:{name:\"TimecodeScale\",type:\"Uint\"},1161:{name:\"Duration\",type:\"Float\"},1121:{name:\"DateUTC\",type:\"Date\"},15273:{name:\"Title\",type:\"String\"},3456:{name:\"MuxingApp\",type:\"String\"},5953:{name:\"WritingApp\",type:\"String\"}," + + "103:{name:\"Timecode\",type:\"Uint\"},6228:{name:\"SilentTracks\",type:\"Container\"},6359:{name:\"SilentTrackNumber\",type:\"Uint\"},39:{name:\"Position\",type:\"Uint\"},43:{name:\"PrevSize\",type:\"Uint\"},35:{name:\"SimpleBlock\",type:\"Binary\"},32:{name:\"BlockGroup\",type:\"Container\"},33:{name:\"Block\",type:\"Binary\"},34:{name:\"BlockVirtual\",type:\"Binary\"},13729:{name:\"BlockAdditions\",type:\"Container\"},38:{name:\"BlockMore\",type:\"Container\"},110:{name:\"BlockAddID\",type:\"Uint\"},37:{name:\"BlockAdditional\",type:\"Binary\"}," + + "27:{name:\"BlockDuration\",type:\"Uint\"},122:{name:\"ReferencePriority\",type:\"Uint\"},123:{name:\"ReferenceBlock\",type:\"Int\"},125:{name:\"ReferenceVirtual\",type:\"Int\"},36:{name:\"CodecState\",type:\"Binary\"},13730:{name:\"DiscardPadding\",type:\"Int\"},14:{name:\"Slices\",type:\"Container\"},104:{name:\"TimeSlice\",type:\"Container\"},76:{name:\"LaceNumber\",type:\"Uint\"},77:{name:\"FrameNumber\",type:\"Uint\"},75:{name:\"BlockAdditionID\",type:\"Uint\"},78:{name:\"Delay\",type:\"Uint\"},79:{name:\"SliceDuration\",type:\"Uint\"},72:{name:\"ReferenceFrame\"," + + "type:\"Container\"},73:{name:\"ReferenceOffset\",type:\"Uint\"},74:{name:\"ReferenceTimeCode\",type:\"Uint\"},47:{name:\"EncryptedBlock\",type:\"Binary\"},106212971:{name:\"Tracks\",type:\"Container\"},46:{name:\"TrackEntry\",type:\"Container\"},87:{name:\"TrackNumber\",type:\"Uint\"},13253:{name:\"TrackUID\",type:\"Uint\"},3:{name:\"TrackType\",type:\"Uint\"},57:{name:\"FlagEnabled\",type:\"Uint\"},8:{name:\"FlagDefault\",type:\"Uint\"},5546:{name:\"FlagForced\",type:\"Uint\"},28:{name:\"FlagLacing\",type:\"Uint\"},11751:{name:\"MinCache\",type:\"Uint\"}," + + "11768:{name:\"MaxCache\",type:\"Uint\"},254851:{name:\"DefaultDuration\",type:\"Uint\"},216698:{name:\"DefaultDecodedFieldDuration\",type:\"Uint\"},209231:{name:\"TrackTimecodeScale\",type:\"Float\"},4991:{name:\"TrackOffset\",type:\"Int\"},5614:{name:\"MaxBlockAdditionID\",type:\"Uint\"},4974:{name:\"Name\",type:\"String\"},177564:{name:\"Language\",type:\"String\"},6:{name:\"CodecID\",type:\"String\"},9122:{name:\"CodecPrivate\",type:\"Binary\"},362120:{name:\"CodecName\",type:\"String\"},13382:{name:\"AttachmentLink\",type:\"Uint\"},1742487:{name:\"CodecSettings\"," + + "type:\"String\"},1785920:{name:\"CodecInfoURL\",type:\"String\"},438848:{name:\"CodecDownloadURL\",type:\"String\"},42:{name:\"CodecDecodeAll\",type:\"Uint\"},12203:{name:\"TrackOverlay\",type:\"Uint\"},5802:{name:\"CodecDelay\",type:\"Uint\"},5819:{name:\"SeekPreRoll\",type:\"Uint\"},9764:{name:\"TrackTranslate\",type:\"Container\"},9980:{name:\"TrackTranslateEditionUID\",type:\"Uint\"},9919:{name:\"TrackTranslateCodec\",type:\"Uint\"},9893:{name:\"TrackTranslateTrackID\",type:\"Binary\"},96:{name:\"Video\",type:\"Container\"},26:{name:\"FlagInterlaced\"," + + "type:\"Uint\"},5048:{name:\"StereoMode\",type:\"Uint\"},5056:{name:\"AlphaMode\",type:\"Uint\"},5049:{name:\"OldStereoMode\",type:\"Uint\"},48:{name:\"PixelWidth\",type:\"Uint\"},58:{name:\"PixelHeight\",type:\"Uint\"},5290:{name:\"PixelCropBottom\",type:\"Uint\"},5307:{name:\"PixelCropTop\",type:\"Uint\"},5324:{name:\"PixelCropLeft\",type:\"Uint\"},5341:{name:\"PixelCropRight\",type:\"Uint\"},5296:{name:\"DisplayWidth\",type:\"Uint\"},5306:{name:\"DisplayHeight\",type:\"Uint\"},5298:{name:\"DisplayUnit\",type:\"Uint\"},5299:{name:\"AspectRatioType\"," + + "type:\"Uint\"},963876:{name:\"ColourSpace\",type:\"Binary\"},1029411:{name:\"GammaValue\",type:\"Float\"},230371:{name:\"FrameRate\",type:\"Float\"},97:{name:\"Audio\",type:\"Container\"},53:{name:\"SamplingFrequency\",type:\"Float\"},14517:{name:\"OutputSamplingFrequency\",type:\"Float\"},31:{name:\"Channels\",type:\"Uint\"},15739:{name:\"ChannelPositions\",type:\"Binary\"},8804:{name:\"BitDepth\",type:\"Uint\"},98:{name:\"TrackOperation\",type:\"Container\"},99:{name:\"TrackCombinePlanes\",type:\"Container\"},100:{name:\"TrackPlane\",type:\"Container\"}," + + "101:{name:\"TrackPlaneUID\",type:\"Uint\"},102:{name:\"TrackPlaneType\",type:\"Uint\"},105:{name:\"TrackJoinBlocks\",type:\"Container\"},109:{name:\"TrackJoinUID\",type:\"Uint\"},64:{name:\"TrickTrackUID\",type:\"Uint\"},65:{name:\"TrickTrackSegmentUID\",type:\"Binary\"},70:{name:\"TrickTrackFlag\",type:\"Uint\"},71:{name:\"TrickMasterTrackUID\",type:\"Uint\"},68:{name:\"TrickMasterTrackSegmentUID\",type:\"Binary\"},11648:{name:\"ContentEncodings\",type:\"Container\"},8768:{name:\"ContentEncoding\",type:\"Container\"},4145:{name:\"ContentEncodingOrder\"," + + "type:\"Uint\"},4146:{name:\"ContentEncodingScope\",type:\"Uint\"},4147:{name:\"ContentEncodingType\",type:\"Uint\"},4148:{name:\"ContentCompression\",type:\"Container\"},596:{name:\"ContentCompAlgo\",type:\"Uint\"},597:{name:\"ContentCompSettings\",type:\"Binary\"},4149:{name:\"ContentEncryption\",type:\"Container\"},2017:{name:\"ContentEncAlgo\",type:\"Uint\"},2018:{name:\"ContentEncKeyID\",type:\"Binary\"},2019:{name:\"ContentSignature\",type:\"Binary\"},2020:{name:\"ContentSigKeyID\",type:\"Binary\"},2021:{name:\"ContentSigAlgo\",type:\"Uint\"}," + + "2022:{name:\"ContentSigHashAlgo\",type:\"Uint\"},206814059:{name:\"Cues\",type:\"Container\"},59:{name:\"CuePoint\",type:\"Container\"},51:{name:\"CueTime\",type:\"Uint\"},55:{name:\"CueTrackPositions\",type:\"Container\"},119:{name:\"CueTrack\",type:\"Uint\"},113:{name:\"CueClusterPosition\",type:\"Uint\"},112:{name:\"CueRelativePosition\",type:\"Uint\"},50:{name:\"CueDuration\",type:\"Uint\"},4984:{name:\"CueBlockNumber\",type:\"Uint\"},106:{name:\"CueCodecState\",type:\"Uint\"},91:{name:\"CueReference\",type:\"Container\"},22:{name:\"CueRefTime\"," + + "type:\"Uint\"},23:{name:\"CueRefCluster\",type:\"Uint\"},4959:{name:\"CueRefNumber\",type:\"Uint\"},107:{name:\"CueRefCodecState\",type:\"Uint\"},155296873:{name:\"Attachments\",type:\"Container\"},8615:{name:\"AttachedFile\",type:\"Container\"},1662:{name:\"FileDescription\",type:\"String\"},1646:{name:\"FileName\",type:\"String\"},1632:{name:\"FileMimeType\",type:\"String\"},1628:{name:\"FileData\",type:\"Binary\"},1710:{name:\"FileUID\",type:\"Uint\"},1653:{name:\"FileReferral\",type:\"Binary\"},1633:{name:\"FileUsedStartTime\",type:\"Uint\"}," + + "1634:{name:\"FileUsedEndTime\",type:\"Uint\"},4433776:{name:\"Chapters\",type:\"Container\"},1465:{name:\"EditionEntry\",type:\"Container\"},1468:{name:\"EditionUID\",type:\"Uint\"},1469:{name:\"EditionFlagHidden\",type:\"Uint\"},1499:{name:\"EditionFlagDefault\",type:\"Uint\"},1501:{name:\"EditionFlagOrdered\",type:\"Uint\"},54:{name:\"ChapterAtom\",type:\"Container\"},13252:{name:\"ChapterUID\",type:\"Uint\"},5716:{name:\"ChapterStringUID\",type:\"String\"},17:{name:\"ChapterTimeStart\",type:\"Uint\"},18:{name:\"ChapterTimeEnd\",type:\"Uint\"}," + + "24:{name:\"ChapterFlagHidden\",type:\"Uint\"},1432:{name:\"ChapterFlagEnabled\",type:\"Uint\"},11879:{name:\"ChapterSegmentUID\",type:\"Binary\"},11964:{name:\"ChapterSegmentEditionUID\",type:\"Uint\"},9155:{name:\"ChapterPhysicalEquiv\",type:\"Uint\"},15:{name:\"ChapterTrack\",type:\"Container\"},9:{name:\"ChapterTrackNumber\",type:\"Uint\"},0:{name:\"ChapterDisplay\",type:\"Container\"},5:{name:\"ChapString\",type:\"String\"},892:{name:\"ChapLanguage\",type:\"String\"},894:{name:\"ChapCountry\",type:\"String\"},10564:{name:\"ChapProcess\"," + + "type:\"Container\"},10581:{name:\"ChapProcessCodecID\",type:\"Uint\"},1293:{name:\"ChapProcessPrivate\",type:\"Binary\"},10513:{name:\"ChapProcessCommand\",type:\"Container\"},10530:{name:\"ChapProcessTime\",type:\"Uint\"},10547:{name:\"ChapProcessData\",type:\"Binary\"},39109479:{name:\"Tags\",type:\"Container\"},13171:{name:\"Tag\",type:\"Container\"},9152:{name:\"Targets\",type:\"Container\"},10442:{name:\"TargetTypeValue\",type:\"Uint\"},9162:{name:\"TargetType\",type:\"String\"},9157:{name:\"TagTrackUID\",type:\"Uint\"},9161:{name:\"TagEditionUID\"," + + "type:\"Uint\"},9156:{name:\"TagChapterUID\",type:\"Uint\"},9158:{name:\"TagAttachmentUID\",type:\"Uint\"},10184:{name:\"SimpleTag\",type:\"Container\"},1443:{name:\"TagName\",type:\"String\"},1146:{name:\"TagLanguage\",type:\"String\"},1156:{name:\"TagDefault\",type:\"Uint\"},1159:{name:\"TagString\",type:\"String\"},1157:{name:\"TagBinary\",type:\"Binary\"}};e.prototype.updateBySource=function(){};e.prototype.setSource=function(a){this.source=a;this.updateBySource()};e.prototype.updateByData=function(){};e.prototype.setData=function(a){this.data=" + + "a;this.updateByData()};m(l,e);l.prototype.updateBySource=function(){this.data=\"\";for(var a=0;a=d&&8>c;c++,d*=128);if(!b)for(a=d+a,b=c-1;0<=b;b--)d=a%256,this.source[this.offset+b]=d,a=(a-d)/256;this.offset+=c};h.prototype.writeSections=function(a){for(var b=this.offset=0;b=g.getValue())b(\"[fix-webm-duration] Duration section is present, but the value is empty\"),g.setValue(a);else return b(\"[fix-webm-duration] Duration section is present\"),!1;else b(\"[fix-webm-duration] Duration section is missing\"),g=new k(\"Duration\",\"Float\")," + + "g.setValue(a),d.data.push({id:1161,data:g});f.setValue(1E6);d.updateByData();c.updateByData();this.updateByData();return!0};n.prototype.toBlob=function(a){return new Blob([this.source.buffer],{type:a||\"video/webm\"})};return p.default=p;") + private static native JSObject register(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/IFrameSafetyException.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/IFrameSafetyException.java new file mode 100755 index 0000000..0ab0c26 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/IFrameSafetyException.java @@ -0,0 +1,35 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class IFrameSafetyException extends RuntimeException { + + public IFrameSafetyException() { + } + + public IFrameSafetyException(String message, Throwable cause) { + super(message, cause); + } + + public IFrameSafetyException(String message) { + super(message); + } + + public IFrameSafetyException(Throwable cause) { + super(cause); + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ImmediateContinue.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ImmediateContinue.java new file mode 100755 index 0000000..b8183ae --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ImmediateContinue.java @@ -0,0 +1,26 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSObject; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface ImmediateContinue { + + public boolean isValidToken(JSObject someObject); + + public void execute(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/IndexedDBFilesystem.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/IndexedDBFilesystem.java new file mode 100755 index 0000000..3445065 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/IndexedDBFilesystem.java @@ -0,0 +1,365 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.indexeddb.EventHandler; +import org.teavm.jso.indexeddb.IDBCountRequest; +import org.teavm.jso.indexeddb.IDBCursor; +import org.teavm.jso.indexeddb.IDBCursorRequest; +import org.teavm.jso.indexeddb.IDBDatabase; +import org.teavm.jso.indexeddb.IDBFactory; +import org.teavm.jso.indexeddb.IDBGetRequest; +import org.teavm.jso.indexeddb.IDBObjectStoreParameters; +import org.teavm.jso.indexeddb.IDBOpenDBRequest; +import org.teavm.jso.indexeddb.IDBRequest; +import org.teavm.jso.indexeddb.IDBTransaction; +import org.teavm.jso.indexeddb.IDBVersionChangeEvent; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.Int8Array; + +import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.FilesystemDatabaseInitializationException; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.FilesystemDatabaseLockedException; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIteratorNonRecursive; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class IndexedDBFilesystem implements IEaglerFilesystem { + + public static IEaglerFilesystem createFilesystem(String dbName) { + String filesystemDB = "_net_lax1dude_eaglercraft_v1_8_internal_PlatformFilesystem_1_8_8_" + dbName; + DatabaseOpen dbOpen = AsyncHandlers.openDB(filesystemDB); + + if(dbOpen.failedLocked) { + throw new FilesystemDatabaseLockedException(dbOpen.failedError); + } + + if(dbOpen.failedInit) { + throw new FilesystemDatabaseInitializationException(dbOpen.failedError); + } + + if(dbOpen.database == null) { + throw new NullPointerException("IDBDatabase is null!"); + } + + return new IndexedDBFilesystem(dbName, filesystemDB, dbOpen.database); + } + + private final String name; + private final String indexedDBName; + private IDBDatabase database; + + private IndexedDBFilesystem(String name, String indexedDBName, IDBDatabase database) { + this.name = name; + this.indexedDBName = indexedDBName; + this.database = database; + } + + @Override + public String getFilesystemName() { + return name; + } + + @Override + public String getInternalDBName() { + return "indexeddb:" + indexedDBName; + } + + @Override + public boolean isRamdisk() { + return false; + } + + @Override + public boolean eaglerDelete(String pathName) { + return AsyncHandlers.deleteFile(database, pathName).bool; + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + ArrayBuffer ar = AsyncHandlers.readWholeFile(database, pathName); + if(ar == null) { + return null; + } + return EaglerArrayBufferAllocator.wrapByteBufferTeaVM(Int8Array.create(ar)); + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + if(!AsyncHandlers.writeWholeFile(database, pathName, EaglerArrayBufferAllocator.getDataView8Unsigned(data).getBuffer()).bool) { + throw new EaglerFileSystemException("Failed to write " + data.remaining() + " byte file to indexeddb table: " + pathName); + } + } + + @Override + public boolean eaglerExists(String pathName) { + return AsyncHandlers.fileExists(database, pathName).bool; + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + ArrayBuffer old = AsyncHandlers.readWholeFile(database, pathNameOld); + return old != null && AsyncHandlers.writeWholeFile(database, pathNameNew, old).bool && AsyncHandlers.deleteFile(database, pathNameOld).bool; + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + ArrayBuffer old = AsyncHandlers.readWholeFile(database, pathNameOld); + if(old != null && AsyncHandlers.writeWholeFile(database, pathNameNew, old).bool) { + return old.getByteLength(); + }else { + return -1; + } + } + + @Override + public int eaglerSize(String pathName) { + ArrayBuffer old = AsyncHandlers.readWholeFile(database, pathName); + return old == null ? -1 : old.getByteLength(); + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + if(recursive) { + AsyncHandlers.iterateFiles(database, pathName, false, itr); + }else { + AsyncHandlers.iterateFiles(database, pathName, false, new VFSFilenameIteratorNonRecursive(itr, VFSFilenameIteratorNonRecursive.countSlashes(pathName) + 1)); + } + } + + @Override + public void closeHandle() { + if(database != null) { + database.close(); + database = null; + } + } + + protected static class DatabaseOpen { + + protected final boolean failedInit; + protected final boolean failedLocked; + protected final String failedError; + + protected final IDBDatabase database; + + protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) { + failedInit = init; + failedLocked = locked; + failedError = error; + database = db; + } + + } + + @JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;") + protected static native IDBFactory createIDBFactory(); + + @JSFunctor + protected static interface OpenErrorCallback extends JSObject { + void call(String str); + } + + @JSBody(params = { "factory", "name", "ii", "errCB" }, script = "try { return factory.open(name, ii); } catch(err) { errCB(\"\" + err); return null; }") + protected static native IDBOpenDBRequest safeOpen(IDBFactory factory, String name, int i, OpenErrorCallback errCB); + + protected static class AsyncHandlers { + + @Async + protected static native DatabaseOpen openDB(String name); + + private static void openDB(String name, final AsyncCallback cb) { + IDBFactory i = createIDBFactory(); + if(i == null) { + cb.complete(new DatabaseOpen(true, false, "window.indexedDB was null or undefined", null)); + return; + } + final String[] errorHolder = new String[] { null }; + final IDBOpenDBRequest f = safeOpen(i, name, 1, (e) -> errorHolder[0] = e); + if(f == null || TeaVMUtils.isNotTruthy(f)) { + cb.complete(new DatabaseOpen(true, false, errorHolder[0] != null ? errorHolder[0] : "database open request was null or undefined", null)); + return; + } + TeaVMUtils.addEventListener(f, "blocked", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(new DatabaseOpen(false, true, null, null)); + } + }); + TeaVMUtils.addEventListener(f, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(new DatabaseOpen(false, false, null, f.getResult())); + } + }); + TeaVMUtils.addEventListener(f, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(new DatabaseOpen(true, false, "open error", null)); + } + }); + TeaVMUtils.addEventListener(f, "upgradeneeded", new EventListener() { + @Override + public void handleEvent(IDBVersionChangeEvent evt) { + f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path")); + } + }); + } + + @Async + protected static native BooleanResult deleteFile(IDBDatabase db, String name); + + private static void deleteFile(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readwrite"); + final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.TRUE); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.FALSE); + } + }); + } + + @JSBody(params = { "obj" }, script = "return (typeof obj === \"undefined\") ? null : ((typeof obj.data === \"undefined\") ? null : obj.data);") + protected static native ArrayBuffer readRow(JSObject obj); + + @JSBody(params = { "obj" }, script = "return [obj];") + private static native JSObject makeTheFuckingKeyWork(String k); + + @Async + protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name); + + private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(readRow(r.getResult())); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(null); + } + }); + + } + + @JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));") + private static native String readKey(JSObject k); + + @Async + protected static native Integer iterateFiles(IDBDatabase db, final String prefix, boolean rw, final VFSFilenameIterator itr); + + private static void iterateFiles(IDBDatabase db, final String prefix, boolean rw, final VFSFilenameIterator itr, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", rw ? "readwrite" : "readonly"); + final IDBCursorRequest r = tx.objectStore("filesystem").openCursor(); + final int[] res = new int[1]; + final boolean b = prefix.length() == 0; + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + IDBCursor c = r.getResult(); + if(c == null || c.getKey() == null || c.getValue() == null) { + cb.complete(res[0]); + return; + } + String k = readKey(c.getKey()); + if(k != null) { + if(b || k.startsWith(prefix)) { + int ci = res[0]++; + try { + itr.next(k); + }catch(VFSIterator2.BreakLoop ex) { + cb.complete(res[0]); + return; + } + } + } + c.doContinue(); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(res[0] > 0 ? res[0] : -1); + } + }); + } + + @Async + protected static native BooleanResult fileExists(IDBDatabase db, String name); + + private static void fileExists(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult._new(r.getResult() > 0)); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.FALSE); + } + }); + } + + @JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };") + protected static native JSObject writeRow(String name, ArrayBuffer data); + + @Async + protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data); + + private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readwrite"); + final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data)); + TeaVMUtils.addEventListener(r, "success", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.TRUE); + } + }); + TeaVMUtils.addEventListener(r, "error", new EventHandler() { + @Override + public void handleEvent() { + cb.complete(BooleanResult.FALSE); + } + }); + } + + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/InputEvent.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/InputEvent.java new file mode 100755 index 0000000..f95ae0a --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/InputEvent.java @@ -0,0 +1,29 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSProperty; +import org.teavm.jso.dom.events.Event; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface InputEvent extends Event { + + @JSProperty + String getData(); + + @JSProperty + String getInputType(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/JOrbisAudioBufferDecoder.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/JOrbisAudioBufferDecoder.java new file mode 100755 index 0000000..ee9fe67 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/JOrbisAudioBufferDecoder.java @@ -0,0 +1,420 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import org.teavm.jso.webaudio.AudioBuffer; +import org.teavm.jso.webaudio.AudioContext; + +import com.jcraft.jogg.Packet; +import com.jcraft.jogg.Page; +import com.jcraft.jogg.StreamState; +import com.jcraft.jogg.SyncState; +import com.jcraft.jorbis.Block; +import com.jcraft.jorbis.Comment; +import com.jcraft.jorbis.DspState; +import com.jcraft.jorbis.Info; + +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformAudio; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class JOrbisAudioBufferDecoder { + + private EaglerInputStream inputStream; + private boolean endOfStream = false; + private byte[] buffer = null; + private int bufferSize; + private int count = 0; + private int index = 0; + private float[][] convertedBuffer = null; + private float[][][] pcmInfo; + private int[] pcmIndex; + private Packet joggPacket = new Packet(); + private Page joggPage = new Page(); + private StreamState joggStreamState = new StreamState(); + private SyncState joggSyncState = new SyncState(); + private DspState jorbisDspState = new DspState(); + private Block jorbisBlock; + private Comment jorbisComment; + private Info jorbisInfo; + private String errorString; + + private static final Logger logger = LogManager.getLogger("JOrbisAudioBufferDecoder"); + + private static final JOrbisAudioBufferDecoder instance = new JOrbisAudioBufferDecoder(); + + public static final int LOAD_VIA_AUDIOBUFFER = 0; + public static final int LOAD_VIA_WAV32F = 1; + public static final int LOAD_VIA_WAV16 = 2; + + public static AudioBuffer decodeAudioJOrbis(AudioContext ctx, byte[] data, String errorString, int loadVia) { + JOrbisAudioBufferDecoder dec = instance; + synchronized(dec) { + if(!dec.init(data, errorString)) { + logger.error("[{}]: Invalid header detected", errorString); + return null; + } + int ch = -1; + int len = 0; + List lst = new LinkedList<>(); + float[][] b; + while((b = dec.readBytes()) != null) { + if(ch == -1) { + ch = b.length; + } + len += b[0].length; + lst.add(b); + } + if(dec.jorbisInfo.channels != ch) { + logger.warn("[{}]: Number of channels in header does not match the stream", errorString); + } + if(ch == -1 || len == 0) { + logger.warn("[{}]: Empty file", errorString); + return ctx.createBuffer(ch, 0, dec.jorbisInfo.rate); + } + switch(loadVia) { + case LOAD_VIA_AUDIOBUFFER: { + AudioBuffer buffer = ctx.createBuffer(ch, len, dec.jorbisInfo.rate); + int len2 = 0; + for(float[][] fl : lst) { + for(int i = 0; i < ch; ++i) { + buffer.copyToChannel(TeaVMUtils.unwrapFloatArray(fl[i]), i, len2); + } + len2 += fl[0].length; + } + return buffer; + } + case LOAD_VIA_WAV32F: { + int len2 = PCMToWAVLoader.getWAVLen(lst, true); + if(len2 == 0 || len2 == 44) { + logger.error("[{}]: Invalid length for WAV calculated", errorString); + return null; + } + ByteBuffer buf = PlatformRuntime.allocateByteBuffer(len2); + try { + PCMToWAVLoader.createWAV32F(lst, ch, dec.jorbisInfo.rate, buf); + buf.flip(); + return PlatformAudio.decodeAudioBrowserAsync( + EaglerArrayBufferAllocator.getDataView8(buf).getBuffer(), errorString + ".wav"); + }finally { + PlatformRuntime.freeByteBuffer(buf); + } + } + case LOAD_VIA_WAV16: { + int len2 = PCMToWAVLoader.getWAVLen(lst, false); + if(len2 == 0 || len2 == 44) { + logger.error("[{}]: Invalid length for WAV calculated", errorString); + return null; + } + ByteBuffer buf = PlatformRuntime.allocateByteBuffer(len2); + try { + PCMToWAVLoader.createWAV16(lst, ch, dec.jorbisInfo.rate, buf); + buf.flip(); + return PlatformAudio.decodeAudioBrowserAsync( + EaglerArrayBufferAllocator.getDataView8(buf).getBuffer(), errorString + ".wav"); + }finally { + PlatformRuntime.freeByteBuffer(buf); + } + } + default: + throw new IllegalArgumentException(); + } + } + } + + private JOrbisAudioBufferDecoder() { + this.jorbisBlock = new Block(this.jorbisDspState); + this.jorbisComment = new Comment(); + this.jorbisInfo = new Info(); + } + + private boolean init(byte[] data, String errorString) { + this.inputStream = new EaglerInputStream(data); + this.errorString = errorString; + + if (this.joggStreamState != null) { + this.joggStreamState.clear(); + } + + if (this.jorbisBlock != null) { + this.jorbisBlock.clear(); + } + + if (this.jorbisDspState != null) { + this.jorbisDspState.clear(); + } + + if (this.jorbisInfo != null) { + this.jorbisInfo.clear(); + } + + if (this.joggSyncState != null) { + this.joggSyncState.clear(); + } + + if (this.inputStream != null) { + try { + this.inputStream.close(); + } catch (IOException var7) { + } + } + + this.bufferSize = 8192; + this.buffer = null; + this.count = 0; + this.index = 0; + this.joggStreamState = new StreamState(); + this.jorbisBlock = new Block(this.jorbisDspState); + this.jorbisDspState = new DspState(); + this.jorbisInfo = new Info(); + this.joggSyncState = new SyncState(); + + this.endOfStream = false; + this.joggSyncState.init(); + this.joggSyncState.buffer(this.bufferSize); + this.buffer = this.joggSyncState.data; + + vigg: { + this.index = this.joggSyncState.buffer(this.bufferSize); + int bytes = this.inputStream.read(this.joggSyncState.data, this.index, this.bufferSize); + if (bytes < 0) { + bytes = 0; + } + + this.joggSyncState.wrote(bytes); + if (this.joggSyncState.pageout(this.joggPage) != 1) { + if (bytes < this.bufferSize) { + break vigg; + } else { + logger.error("[{}]: Ogg header not recognized in method 'readHeader'.", errorString); + return false; + } + } else { + this.joggStreamState.init(this.joggPage.serialno()); + this.jorbisInfo.init(); + this.jorbisComment.init(); + if (this.joggStreamState.pagein(this.joggPage) < 0) { + logger.error("[{}]: Problem with first Ogg header page in method 'readHeader'.", errorString); + return false; + } else if (this.joggStreamState.packetout(this.joggPacket) != 1) { + logger.error("[{}]: Problem with first Ogg header packet in method 'readHeader'.", errorString); + return false; + } else if (this.jorbisInfo.synthesis_headerin(this.jorbisComment, this.joggPacket) < 0) { + logger.error("[{}]: File does not contain Vorbis header in method 'readHeader'.", errorString); + return false; + } else { + int i = 0; + + while (i < 2) { + label73: while (true) { + int result; + do { + if (i >= 2) { + break label73; + } + + result = this.joggSyncState.pageout(this.joggPage); + if (result == 0) { + break label73; + } + } while (result != 1); + + this.joggStreamState.pagein(this.joggPage); + + while (i < 2) { + result = this.joggStreamState.packetout(this.joggPacket); + if (result == 0) { + break; + } + + if (result == -1) { + logger.error("[{}]: Secondary Ogg header corrupt in method 'readHeader'.", errorString); + return false; + } + + this.jorbisInfo.synthesis_headerin(this.jorbisComment, this.joggPacket); + ++i; + } + } + + this.index = this.joggSyncState.buffer(this.bufferSize); + bytes = this.inputStream.read(this.joggSyncState.data, this.index, this.bufferSize); + if (bytes < 0) { + bytes = 0; + } + + if (bytes == 0 && i < 2) { + logger.error( + "[{}]: End of file reached before finished reading Ogg header in method 'readHeader'", + errorString); + return false; + } + + this.joggSyncState.wrote(bytes); + } + + this.index = this.joggSyncState.buffer(this.bufferSize); + this.buffer = this.joggSyncState.data; + } + } + } + + this.jorbisDspState.synthesis_init(this.jorbisInfo); + this.jorbisBlock.init(this.jorbisDspState); + int channels = this.jorbisInfo.channels; + int rate = this.jorbisInfo.rate; + this.pcmInfo = new float[1][][]; + this.pcmIndex = new int[channels]; + if(convertedBuffer == null || convertedBuffer.length != this.jorbisInfo.channels || (convertedBuffer.length > 0 && convertedBuffer[0].length != this.bufferSize)) { + this.convertedBuffer = new float[this.jorbisInfo.channels][this.bufferSize]; + } + + return true; + } + + private float[][] readBytes() { + if (this.endOfStream) { + return null; + } else { + float[][] returnBuffer = null; + switch (this.joggSyncState.pageout(this.joggPage)) { + default: + this.joggStreamState.pagein(this.joggPage); + if (this.joggPage.granulepos() == 0L) { + this.endOfStream = true; + return null; + } else { + label99: { + while (true) { + switch (this.joggStreamState.packetout(this.joggPacket)) { + case -1: + break; + case 0: + if (this.joggPage.eos() != 0) { + this.endOfStream = true; + } + break label99; + default: + if (this.jorbisBlock.synthesis(this.joggPacket) == 0) { + this.jorbisDspState.synthesis_blockin(this.jorbisBlock); + } + + int samples; + while ((samples = this.jorbisDspState.synthesis_pcmout(this.pcmInfo, + this.pcmIndex)) > 0) { + float[][] pcmf = this.pcmInfo[0]; + int bout = samples < bufferSize ? samples : this.bufferSize; + + for (int i = 0; i < this.jorbisInfo.channels; ++i) { + float[] f1 = convertedBuffer[i]; + float[] f2 = pcmf[i]; + int mono = this.pcmIndex[i]; + for (int j = 0; j < bout; ++j) { + f1[j] = f2[mono + j]; + } + } + + this.jorbisDspState.synthesis_read(bout); + returnBuffer = appendFloatArrays(returnBuffer, this.convertedBuffer, bout); + } + } + } + } + } + case -1: + case 0: + if (!this.endOfStream) { + this.index = this.joggSyncState.buffer(this.bufferSize); + this.buffer = this.joggSyncState.data; + + try { + this.count = this.inputStream.read(this.buffer, this.index, this.bufferSize); + } catch (Exception var11) { + return null; + } + + if (this.count == -1) { + return returnBuffer; + } + + this.joggSyncState.wrote(this.count); + if (this.count == 0) { + this.endOfStream = true; + } + } + + return returnBuffer; + } + } + } + + private static float[][] appendFloatArrays(float[][] arrayOne, float[][] arrayTwo, int arrayTwoBytes) { + int bytes = arrayTwoBytes; + int l; + if (arrayTwo != null && (l = arrayTwo[0].length) != 0) { + if (l < arrayTwoBytes) { + bytes = l; + } + } else { + bytes = 0; + } + + if ((arrayOne != null || arrayTwo != null) && bytes > 0) { + float[][] newArray; + + if (arrayOne == null) { + int ch = arrayTwo.length; + int len1 = arrayTwo[0].length; + newArray = new float[ch][bytes]; + for(int i = 0; i < ch; ++i) { + System.arraycopy(arrayTwo[i], 0, newArray[i], 0, bytes); + } + arrayTwo = null; + } else { + int ch = arrayOne.length; + int len1 = arrayOne[0].length; + if (arrayTwo != null && bytes > 0) { + newArray = new float[ch][len1 + bytes]; + for(int i = 0; i < ch; ++i) { + System.arraycopy(arrayOne[i], 0, newArray[i], 0, len1); + System.arraycopy(arrayTwo[i], 0, newArray[i], len1, bytes); + } + arrayOne = null; + arrayTwo = null; + } else { + newArray = new float[ch][len1]; + for(int i = 0; i < ch; ++i) { + System.arraycopy(arrayOne[i], 0, newArray[i], 0, len1); + } + arrayOne = null; + } + } + + return newArray; + } else { + return null; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/LegacyKeycodeTranslator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/LegacyKeycodeTranslator.java new file mode 100755 index 0000000..2bc135b --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/LegacyKeycodeTranslator.java @@ -0,0 +1,328 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Sets; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class LegacyKeycodeTranslator { + + public static class LegacyKeycode { + + public final int keyCode; + public final int location; + + private LegacyKeycode(int keyCode, int location) { + this.keyCode = keyCode; + this.location = location; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof LegacyKeycode)) + return false; + LegacyKeycode other = (LegacyKeycode) obj; + if (keyCode != other.keyCode) + return false; + if (location != other.location) + return false; + return true; + } + + } + + private static final Set numpadVolatile = Sets.newHashSet( + "Comma", "Minus", "Period", "Slash", "Equal", "Enter", "Digit0", "Digit1", "Digit2", "Digit3", + "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9", "IntlYen"); + + private final Map codeLookupBase = new HashMap<>(); + private final Map codeLookupLayout = new HashMap<>(); + + public LegacyKeycodeTranslator() { + codeLookupBase.put("Digit0", new LegacyKeycode(0x30, 0)); + codeLookupBase.put("Digit1", new LegacyKeycode(0x31, 0)); + codeLookupBase.put("Digit2", new LegacyKeycode(0x32, 0)); + codeLookupBase.put("Digit3", new LegacyKeycode(0x33, 0)); + codeLookupBase.put("Digit4", new LegacyKeycode(0x34, 0)); + codeLookupBase.put("Digit5", new LegacyKeycode(0x35, 0)); + codeLookupBase.put("Digit6", new LegacyKeycode(0x36, 0)); + codeLookupBase.put("Digit7", new LegacyKeycode(0x37, 0)); + codeLookupBase.put("Digit8", new LegacyKeycode(0x38, 0)); + codeLookupBase.put("Digit9", new LegacyKeycode(0x39, 0)); + codeLookupBase.put("KeyA", new LegacyKeycode(0x41, 0)); + codeLookupBase.put("KeyB", new LegacyKeycode(0x42, 0)); + codeLookupBase.put("KeyC", new LegacyKeycode(0x43, 0)); + codeLookupBase.put("KeyD", new LegacyKeycode(0x44, 0)); + codeLookupBase.put("KeyE", new LegacyKeycode(0x45, 0)); + codeLookupBase.put("KeyF", new LegacyKeycode(0x46, 0)); + codeLookupBase.put("KeyG", new LegacyKeycode(0x47, 0)); + codeLookupBase.put("KeyH", new LegacyKeycode(0x48, 0)); + codeLookupBase.put("KeyI", new LegacyKeycode(0x49, 0)); + codeLookupBase.put("KeyJ", new LegacyKeycode(0x4A, 0)); + codeLookupBase.put("KeyK", new LegacyKeycode(0x4B, 0)); + codeLookupBase.put("KeyL", new LegacyKeycode(0x4C, 0)); + codeLookupBase.put("KeyM", new LegacyKeycode(0x4D, 0)); + codeLookupBase.put("KeyN", new LegacyKeycode(0x4E, 0)); + codeLookupBase.put("KeyO", new LegacyKeycode(0x4F, 0)); + codeLookupBase.put("KeyP", new LegacyKeycode(0x50, 0)); + codeLookupBase.put("KeyQ", new LegacyKeycode(0x51, 0)); + codeLookupBase.put("KeyR", new LegacyKeycode(0x52, 0)); + codeLookupBase.put("KeyS", new LegacyKeycode(0x53, 0)); + codeLookupBase.put("KeyT", new LegacyKeycode(0x54, 0)); + codeLookupBase.put("KeyU", new LegacyKeycode(0x55, 0)); + codeLookupBase.put("KeyV", new LegacyKeycode(0x56, 0)); + codeLookupBase.put("KeyW", new LegacyKeycode(0x57, 0)); + codeLookupBase.put("KeyX", new LegacyKeycode(0x58, 0)); + codeLookupBase.put("KeyY", new LegacyKeycode(0x59, 0)); + codeLookupBase.put("KeyZ", new LegacyKeycode(0x5A, 0)); + codeLookupBase.put("Comma", new LegacyKeycode(0xBC, 0)); + codeLookupBase.put("Period", new LegacyKeycode(0xBE, 0)); + codeLookupBase.put("Semicolon", new LegacyKeycode(0xBA, 0)); + codeLookupBase.put("Quote", new LegacyKeycode(0xDE, 0)); + codeLookupBase.put("BracketLeft", new LegacyKeycode(0xDB, 0)); + codeLookupBase.put("BracketRight", new LegacyKeycode(0xDD, 0)); + codeLookupBase.put("Backquote", new LegacyKeycode(0xC0, 0)); + codeLookupBase.put("Backslash", new LegacyKeycode(0xDC, 0)); + codeLookupBase.put("IntlBackslash", new LegacyKeycode(0xDC, 0)); + codeLookupBase.put("Minus", new LegacyKeycode(0xBD, 0)); + codeLookupBase.put("Equal", new LegacyKeycode(0xBB, 0)); + codeLookupBase.put("Slash", new LegacyKeycode(0xBF, 0)); + codeLookupBase.put("IntlRo", new LegacyKeycode(0xC1, 0)); + codeLookupBase.put("IntlYen", new LegacyKeycode(0xFF, 0)); + codeLookupBase.put("AltLeft", new LegacyKeycode(0x12, 1)); + codeLookupBase.put("AltRight", new LegacyKeycode(0x12, 2)); + codeLookupBase.put("CapsLock", new LegacyKeycode(0x14, 0)); + codeLookupBase.put("ControlLeft", new LegacyKeycode(0x11, 1)); + codeLookupBase.put("ControlRight", new LegacyKeycode(0x11, 2)); + codeLookupBase.put("MetaLeft", new LegacyKeycode(0x5B, 1)); + codeLookupBase.put("MetaRight", new LegacyKeycode(0x5C, 2)); + codeLookupBase.put("ShiftLeft", new LegacyKeycode(0x10, 1)); + codeLookupBase.put("ShiftRight", new LegacyKeycode(0x10, 2)); + codeLookupBase.put("ContextMenu", new LegacyKeycode(0x5D, 0)); + codeLookupBase.put("Enter", new LegacyKeycode(0x0D, 0)); + codeLookupBase.put("Space", new LegacyKeycode(0x20, 0)); + codeLookupBase.put("Backspace", new LegacyKeycode(0x08, 0)); + codeLookupBase.put("Tab", new LegacyKeycode(0x09, 0)); + codeLookupBase.put("Delete", new LegacyKeycode(0x2E, 0)); + codeLookupBase.put("End", new LegacyKeycode(0x23, 0)); + codeLookupBase.put("Help", new LegacyKeycode(0x2D, 0)); + codeLookupBase.put("Home", new LegacyKeycode(0x24, 0)); + codeLookupBase.put("Insert", new LegacyKeycode(0x2D, 0)); + codeLookupBase.put("PageDown", new LegacyKeycode(0x22, 0)); + codeLookupBase.put("PageUp", new LegacyKeycode(0x21, 0)); + codeLookupBase.put("ArrowDown", new LegacyKeycode(0x28, 0)); + codeLookupBase.put("ArrowLeft", new LegacyKeycode(0x25, 0)); + codeLookupBase.put("ArrowRight", new LegacyKeycode(0x27, 0)); + codeLookupBase.put("ArrowUp", new LegacyKeycode(0x26, 0)); + codeLookupBase.put("Escape", new LegacyKeycode(0x1B, 0)); + codeLookupBase.put("PrintScreen", new LegacyKeycode(0x2C, 0)); + codeLookupBase.put("ScrollLock", new LegacyKeycode(0x91, 0)); + codeLookupBase.put("Pause", new LegacyKeycode(0x13, 0)); + codeLookupBase.put("F1", new LegacyKeycode(0x70, 0)); + codeLookupBase.put("F2", new LegacyKeycode(0x71, 0)); + codeLookupBase.put("F3", new LegacyKeycode(0x72, 0)); + codeLookupBase.put("F4", new LegacyKeycode(0x73, 0)); + codeLookupBase.put("F5", new LegacyKeycode(0x74, 0)); + codeLookupBase.put("F6", new LegacyKeycode(0x75, 0)); + codeLookupBase.put("F7", new LegacyKeycode(0x76, 0)); + codeLookupBase.put("F8", new LegacyKeycode(0x77, 0)); + codeLookupBase.put("F9", new LegacyKeycode(0x78, 0)); + codeLookupBase.put("F10", new LegacyKeycode(0x79, 0)); + codeLookupBase.put("F11", new LegacyKeycode(0x7A, 0)); + codeLookupBase.put("F12", new LegacyKeycode(0x7B, 0)); + codeLookupBase.put("F13", new LegacyKeycode(0x7C, 0)); + codeLookupBase.put("F14", new LegacyKeycode(0x7D, 0)); + codeLookupBase.put("F15", new LegacyKeycode(0x7E, 0)); + codeLookupBase.put("F16", new LegacyKeycode(0x7F, 0)); + codeLookupBase.put("F17", new LegacyKeycode(0x80, 0)); + codeLookupBase.put("F18", new LegacyKeycode(0x81, 0)); + codeLookupBase.put("F19", new LegacyKeycode(0x82, 0)); + codeLookupBase.put("F20", new LegacyKeycode(0x83, 0)); + codeLookupBase.put("F21", new LegacyKeycode(0x84, 0)); + codeLookupBase.put("F22", new LegacyKeycode(0x85, 0)); + codeLookupBase.put("F23", new LegacyKeycode(0x86, 0)); + codeLookupBase.put("F24", new LegacyKeycode(0x87, 0)); + codeLookupBase.put("NumLock", new LegacyKeycode(0x90, 3)); + codeLookupBase.put("Numpad0", new LegacyKeycode(0x60, 3)); + codeLookupBase.put("Numpad1", new LegacyKeycode(0x61, 3)); + codeLookupBase.put("Numpad2", new LegacyKeycode(0x62, 3)); + codeLookupBase.put("Numpad3", new LegacyKeycode(0x63, 3)); + codeLookupBase.put("Numpad4", new LegacyKeycode(0x64, 3)); + codeLookupBase.put("Numpad5", new LegacyKeycode(0x65, 3)); + codeLookupBase.put("Numpad6", new LegacyKeycode(0x66, 3)); + codeLookupBase.put("Numpad7", new LegacyKeycode(0x67, 3)); + codeLookupBase.put("Numpad8", new LegacyKeycode(0x68, 3)); + codeLookupBase.put("Numpad9", new LegacyKeycode(0x69, 3)); + codeLookupBase.put("NumpadAdd", new LegacyKeycode(0x6B, 3)); + codeLookupBase.put("NumpadComma", new LegacyKeycode(0xC2, 3)); + codeLookupBase.put("NumpadDecimal", new LegacyKeycode(0x6E, 3)); + codeLookupBase.put("NumpadDivide", new LegacyKeycode(0x6F, 3)); + codeLookupBase.put("NumpadEnter", new LegacyKeycode(0x0D, 3)); + codeLookupBase.put("NumpadEqual", new LegacyKeycode(0x0C, 3)); + codeLookupBase.put("NumpadMultiply", new LegacyKeycode(0x6A, 3)); + codeLookupBase.put("NumpadSubtract", new LegacyKeycode(0x6D, 3)); + } + + public LegacyKeycodeTranslator addBrowserLayoutMapping(String keyChar, String codeStr) { + LegacyKeycode mapTo = codeLookupBase.get(codeStr); + if(mapTo != null) { + String keyCode = getCodeFromLayoutChar(keyChar); + if(keyCode != null && !keyCode.equals(codeStr) && !(codeStr.startsWith("Numpad") && numpadVolatile.contains(keyCode)) && !mapTo.equals(codeLookupBase.get(keyCode))) { + codeLookupLayout.put(keyCode, mapTo); + } + } + return this; + } + + public int getRemappedKeyCount() { + return codeLookupLayout.size(); + } + + public Map buildLayoutTable() { + if(codeLookupLayout.isEmpty()) { + return codeLookupBase; + } + Map ret = new HashMap<>(); + ret.putAll(codeLookupBase); + ret.putAll(codeLookupLayout); + return ret; + } + + public static String getCodeFromLayoutChar(String keyChar) { + if(keyChar.length() != 1) { + return null; + } + char c = keyChar.charAt(0); + String ret = getCodeFromLayoutChar0(c); + if(ret == null) { + ret = getCodeFromLayoutChar0(Character.toLowerCase(c)); + } + return ret; + } + + private static String getCodeFromLayoutChar0(char keyChar) { + switch(keyChar) { + case 'e': + return "KeyE"; + case 'd': + return "KeyD"; + case 'u': + return "KeyU"; + case '-': + return "Minus"; + case 'h': + return "KeyH"; + case 'z': + return "KeyZ"; + case '=': + return "Equal"; + case 'p': + return "KeyP"; + case ';': + return "Semicolon"; + case ']': + return "BracketRight"; + case '/': + return "Slash"; + case '[': + return "BracketLeft"; + case 'l': + return "KeyL"; + case '8': + return "Digit8"; + case 'w': + return "KeyW"; + case 's': + return "KeyS"; + case '5': + return "Digit5"; + case '9': + return "Digit9"; + case 'o': + return "KeyO"; + case '.': + return "Period"; + case '6': + return "Digit6"; + case 'v': + return "KeyV"; + case '3': + return "Digit3"; + case '`': + return "Backquote"; + case 'g': + return "KeyG"; + case 'j': + return "KeyJ"; + case 'q': + return "KeyQ"; + case '1': + return "Digit1"; + case 't': + return "KeyT"; + case 'y': + return "KeyY"; + case '\'': + return "Quote"; + case '\\': + return "Backslash"; + case 'k': + return "KeyK"; + case 'f': + return "KeyF"; + case 'i': + return "KeyI"; + case 'r': + return "KeyR"; + case 'x': + return "KeyX"; + case 'a': + return "KeyA"; + case '2': + return "Digit2"; + case '7': + return "Digit7"; + case 'm': + return "KeyM"; + case '4': + return "Digit4"; + case '0': + return "Digit0"; + case 'n': + return "KeyN"; + case 'b': + return "KeyB"; + case 'c': + return "KeyC"; + case ',': + return "Comma"; + case '*': + return "NumpadMultiply"; + case 0xA5: + return "IntlYen"; + default: + return null; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/MessageChannel.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/MessageChannel.java new file mode 100755 index 0000000..6ba9db0 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/MessageChannel.java @@ -0,0 +1,37 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; +import org.teavm.jso.workers.MessagePort; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class MessageChannel implements JSObject { + + @JSBody(params = { }, script = "return (typeof MessageChannel !== \"undefined\");") + public static native boolean supported(); + + @JSBody(params = { }, script = "return new MessageChannel();") + public static native MessageChannel create(); + + @JSProperty + public abstract MessagePort getPort1(); + + @JSProperty + public abstract MessagePort getPort2(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/OffsetTouch.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/OffsetTouch.java new file mode 100755 index 0000000..1f41562 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/OffsetTouch.java @@ -0,0 +1,43 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.SortedTouchEvent.ITouchUIDMapper; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class OffsetTouch { + + public final Touch touch; + public final int eventUID; + public final int posX; + public final int posY; + + public OffsetTouch(Touch touch, int eventUID, int posX, int posY) { + this.touch = touch; + this.eventUID = eventUID; + this.posX = posX; + this.posY = posY; + } + + public static OffsetTouch create(Touch touch, ITouchUIDMapper mapper, int originX, int originY) { + double contentScale = PlatformInput.getDPI(); + OffsetTouch ot = new OffsetTouch(touch, mapper.call(touch.getIdentifier()), + (int) ((touch.getPageX() - originX) * contentScale), + PlatformInput.getWindowHeight() - (int) ((touch.getPageY() - originY) * contentScale) - 1); + return ot; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/PCMToWAVLoader.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/PCMToWAVLoader.java new file mode 100755 index 0000000..b3411df --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/PCMToWAVLoader.java @@ -0,0 +1,118 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PCMToWAVLoader { + + public static int getWAVLen(List data, boolean floating) { + int i = 44; + int j = floating ? 4 : 2; + int k; + for(float[][] f : data) { + k = f.length; + if(k == 0) continue; + i += k * f[0].length * j; + } + return i; + } + + public static void createWAV16(List data, int chCount, int sampleRate, ByteBuffer bufferOut) { + if(chCount == 0 || data.isEmpty()) return; + int finalSize = bufferOut.remaining(); + + // header + bufferOut.putInt(0x46464952); // magic + bufferOut.putInt(finalSize - 8); // file len + bufferOut.putInt(0x45564157); // magic + + // format chunk + bufferOut.putInt(0x20746D66); // magic + bufferOut.putInt(16); // format chunk len - 8 + bufferOut.putShort((short)1); // audio format = int + bufferOut.putShort((short)chCount); // channels + bufferOut.putInt(sampleRate); // sample rate + bufferOut.putInt(sampleRate * chCount * 2); // bytes per second + bufferOut.putShort((short)(chCount * 2)); // bytes per sample + bufferOut.putShort((short)16); // bits per sample + + // data chunk + bufferOut.putInt(0x61746164); // magic + bufferOut.putInt(finalSize - 44); + + for(float[][] f : data) { + for(int i = 0, l = f[0].length; i < l; ++i) { + for(int c = 0; c < chCount; ++c) { + int val = (int)(f[c][i] * 32767.0f); + if (val > 32767) { + val = 32767; + } + if (val < -32768) { + val = -32768; + } + if (val < 0) { + val |= 32768; + } + bufferOut.putShort((short)val); + } + } + } + + if(bufferOut.hasRemaining()) { + throw new IllegalStateException("Buffer was the wrong size! " + bufferOut.remaining() + " remaining"); + } + } + + public static void createWAV32F(List data, int chCount, int sampleRate, ByteBuffer bufferOut) { + if(chCount == 0 || data.isEmpty()) return; + int finalSize = bufferOut.remaining(); + + // header + bufferOut.putInt(0x46464952); // magic + bufferOut.putInt(finalSize - 8); // file len + bufferOut.putInt(0x45564157); // magic + + // format chunk + bufferOut.putInt(0x20746D66); // magic + bufferOut.putInt(16); // format chunk len - 8 + bufferOut.putShort((short)3); // audio format = float + bufferOut.putShort((short)chCount); // channels + bufferOut.putInt(sampleRate); // sample rate + bufferOut.putInt(sampleRate * chCount * 4); // bytes per second + bufferOut.putShort((short)(chCount * 4)); // bytes per sample + bufferOut.putShort((short)32); // bits per sample + + // data chunk + bufferOut.putInt(0x61746164); // magic + bufferOut.putInt(finalSize - 44); + + for(float[][] f : data) { + for(int i = 0, l = f[0].length; i < l; ++i) { + for(int c = 0; c < chCount; ++c) { + bufferOut.putFloat(f[c][i]); + } + } + } + + if(bufferOut.hasRemaining()) { + throw new IllegalStateException("Buffer was the wrong size! " + finalSize + " " + bufferOut.remaining() + " remaining"); + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/SortedTouchEvent.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/SortedTouchEvent.java new file mode 100755 index 0000000..fad6aa7 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/SortedTouchEvent.java @@ -0,0 +1,85 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class SortedTouchEvent { + + public static interface ITouchUIDMapper { + int call(int uidIn); + } + + public final TouchEvent event; + public final EnumTouchEvent type; + private final List targetTouches; + private final List changedTouches; + private final List eventTouches; + + public SortedTouchEvent(TouchEvent event, ITouchUIDMapper mapper) { + changedTouches = TeaVMUtils.toSortedTouchList(event.getChangedTouches(), mapper, PlatformInput.touchOffsetXTeaVM, PlatformInput.touchOffsetYTeaVM); + targetTouches = TeaVMUtils.toSortedTouchList(event.getTargetTouches(), mapper, PlatformInput.touchOffsetXTeaVM, PlatformInput.touchOffsetYTeaVM); + this.event = event; + switch(event.getType()) { + case "touchstart": + type = EnumTouchEvent.TOUCHSTART; + eventTouches = changedTouches; + break; + case "touchmove": + type = EnumTouchEvent.TOUCHMOVE; + eventTouches = targetTouches; + break; + case "touchend": + case "touchcancel": + default: + type = EnumTouchEvent.TOUCHEND; + eventTouches = changedTouches; + break; + } + } + + public int getTouchesSize() { + return event.getTouches().getLength(); + } + + public int getChangedTouchesSize() { + return event.getChangedTouches().getLength(); + } + + public List getChangedTouches() { + return changedTouches; + } + + public int getTargetTouchesSize() { + return event.getTargetTouches().getLength(); + } + + public List getTargetTouches() { + return targetTouches; + } + + public int getEventTouchesSize() { + return eventTouches.size(); + } + + public List getEventTouches() { + return eventTouches; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMBlobURLHandle.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMBlobURLHandle.java new file mode 100755 index 0000000..7c95411 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMBlobURLHandle.java @@ -0,0 +1,28 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface TeaVMBlobURLHandle { + + default String toExternalForm() { + return TeaVMBlobURLManager.toExternalForm(this); + } + + default void release() { + TeaVMBlobURLManager.releaseURL(this); + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMBlobURLManager.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMBlobURLManager.java new file mode 100755 index 0000000..1c97ce2 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMBlobURLManager.java @@ -0,0 +1,188 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMBlobURLManager { + + private static final Logger logger = LogManager.getLogger("TeaVMBlobURLManager"); + + private static boolean isSameOriginSupport = true; + + public static void initialize() { + if(((TeaVMClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isDisableBlobURLsTeaVM()) { + isSameOriginSupport = false; + logger.info("Note: Blob urls have been disabled, client will use data: urls instead"); + }else { + try { + isSameOriginSupport = checkSameOriginSupport(); + }catch(Throwable t) { + isSameOriginSupport = false; + } + if(!isSameOriginSupport) { + logger.warn("Warning: Same-origin fetch support detected as false, client will use data: urls instead of blob: urls"); + } + } + } + + @Async + private static native Boolean checkSameOriginSupport(); + + private static void checkSameOriginSupport(final AsyncCallback cb) { + try { + checkSameOriginSupport0((v) -> cb.complete(v)); + }catch(Throwable t) { + cb.error(t); + } + } + + @JSFunctor + private static interface SameOriginSupportCallback extends JSObject { + void call(boolean support); + } + + @JSBody(params = { "cb" }, script = "if((typeof URL === \"undefined\") || (typeof URL.createObjectURL !== \"function\")) { cb(false); }" + + "else { var objURL = URL.createObjectURL(new Blob([new Uint8Array([69, 69, 69, 69])]));" + + "if(!objURL) { cb(false); return; }" + + "var eag = function(theObjURL, theXHRObj) {" + + "theXHRObj.responseType = \"arraybuffer\";" + + "theXHRObj.addEventListener(\"load\", function(evt) { try { URL.revokeObjectURL(theObjURL); } catch(exx) { }" + + "var stat = theXHRObj.status;" + + "if(stat === 0 || (stat >= 200 && stat < 400)) {" + + "var typedArr = new Uint8Array(theXHRObj.response);" + + "if(typedArr.length === 4 && typedArr[0] === 69 && typedArr[1] === 69 && typedArr[2] === 69 && typedArr[3] === 69) {" + + "cb(true);" + + "} else { cb(false); } } else { cb(false); } });" + + "theXHRObj.addEventListener(\"error\", function(evt) { try { URL.revokeObjectURL(theObjURL); } catch(exx) { } cb(false); });" + + "theXHRObj.open(\"GET\", theObjURL, true);" + + "theXHRObj.send();" + + "}; eag(objURL, new XMLHttpRequest()); }") + private static native void checkSameOriginSupport0(SameOriginSupportCallback cb); + + private static class HandleRealBlobURL implements TeaVMBlobURLHandle { + + private final String blobURL; + + public HandleRealBlobURL(String blobURL) { + this.blobURL = blobURL; + } + + @Override + public String toExternalForm() { + return blobURL; + } + + @Override + public void release() { + revokeBlobURL(blobURL); + } + + } + + private static class HandleFakeBlobURL implements TeaVMBlobURLHandle { + + private final byte[] blobData; + private final String blobMIME; + + public HandleFakeBlobURL(byte[] blobData, String blobMIME) { + this.blobData = blobData; + this.blobMIME = blobMIME; + } + + @Override + public String toExternalForm() { + return "data:" + blobMIME + ";base64," + Base64.encodeBase64String(blobData); + } + + @Override + public void release() { + + } + + } + + public static TeaVMBlobURLHandle registerNewURLByte(byte[] objectData, String mimeType) { + if(isSameOriginSupport) { + return new HandleRealBlobURL(createBlobURL(TeaVMUtils.unwrapArrayBuffer(objectData), mimeType)); + }else { + return new HandleFakeBlobURL(objectData, mimeType); + } + } + + public static TeaVMBlobURLHandle registerNewURLArrayBuffer(ArrayBuffer objectData, String mimeType) { + return registerNewURLByte(TeaVMUtils.wrapByteArrayBuffer(objectData), mimeType); + } + + public static TeaVMBlobURLHandle registerNewURLBlob(JSObject objectData) { + if(isSameOriginSupport) { + return new HandleRealBlobURL(createBlobURL(objectData)); + }else { + return new HandleFakeBlobURL(TeaVMUtils.wrapByteArrayBuffer(blobToArrayBuffer(objectData)), getBlobMime(objectData)); + } + } + + @JSBody(params = { "objectData" }, script = "return objectData.type || \"application/octet-stream\";") + private static native String getBlobMime(JSObject objectData); + + @Async + private static native ArrayBuffer blobToArrayBuffer(JSObject objectData); + + private static void blobToArrayBuffer(JSObject objectData, final AsyncCallback cb) { + blobToArrayBuffer0(objectData, cb::complete); + } + + @JSFunctor + private static interface ArrayBufferCallback extends JSObject { + void call(ArrayBuffer buf); + } + + @JSBody(params = { "objectData", "callback" }, script = + "var eag = function(reader){" + + "reader.addEventListener(\"loadend\",function(evt){ callback(reader.result); });" + + "reader.addEventListener(\"error\",function(evt){ callback(null); });" + + "reader.readAsArrayBuffer(objectData);" + + "}; eag(new FileReader());") + private static native ArrayBuffer blobToArrayBuffer0(JSObject objectData, ArrayBufferCallback callback); + + @JSBody(params = { "buf", "mime" }, script = "return URL.createObjectURL(new Blob([buf], {type: mime}));") + private static native String createBlobURL(ArrayBuffer buf, String mime); + + @JSBody(params = { "objectBlob" }, script = "return URL.createObjectURL(objectBlob);") + private static native String createBlobURL(JSObject objectBlob); + + @JSBody(params = { "url" }, script = "URL.revokeObjectURL(url);") + private static native void revokeBlobURL(String url); + + public static String toExternalForm(TeaVMBlobURLHandle handle) { + return handle.toExternalForm(); + } + + public static void releaseURL(TeaVMBlobURLHandle handle) { + handle.release(); + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java index 79f4505..0aaa1fc 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java @@ -1,413 +1,620 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm; - -import java.util.ArrayList; -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; -import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom; -import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; -import org.json.JSONArray; -import org.json.JSONObject; -import org.teavm.jso.JSObject; -import org.teavm.jso.core.JSArrayReader; - -import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; -import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsHooks; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRelay; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsServer; -import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry; - -/** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class TeaVMClientConfigAdapter implements IClientConfigAdapter { - - public static final IClientConfigAdapter instance = new TeaVMClientConfigAdapter(); - - private String defaultLocale = "en_US"; - private List defaultServers = new ArrayList(); - private List relays = new ArrayList(); - private String serverToJoin = null; - private String worldsDB = "worlds"; - private String resourcePacksDB = "resourcePacks"; - private JSONObject integratedServerOpts; - private boolean checkShaderGLErrors = false; - private boolean demoMode = EaglercraftVersion.forceDemoMode; - private boolean isAllowUpdateSvc = EaglercraftVersion.enableUpdateService; - private boolean isAllowUpdateDL = EaglercraftVersion.enableUpdateService; - private boolean isEnableDownloadOfflineButton = true; - private String downloadOfflineButtonLink = null; - private boolean useSpecialCursors = false; - private boolean logInvalidCerts = false; - private boolean checkRelaysForUpdates = false; - private boolean enableSignatureBadge = false; - private boolean allowVoiceClient = true; - private boolean allowFNAWSkins = true; - private String localStorageNamespace = "_eaglercraftX"; - private final TeaVMClientConfigAdapterHooks hooks = new TeaVMClientConfigAdapterHooks(); - private boolean enableMinceraft = true; - private boolean crashOnUncaughtExceptions = false; - - public void loadNative(JSObject jsObject) { - integratedServerOpts = new JSONObject(); - JSEaglercraftXOptsRoot eaglercraftXOpts = (JSEaglercraftXOptsRoot) jsObject; - - defaultLocale = eaglercraftXOpts.getLang("en_US"); - serverToJoin = eaglercraftXOpts.getJoinServer(null); - worldsDB = eaglercraftXOpts.getWorldsDB("worlds"); - resourcePacksDB = eaglercraftXOpts.getResourcePacksDB("resourcePacks"); - checkShaderGLErrors = eaglercraftXOpts.getCheckShaderGLErrors(false); - demoMode = EaglercraftVersion.forceDemoMode || eaglercraftXOpts.getDemoMode(false); - isAllowUpdateSvc = EaglercraftVersion.enableUpdateService && !demoMode - && eaglercraftXOpts.getAllowUpdateSvc(true); - isAllowUpdateDL = EaglercraftVersion.enableUpdateService && !demoMode - && eaglercraftXOpts.getAllowUpdateDL(true); - isEnableDownloadOfflineButton = eaglercraftXOpts.getEnableDownloadOfflineButton(true); - downloadOfflineButtonLink = eaglercraftXOpts.getDownloadOfflineButtonLink(null); - useSpecialCursors = eaglercraftXOpts.getHtml5CursorSupport(false); - logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode - && eaglercraftXOpts.getLogInvalidCerts(false); - enableSignatureBadge = eaglercraftXOpts.getEnableSignatureBadge(false); - allowVoiceClient = eaglercraftXOpts.getAllowVoiceClient(true); - allowFNAWSkins = !demoMode && eaglercraftXOpts.getAllowFNAWSkins(true); - localStorageNamespace = eaglercraftXOpts.getLocalStorageNamespace(EaglercraftVersion.localStorageNamespace); - enableMinceraft = eaglercraftXOpts.getEnableMinceraft(true); - crashOnUncaughtExceptions = eaglercraftXOpts.getCrashOnUncaughtExceptions(false); - JSEaglercraftXOptsHooks hooksObj = eaglercraftXOpts.getHooks(); - if (hooksObj != null) { - hooks.loadHooks(hooksObj); - } - - integratedServerOpts.put("worldsDB", worldsDB); - integratedServerOpts.put("demoMode", demoMode); - integratedServerOpts.put("lang", defaultLocale); - integratedServerOpts.put("allowUpdateSvc", isAllowUpdateSvc); - integratedServerOpts.put("allowUpdateDL", isAllowUpdateDL); - integratedServerOpts.put("allowVoiceClient", allowVoiceClient); - integratedServerOpts.put("allowFNAWSkins", allowFNAWSkins); - integratedServerOpts.put("crashOnUncaughtExceptions", crashOnUncaughtExceptions); - - JSArrayReader serversArray = eaglercraftXOpts.getServers(); - if (serversArray != null) { - for (int i = 0, l = serversArray.getLength(); i < l; ++i) { - JSEaglercraftXOptsServer serverEntry = serversArray.get(i); - String serverAddr = serverEntry.getAddr(); - if (serverAddr != null) { - String serverName = serverEntry.getName("Default Server #" + i); - defaultServers.add(new DefaultServer(serverName, serverAddr)); - } - } - } - - JSArrayReader relaysArray = eaglercraftXOpts.getRelays(); - if (relaysArray != null) { - boolean gotAPrimary = false; - for (int i = 0, l = relaysArray.getLength(); i < l; ++i) { - JSEaglercraftXOptsRelay relay = relaysArray.get(i); - String addr = relay.getAddr(); - if (addr != null) { - boolean p = relay.getPrimary(); - if (p) { - if (gotAPrimary) { - p = false; - } else { - gotAPrimary = true; - } - } - relays.add(new RelayEntry(relay.getAddr(), relay.getComment("Default Relay #" + i), p)); - } - } - } - - boolean officialUpdates = !demoMode - && EaglercraftVersion.updateBundlePackageName.equals("net.lax1dude.eaglercraft.v1_8.client"); - if (relays.size() <= 0) { - int choice = ThreadLocalRandom.current().nextInt(3); - relays.add(new RelayEntry("wss://relay.deev.is/", "lax1dude relay #1", choice == 0)); - relays.add(new RelayEntry("wss://relay.lax1dude.net/", "lax1dude relay #2", choice == 1)); - relays.add(new RelayEntry("wss://relay.shhnowisnottheti.me/", "ayunami relay #1", choice == 2)); - checkRelaysForUpdates = !demoMode && eaglercraftXOpts.getCheckRelaysForUpdates(officialUpdates); - } else { - if (officialUpdates) { - for (int i = 0, l = relays.size(); i < l; ++i) { - String addr = relays.get(i).address; - if (!addr.contains("deev.is") && !addr.contains("lax1dude.net") - && !addr.contains("shhnowisnottheti.me")) { - officialUpdates = false; - break; - } - } - } - checkRelaysForUpdates = !demoMode && eaglercraftXOpts.getCheckRelaysForUpdates(officialUpdates); - } - - RelayManager.relayManager.load(EagRuntime.getStorage("r")); - - if (RelayManager.relayManager.count() <= 0) { - RelayManager.relayManager.loadDefaults(); - RelayManager.relayManager.save(); - } - } - - public void loadJSON(JSONObject eaglercraftOpts) { - integratedServerOpts = eaglercraftOpts; - defaultLocale = eaglercraftOpts.optString("lang", "en_US"); - serverToJoin = eaglercraftOpts.optString("joinServer", null); - worldsDB = eaglercraftOpts.optString("worldsDB", "worlds"); - resourcePacksDB = eaglercraftOpts.optString("resourcePacksDB", "resourcePacks"); - checkShaderGLErrors = eaglercraftOpts.optBoolean("checkShaderGLErrors", false); - if (EaglercraftVersion.forceDemoMode) { - eaglercraftOpts.put("demoMode", true); - } - demoMode = EaglercraftVersion.forceDemoMode || eaglercraftOpts.optBoolean("demoMode", false); - isAllowUpdateSvc = EaglercraftVersion.enableUpdateService && !demoMode - && eaglercraftOpts.optBoolean("allowUpdateSvc", true); - isAllowUpdateDL = EaglercraftVersion.enableUpdateService && !demoMode - && eaglercraftOpts.optBoolean("allowUpdateDL", true); - isEnableDownloadOfflineButton = eaglercraftOpts.optBoolean("enableDownloadOfflineButton", true); - downloadOfflineButtonLink = eaglercraftOpts.optString("downloadOfflineButtonLink", null); - useSpecialCursors = eaglercraftOpts.optBoolean("html5CursorSupport", false); - logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode - && eaglercraftOpts.optBoolean("logInvalidCerts", false); - enableSignatureBadge = eaglercraftOpts.optBoolean("enableSignatureBadge", false); - allowVoiceClient = eaglercraftOpts.optBoolean("allowVoiceClient", true); - allowFNAWSkins = eaglercraftOpts.optBoolean("allowFNAWSkins", true); - localStorageNamespace = eaglercraftOpts.optString("localStorageNamespace", - EaglercraftVersion.localStorageNamespace); - enableMinceraft = eaglercraftOpts.optBoolean("enableMinceraft", true); - crashOnUncaughtExceptions = eaglercraftOpts.optBoolean("crashOnUncaughtExceptions", false); - JSONArray serversArray = eaglercraftOpts.optJSONArray("servers"); - if (serversArray != null) { - for (int i = 0, l = serversArray.length(); i < l; ++i) { - JSONObject serverEntry = serversArray.getJSONObject(i); - String serverAddr = serverEntry.optString("addr", null); - if (serverAddr != null) { - String serverName = serverEntry.optString("name", "Default Server #" + i); - defaultServers.add(new DefaultServer(serverName, serverAddr)); - } - } - } - - JSONArray relaysArray = eaglercraftOpts.optJSONArray("relays"); - if (relaysArray != null) { - boolean gotAPrimary = false; - for (int i = 0, l = relaysArray.length(); i < l; ++i) { - JSONObject relay = relaysArray.getJSONObject(i); - boolean p = relay.optBoolean("primary"); - if (p) { - if (gotAPrimary) { - p = false; - } else { - gotAPrimary = true; - } - } - relays.add(new RelayEntry(relay.getString("addr"), relay.getString("comment"), p)); - } - } - - boolean officialUpdates = !demoMode - && EaglercraftVersion.updateBundlePackageName.equals("net.lax1dude.eaglercraft.v1_8.client"); - if (relays.size() <= 0) { - int choice = ThreadLocalRandom.current().nextInt(3); - relays.add(new RelayEntry("wss://relay.deev.is/", "lax1dude relay #1", choice == 0)); - relays.add(new RelayEntry("wss://relay.lax1dude.net/", "lax1dude relay #2", choice == 1)); - relays.add(new RelayEntry("wss://relay.shhnowisnottheti.me/", "ayunami relay #1", choice == 2)); - checkRelaysForUpdates = !demoMode && eaglercraftOpts.optBoolean("checkRelaysForUpdates", officialUpdates); - } else { - if (officialUpdates) { - for (int i = 0, l = relays.size(); i < l; ++i) { - String addr = relays.get(i).address; - if (!addr.contains("deev.is") && !addr.contains("lax1dude.net") - && !addr.contains("shhnowisnottheti.me")) { - officialUpdates = false; - break; - } - } - } - checkRelaysForUpdates = !demoMode && eaglercraftOpts.optBoolean("checkRelaysForUpdates", officialUpdates); - } - - RelayManager.relayManager.load(EagRuntime.getStorage("r")); - - if (RelayManager.relayManager.count() <= 0) { - RelayManager.relayManager.loadDefaults(); - RelayManager.relayManager.save(); - } - } - - @Override - public String getDefaultLocale() { - return defaultLocale; - } - - @Override - public List getDefaultServerList() { - return defaultServers; - } - - @Override - public String getServerToJoin() { - return serverToJoin; - } - - @Override - public String getWorldsDB() { - return worldsDB; - } - - @Override - public String getResourcePacksDB() { - return resourcePacksDB; - } - - @Override - public JSONObject getIntegratedServerOpts() { - return integratedServerOpts; - } - - @Override - public List getRelays() { - return relays; - } - - @Override - public boolean isCheckShaderGLErrors() { - return checkShaderGLErrors; - } - - @Override - public boolean isDemo() { - return demoMode; - } - - @Override - public boolean allowUpdateSvc() { - return isAllowUpdateSvc; - } - - @Override - public boolean allowUpdateDL() { - return isAllowUpdateDL; - } - - @Override - public boolean isEnableDownloadOfflineButton() { - return isEnableDownloadOfflineButton; - } - - @Override - public String getDownloadOfflineButtonLink() { - return downloadOfflineButtonLink; - } - - @Override - public boolean useSpecialCursors() { - return useSpecialCursors; - } - - @Override - public boolean isLogInvalidCerts() { - return logInvalidCerts; - } - - @Override - public boolean isCheckRelaysForUpdates() { - return checkRelaysForUpdates; - } - - @Override - public boolean isEnableSignatureBadge() { - return enableSignatureBadge; - } - - @Override - public boolean isAllowVoiceClient() { - return allowVoiceClient; - } - - @Override - public boolean isAllowFNAWSkins() { - return allowFNAWSkins; - } - - @Override - public String getLocalStorageNamespace() { - return localStorageNamespace; - } - - @Override - public boolean isEnableMinceraft() { - return enableMinceraft; - } - - @Override - public IClientConfigAdapterHooks getHooks() { - return hooks; - } - - @Override - public String toString() { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("lang", defaultLocale); - jsonObject.put("joinServer", serverToJoin); - jsonObject.put("worldsDB", worldsDB); - jsonObject.put("resourcePacksDB", resourcePacksDB); - jsonObject.put("checkShaderGLErrors", checkShaderGLErrors); - jsonObject.put("demoMode", demoMode); - jsonObject.put("allowUpdateSvc", isAllowUpdateSvc); - jsonObject.put("allowUpdateDL", isAllowUpdateDL); - jsonObject.put("enableDownloadOfflineButton", isEnableDownloadOfflineButton); - jsonObject.put("downloadOfflineButtonLink", downloadOfflineButtonLink); - jsonObject.put("html5CursorSupport", useSpecialCursors); - jsonObject.put("logInvalidCerts", logInvalidCerts); - jsonObject.put("checkRelaysForUpdates", checkRelaysForUpdates); - jsonObject.put("enableSignatureBadge", enableSignatureBadge); - jsonObject.put("allowVoiceClient", allowVoiceClient); - jsonObject.put("allowFNAWSkins", allowFNAWSkins); - jsonObject.put("localStorageNamespace", localStorageNamespace); - jsonObject.put("enableMinceraft", enableMinceraft); - jsonObject.put("crashOnUncaughtExceptions", crashOnUncaughtExceptions); - JSONArray serversArr = new JSONArray(); - for (int i = 0, l = defaultServers.size(); i < l; ++i) { - DefaultServer srv = defaultServers.get(i); - JSONObject obj = new JSONObject(); - obj.put("addr", srv.addr); - obj.put("name", srv.name); - serversArr.put(obj); - } - jsonObject.put("servers", serversArr); - JSONArray relaysArr = new JSONArray(); - for (int i = 0, l = relays.size(); i < l; ++i) { - RelayEntry rl = relays.get(i); - JSONObject obj = new JSONObject(); - obj.put("addr", rl.address); - obj.put("comment", rl.comment); - obj.put("primary", rl.primary); - relaysArr.put(obj); - } - jsonObject.put("relays", relaysArr); - return jsonObject.toString(); - } -} +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom; +import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.IBootMenuConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; +import org.json.JSONArray; +import org.json.JSONObject; +import org.teavm.jso.JSObject; +import org.teavm.jso.core.JSArrayReader; + +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsHooks; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRelay; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsServer; +import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenuConfigAdapter { + + public static final IClientConfigAdapter instance = new TeaVMClientConfigAdapter(); + + private String defaultLocale = "en_US"; + private List defaultServers = new ArrayList<>(); + private List relays = new ArrayList<>(); + private String serverToJoin = null; + private String worldsDB = "worlds"; + private String resourcePacksDB = "resourcePacks"; + private JSONObject integratedServerOpts; + private boolean checkShaderGLErrors = false; + private boolean demoMode = EaglercraftVersion.forceDemoMode; + private boolean isAllowUpdateSvc = EaglercraftVersion.enableUpdateService; + private boolean isAllowUpdateDL = EaglercraftVersion.enableUpdateService; + private boolean isEnableDownloadOfflineButton = true; + private String downloadOfflineButtonLink = null; + private boolean useSpecialCursors = false; + private boolean logInvalidCerts = false; + private boolean checkRelaysForUpdates = false; + private boolean enableSignatureBadge = false; + private boolean allowVoiceClient = true; + private boolean allowFNAWSkins = true; + private String localStorageNamespace = "_eaglercraftX"; + private final TeaVMClientConfigAdapterHooks hooks = new TeaVMClientConfigAdapterHooks(); + private boolean enableMinceraft = true; + private boolean enableServerCookies = true; + private boolean allowServerRedirects = true; + private boolean crashOnUncaughtExceptions = false; + private boolean openDebugConsoleOnLaunch = false; + private boolean fixDebugConsoleUnloadListener = false; + private boolean forceWebViewSupport = false; + private boolean enableWebViewCSP = false; + private boolean autoFixLegacyStyleAttr = false; + private boolean showBootMenuOnLaunch = false; + private boolean bootMenuBlocksUnsignedClients = false; + private boolean allowBootMenu = true; + private boolean forceProfanityFilter = false; + private boolean forceWebGL1 = false; + private boolean forceWebGL2 = false; + private boolean allowExperimentalWebGL1 = false; + private boolean useWebGLExt = true; + private boolean useDelayOnSwap = false; + private boolean useJOrbisAudioDecoder = false; + private boolean useXHRFetch = false; + private boolean useVisualViewport = true; + private boolean deobfStackTraces = true; + private boolean disableBlobURLs = false; + private boolean eaglerNoDelay = false; + private boolean ramdiskMode = false; + private boolean singleThreadMode = false; + + public void loadNative(JSObject jsObject) { + integratedServerOpts = new JSONObject(); + JSEaglercraftXOptsRoot eaglercraftXOpts = (JSEaglercraftXOptsRoot)jsObject; + + defaultLocale = eaglercraftXOpts.getLang("en_US"); + serverToJoin = eaglercraftXOpts.getJoinServer(null); + worldsDB = eaglercraftXOpts.getWorldsDB("worlds"); + resourcePacksDB = eaglercraftXOpts.getResourcePacksDB("resourcePacks"); + checkShaderGLErrors = eaglercraftXOpts.getCheckShaderGLErrors(false); + demoMode = EaglercraftVersion.forceDemoMode || eaglercraftXOpts.getDemoMode(false); + isAllowUpdateSvc = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getAllowUpdateSvc(true); + isAllowUpdateDL = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getAllowUpdateDL(true); + isEnableDownloadOfflineButton = eaglercraftXOpts.getEnableDownloadOfflineButton(true); + downloadOfflineButtonLink = eaglercraftXOpts.getDownloadOfflineButtonLink(null); + useSpecialCursors = eaglercraftXOpts.getHtml5CursorSupport(false); + logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getLogInvalidCerts(false); + enableSignatureBadge = eaglercraftXOpts.getEnableSignatureBadge(false); + allowVoiceClient = eaglercraftXOpts.getAllowVoiceClient(true); + allowFNAWSkins = !demoMode && eaglercraftXOpts.getAllowFNAWSkins(true); + localStorageNamespace = eaglercraftXOpts.getLocalStorageNamespace(EaglercraftVersion.localStorageNamespace); + enableMinceraft = eaglercraftXOpts.getEnableMinceraft(true); + enableServerCookies = !demoMode && eaglercraftXOpts.getEnableServerCookies(true); + allowServerRedirects = eaglercraftXOpts.getAllowServerRedirects(true); + crashOnUncaughtExceptions = eaglercraftXOpts.getCrashOnUncaughtExceptions(false); + openDebugConsoleOnLaunch = eaglercraftXOpts.getOpenDebugConsoleOnLaunch(false); + fixDebugConsoleUnloadListener = eaglercraftXOpts.getFixDebugConsoleUnloadListener(false); + forceWebViewSupport = eaglercraftXOpts.getForceWebViewSupport(false); + enableWebViewCSP = eaglercraftXOpts.getEnableWebViewCSP(true); + autoFixLegacyStyleAttr = eaglercraftXOpts.getAutoFixLegacyStyleAttr(true); + showBootMenuOnLaunch = eaglercraftXOpts.getShowBootMenuOnLaunch(false); + bootMenuBlocksUnsignedClients = eaglercraftXOpts.getBootMenuBlocksUnsignedClients(false); + allowBootMenu = eaglercraftXOpts.getAllowBootMenu(!demoMode); + forceProfanityFilter = eaglercraftXOpts.getForceProfanityFilter(false); + forceWebGL1 = eaglercraftXOpts.getForceWebGL1(false); + forceWebGL2 = eaglercraftXOpts.getForceWebGL2(false); + allowExperimentalWebGL1 = eaglercraftXOpts.getAllowExperimentalWebGL1(true); + useWebGLExt = eaglercraftXOpts.getUseWebGLExt(true); + useDelayOnSwap = eaglercraftXOpts.getUseDelayOnSwap(false); + useJOrbisAudioDecoder = eaglercraftXOpts.getUseJOrbisAudioDecoder(false); + useXHRFetch = eaglercraftXOpts.getUseXHRFetch(false); + useVisualViewport = eaglercraftXOpts.getUseVisualViewport(true); + deobfStackTraces = eaglercraftXOpts.getDeobfStackTraces(true); + disableBlobURLs = eaglercraftXOpts.getDisableBlobURLs(false); + eaglerNoDelay = eaglercraftXOpts.getEaglerNoDelay(false); + ramdiskMode = eaglercraftXOpts.getRamdiskMode(false); + singleThreadMode = eaglercraftXOpts.getSingleThreadMode(false); + JSEaglercraftXOptsHooks hooksObj = eaglercraftXOpts.getHooks(); + if(hooksObj != null) { + hooks.loadHooks(hooksObj); + } + + integratedServerOpts.put("worldsDB", worldsDB); + integratedServerOpts.put("demoMode", demoMode); + integratedServerOpts.put("lang", defaultLocale); + integratedServerOpts.put("allowUpdateSvc", isAllowUpdateSvc); + integratedServerOpts.put("allowUpdateDL", isAllowUpdateDL); + integratedServerOpts.put("allowVoiceClient", allowVoiceClient); + integratedServerOpts.put("allowFNAWSkins", allowFNAWSkins); + integratedServerOpts.put("crashOnUncaughtExceptions", crashOnUncaughtExceptions); + integratedServerOpts.put("deobfStackTraces", deobfStackTraces); + integratedServerOpts.put("disableBlobURLs", disableBlobURLs); + integratedServerOpts.put("eaglerNoDelay", eaglerNoDelay); + integratedServerOpts.put("ramdiskMode", ramdiskMode); + integratedServerOpts.put("singleThreadMode", singleThreadMode); + + defaultServers.clear(); + JSArrayReader serversArray = eaglercraftXOpts.getServers(); + if(serversArray != null) { + for(int i = 0, l = serversArray.getLength(); i < l; ++i) { + JSEaglercraftXOptsServer serverEntry = serversArray.get(i); + boolean hideAddr = serverEntry.getHideAddr(false); + String serverAddr = serverEntry.getAddr(); + if(serverAddr != null) { + String serverName = serverEntry.getName("Default Server #" + i); + defaultServers.add(new DefaultServer(serverName, serverAddr, hideAddr)); + } + } + } + + relays.clear(); + JSArrayReader relaysArray = eaglercraftXOpts.getRelays(); + if(relaysArray != null) { + boolean gotAPrimary = false; + for(int i = 0, l = relaysArray.getLength(); i < l; ++i) { + JSEaglercraftXOptsRelay relay = relaysArray.get(i); + String addr = relay.getAddr(); + if(addr != null) { + boolean p = relay.getPrimary(); + if(p) { + if(gotAPrimary) { + p = false; + }else { + gotAPrimary = true; + } + } + relays.add(new RelayEntry(relay.getAddr(), relay.getComment("Default Relay #" + i), p)); + } + } + } + + boolean officialUpdates = !demoMode && EaglercraftVersion.updateBundlePackageName.equals("net.lax1dude.eaglercraft.v1_8.client"); + if (relays.size() <= 0) { + int choice = ThreadLocalRandom.current().nextInt(3); + relays.add(new RelayEntry("wss://relay.deev.is/", "lax1dude relay #1", choice == 0)); + relays.add(new RelayEntry("wss://relay.lax1dude.net/", "lax1dude relay #2", choice == 1)); + relays.add(new RelayEntry("wss://relay.shhnowisnottheti.me/", "ayunami relay #1", choice == 2)); + checkRelaysForUpdates = !demoMode && eaglercraftXOpts.getCheckRelaysForUpdates(officialUpdates); + }else { + if(officialUpdates) { + for(int i = 0, l = relays.size(); i < l; ++i) { + String addr = relays.get(i).address; + if(!addr.contains("deev.is") && !addr.contains("lax1dude.net") && !addr.contains("shhnowisnottheti.me")) { + officialUpdates = false; + break; + } + } + } + checkRelaysForUpdates = !demoMode && eaglercraftXOpts.getCheckRelaysForUpdates(officialUpdates); + } + + RelayManager.relayManager.load(EagRuntime.getStorage("r")); + + if (RelayManager.relayManager.count() <= 0) { + RelayManager.relayManager.loadDefaults(); + RelayManager.relayManager.save(); + } + } + + public void loadJSON(JSONObject eaglercraftOpts) { + integratedServerOpts = eaglercraftOpts; + defaultLocale = eaglercraftOpts.optString("lang", "en_US"); + serverToJoin = eaglercraftOpts.optString("joinServer", null); + worldsDB = eaglercraftOpts.optString("worldsDB", "worlds"); + resourcePacksDB = eaglercraftOpts.optString("resourcePacksDB", "resourcePacks"); + checkShaderGLErrors = eaglercraftOpts.optBoolean("checkShaderGLErrors", false); + if(EaglercraftVersion.forceDemoMode) { + eaglercraftOpts.put("demoMode", true); + } + demoMode = EaglercraftVersion.forceDemoMode || eaglercraftOpts.optBoolean("demoMode", false); + isAllowUpdateSvc = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftOpts.optBoolean("allowUpdateSvc", true); + isAllowUpdateDL = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftOpts.optBoolean("allowUpdateDL", true); + isEnableDownloadOfflineButton = eaglercraftOpts.optBoolean("enableDownloadOfflineButton", true); + downloadOfflineButtonLink = eaglercraftOpts.optString("downloadOfflineButtonLink", null); + useSpecialCursors = eaglercraftOpts.optBoolean("html5CursorSupport", false); + logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftOpts.optBoolean("logInvalidCerts", false); + enableSignatureBadge = eaglercraftOpts.optBoolean("enableSignatureBadge", false); + allowVoiceClient = eaglercraftOpts.optBoolean("allowVoiceClient", true); + allowFNAWSkins = eaglercraftOpts.optBoolean("allowFNAWSkins", true); + localStorageNamespace = eaglercraftOpts.optString("localStorageNamespace", EaglercraftVersion.localStorageNamespace); + enableMinceraft = eaglercraftOpts.optBoolean("enableMinceraft", true); + enableServerCookies = !demoMode && eaglercraftOpts.optBoolean("enableServerCookies", true); + allowServerRedirects = eaglercraftOpts.optBoolean("allowServerRedirects", true); + crashOnUncaughtExceptions = eaglercraftOpts.optBoolean("crashOnUncaughtExceptions", false); + openDebugConsoleOnLaunch = eaglercraftOpts.optBoolean("openDebugConsoleOnLaunch", false); + fixDebugConsoleUnloadListener = eaglercraftOpts.optBoolean("fixDebugConsoleUnloadListener", false); + forceWebViewSupport = eaglercraftOpts.optBoolean("forceWebViewSupport", false); + enableWebViewCSP = eaglercraftOpts.optBoolean("enableWebViewCSP", true); + autoFixLegacyStyleAttr = eaglercraftOpts.optBoolean("autoFixLegacyStyleAttr", true); + showBootMenuOnLaunch = eaglercraftOpts.optBoolean("showBootMenuOnLaunch", false); + bootMenuBlocksUnsignedClients = eaglercraftOpts.optBoolean("bootMenuBlocksUnsignedClients", false); + allowBootMenu = eaglercraftOpts.optBoolean("allowBootMenu", !demoMode); + forceProfanityFilter = eaglercraftOpts.optBoolean("forceProfanityFilter", false); + forceWebGL1 = eaglercraftOpts.optBoolean("forceWebGL1", false); + forceWebGL2 = eaglercraftOpts.optBoolean("forceWebGL2", false); + allowExperimentalWebGL1 = eaglercraftOpts.optBoolean("allowExperimentalWebGL1", true); + useWebGLExt = eaglercraftOpts.optBoolean("useWebGLExt", true); + useDelayOnSwap = eaglercraftOpts.optBoolean("useDelayOnSwap", false); + useJOrbisAudioDecoder = eaglercraftOpts.optBoolean("useJOrbisAudioDecoder", false); + useXHRFetch = eaglercraftOpts.optBoolean("useXHRFetch", false); + useVisualViewport = eaglercraftOpts.optBoolean("useVisualViewport", true); + deobfStackTraces = eaglercraftOpts.optBoolean("deobfStackTraces", true); + disableBlobURLs = eaglercraftOpts.optBoolean("disableBlobURLs", false); + eaglerNoDelay = eaglercraftOpts.optBoolean("eaglerNoDelay", false); + ramdiskMode = eaglercraftOpts.optBoolean("ramdiskMode", false); + singleThreadMode = eaglercraftOpts.optBoolean("singleThreadMode", false); + defaultServers.clear(); + JSONArray serversArray = eaglercraftOpts.optJSONArray("servers"); + if(serversArray != null) { + for(int i = 0, l = serversArray.length(); i < l; ++i) { + JSONObject serverEntry = serversArray.getJSONObject(i); + boolean hideAddr = serverEntry.optBoolean("hideAddr", false); + String serverAddr = serverEntry.optString("addr", null); + if(serverAddr != null) { + String serverName = serverEntry.optString("name", "Default Server #" + i); + defaultServers.add(new DefaultServer(serverName, serverAddr, hideAddr)); + } + } + } + + relays.clear(); + JSONArray relaysArray = eaglercraftOpts.optJSONArray("relays"); + if(relaysArray != null) { + boolean gotAPrimary = false; + for (int i = 0, l = relaysArray.length(); i < l; ++i) { + JSONObject relay = relaysArray.getJSONObject(i); + boolean p = relay.optBoolean("primary"); + if(p) { + if(gotAPrimary) { + p = false; + }else { + gotAPrimary = true; + } + } + relays.add(new RelayEntry(relay.getString("addr"), relay.getString("comment"), p)); + } + } + + boolean officialUpdates = !demoMode && EaglercraftVersion.updateBundlePackageName.equals("net.lax1dude.eaglercraft.v1_8.client"); + if (relays.size() <= 0) { + int choice = ThreadLocalRandom.current().nextInt(3); + relays.add(new RelayEntry("wss://relay.deev.is/", "lax1dude relay #1", choice == 0)); + relays.add(new RelayEntry("wss://relay.lax1dude.net/", "lax1dude relay #2", choice == 1)); + relays.add(new RelayEntry("wss://relay.shhnowisnottheti.me/", "ayunami relay #1", choice == 2)); + checkRelaysForUpdates = !demoMode && eaglercraftOpts.optBoolean("checkRelaysForUpdates", officialUpdates); + }else { + if(officialUpdates) { + for(int i = 0, l = relays.size(); i < l; ++i) { + String addr = relays.get(i).address; + if(!addr.contains("deev.is") && !addr.contains("lax1dude.net") && !addr.contains("shhnowisnottheti.me")) { + officialUpdates = false; + break; + } + } + } + checkRelaysForUpdates = !demoMode && eaglercraftOpts.optBoolean("checkRelaysForUpdates", officialUpdates); + } + + RelayManager.relayManager.load(EagRuntime.getStorage("r")); + + if (RelayManager.relayManager.count() <= 0) { + RelayManager.relayManager.loadDefaults(); + RelayManager.relayManager.save(); + } + } + + @Override + public String getDefaultLocale() { + return defaultLocale; + } + + @Override + public List getDefaultServerList() { + return defaultServers; + } + + @Override + public String getServerToJoin() { + return serverToJoin; + } + + @Override + public String getWorldsDB() { + return worldsDB; + } + + @Override + public String getResourcePacksDB() { + return resourcePacksDB; + } + + @Override + public JSONObject getIntegratedServerOpts() { + return integratedServerOpts; + } + + @Override + public List getRelays() { + return relays; + } + + @Override + public boolean isCheckShaderGLErrors() { + return checkShaderGLErrors; + } + + @Override + public boolean isDemo() { + return demoMode; + } + + @Override + public boolean allowUpdateSvc() { + return isAllowUpdateSvc; + } + + @Override + public boolean allowUpdateDL() { + return isAllowUpdateDL; + } + + @Override + public boolean isEnableDownloadOfflineButton() { + return isEnableDownloadOfflineButton; + } + + @Override + public String getDownloadOfflineButtonLink() { + return downloadOfflineButtonLink; + } + + @Override + public boolean useSpecialCursors() { + return useSpecialCursors; + } + + @Override + public boolean isLogInvalidCerts() { + return logInvalidCerts; + } + + @Override + public boolean isCheckRelaysForUpdates() { + return checkRelaysForUpdates; + } + + @Override + public boolean isEnableSignatureBadge() { + return enableSignatureBadge; + } + + @Override + public boolean isAllowVoiceClient() { + return allowVoiceClient; + } + + @Override + public boolean isAllowFNAWSkins() { + return allowFNAWSkins; + } + + @Override + public String getLocalStorageNamespace() { + return localStorageNamespace; + } + + @Override + public boolean isEnableMinceraft() { + return enableMinceraft; + } + + @Override + public boolean isEnableServerCookies() { + return enableServerCookies; + } + + @Override + public boolean isAllowServerRedirects() { + return allowServerRedirects; + } + + @Override + public boolean isOpenDebugConsoleOnLaunch() { + return openDebugConsoleOnLaunch; + } + + public boolean isFixDebugConsoleUnloadListenerTeaVM() { + return fixDebugConsoleUnloadListener; + } + + @Override + public boolean isForceWebViewSupport() { + return forceWebViewSupport; + } + + @Override + public boolean isEnableWebViewCSP() { + return enableWebViewCSP; + } + + public boolean isAutoFixLegacyStyleAttrTeaVM() { + return autoFixLegacyStyleAttr; + } + + public boolean isForceWebGL1TeaVM() { + return forceWebGL1; + } + + public boolean isForceWebGL2TeaVM() { + return forceWebGL2; + } + + public boolean isAllowExperimentalWebGL1TeaVM() { + return allowExperimentalWebGL1; + } + + public boolean isUseWebGLExtTeaVM() { + return useWebGLExt; + } + + public boolean isUseDelayOnSwapTeaVM() { + return useDelayOnSwap; + } + + public boolean isUseJOrbisAudioDecoderTeaVM() { + return useJOrbisAudioDecoder; + } + + public boolean isUseXHRFetchTeaVM() { + return useXHRFetch; + } + + public boolean isDeobfStackTracesTeaVM() { + return deobfStackTraces; + } + + public boolean isUseVisualViewportTeaVM() { + return useVisualViewport; + } + + public boolean isDisableBlobURLsTeaVM() { + return disableBlobURLs; + } + + public boolean isSingleThreadModeTeaVM() { + return singleThreadMode; + } + + @Override + public boolean isShowBootMenuOnLaunch() { + return showBootMenuOnLaunch; + } + + @Override + public boolean isBootMenuBlocksUnsignedClients() { + return bootMenuBlocksUnsignedClients; + } + + @Override + public boolean isAllowBootMenu() { + return allowBootMenu; + } + + @Override + public boolean isForceProfanityFilter() { + return forceProfanityFilter; + } + + @Override + public boolean isEaglerNoDelay() { + return eaglerNoDelay; + } + + @Override + public boolean isRamdiskMode() { + return ramdiskMode; + } + + @Override + public IClientConfigAdapterHooks getHooks() { + return hooks; + } + + public JSONObject toJSONObject() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("lang", defaultLocale); + jsonObject.put("joinServer", serverToJoin); + jsonObject.put("worldsDB", worldsDB); + jsonObject.put("resourcePacksDB", resourcePacksDB); + jsonObject.put("checkShaderGLErrors", checkShaderGLErrors); + jsonObject.put("demoMode", demoMode); + jsonObject.put("allowUpdateSvc", isAllowUpdateSvc); + jsonObject.put("allowUpdateDL", isAllowUpdateDL); + jsonObject.put("enableDownloadOfflineButton", isEnableDownloadOfflineButton); + jsonObject.put("downloadOfflineButtonLink", downloadOfflineButtonLink); + jsonObject.put("html5CursorSupport", useSpecialCursors); + jsonObject.put("logInvalidCerts", logInvalidCerts); + jsonObject.put("checkRelaysForUpdates", checkRelaysForUpdates); + jsonObject.put("enableSignatureBadge", enableSignatureBadge); + jsonObject.put("allowVoiceClient", allowVoiceClient); + jsonObject.put("allowFNAWSkins", allowFNAWSkins); + jsonObject.put("localStorageNamespace", localStorageNamespace); + jsonObject.put("enableMinceraft", enableMinceraft); + jsonObject.put("enableServerCookies", enableServerCookies); + jsonObject.put("allowServerRedirects", allowServerRedirects); + jsonObject.put("crashOnUncaughtExceptions", crashOnUncaughtExceptions); + jsonObject.put("openDebugConsoleOnLaunch", openDebugConsoleOnLaunch); + jsonObject.put("fixDebugConsoleUnloadListener", fixDebugConsoleUnloadListener); + jsonObject.put("forceWebViewSupport", forceWebViewSupport); + jsonObject.put("enableWebViewCSP", enableWebViewCSP); + jsonObject.put("autoFixLegacyStyleAttr", autoFixLegacyStyleAttr); + jsonObject.put("showBootMenuOnLaunch", showBootMenuOnLaunch); + jsonObject.put("bootMenuBlocksUnsignedClients", bootMenuBlocksUnsignedClients); + jsonObject.put("allowBootMenu", allowBootMenu); + jsonObject.put("forceProfanityFilter", forceProfanityFilter); + jsonObject.put("forceWebGL1", forceWebGL1); + jsonObject.put("forceWebGL2", forceWebGL2); + jsonObject.put("allowExperimentalWebGL1", allowExperimentalWebGL1); + jsonObject.put("useWebGLExt", useWebGLExt); + jsonObject.put("useDelayOnSwap", useDelayOnSwap); + jsonObject.put("useJOrbisAudioDecoder", useJOrbisAudioDecoder); + jsonObject.put("useXHRFetch", useXHRFetch); + jsonObject.put("useVisualViewport", useVisualViewport); + jsonObject.put("deobfStackTraces", deobfStackTraces); + jsonObject.put("disableBlobURLs", disableBlobURLs); + jsonObject.put("eaglerNoDelay", eaglerNoDelay); + jsonObject.put("ramdiskMode", ramdiskMode); + jsonObject.put("singleThreadMode", singleThreadMode); + JSONArray serversArr = new JSONArray(); + for(int i = 0, l = defaultServers.size(); i < l; ++i) { + DefaultServer srv = defaultServers.get(i); + JSONObject obj = new JSONObject(); + obj.put("addr", srv.addr); + obj.put("hideAddr", srv.hideAddress); + obj.put("name", srv.name); + serversArr.put(obj); + } + jsonObject.put("servers", serversArr); + JSONArray relaysArr = new JSONArray(); + for(int i = 0, l = relays.size(); i < l; ++i) { + RelayEntry rl = relays.get(i); + JSONObject obj = new JSONObject(); + obj.put("addr", rl.address); + obj.put("comment", rl.comment); + obj.put("primary", rl.primary); + relaysArr.put(obj); + } + jsonObject.put("relays", relaysArr); + return jsonObject; + } + + @Override + public String toString() { + return toJSONObject().toString(); + } + + public String toStringFormatted() { + return toJSONObject().toString(4); + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java index 61be6e6..9fc9411 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java @@ -43,6 +43,7 @@ public class TeaVMClientConfigAdapterHooks implements IClientConfigAdapterHooks private LocalStorageSaveHook saveHook = null; private LocalStorageLoadHook loadHook = null; private CrashReportHook crashHook = null; + private ScreenChangeHook screenChangedHook = null; @JSFunctor private static interface LocalStorageSaveHook extends JSObject { @@ -74,6 +75,22 @@ public class TeaVMClientConfigAdapterHooks implements IClientConfigAdapterHooks } } + @JSFunctor + private static interface ScreenChangeHook extends JSObject { + String call(String screenName, int scaledWidth, int scaledHeight, int realWidth, int realHeight, + int scaleFactor); + } + + @Override + public void callScreenChangedHook(String screenName, int scaledWidth, int scaledHeight, int realWidth, + int realHeight, int scaleFactor) { + if(screenChangedHook != null) { + callHookSafe("screenChanged", () -> { + screenChangedHook.call(screenName, scaledWidth, scaledHeight, realWidth, realHeight, scaleFactor); + }); + } + } + @JSFunctor private static interface CrashReportHook extends JSObject { void call(String crashReport, CustomMessageCB customMessageCB); @@ -142,5 +159,7 @@ public class TeaVMClientConfigAdapterHooks implements IClientConfigAdapterHooks saveHook = (LocalStorageSaveHook)hooks.getLocalStorageSavedHook(); loadHook = (LocalStorageLoadHook)hooks.getLocalStorageLoadedHook(); crashHook = (CrashReportHook)hooks.getCrashReportHook(); + screenChangedHook = (ScreenChangeHook)hooks.getScreenChangedHook(); } + } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMDataURLManager.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMDataURLManager.java new file mode 100755 index 0000000..a7de655 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMDataURLManager.java @@ -0,0 +1,87 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.browser.Window; + +import net.lax1dude.eaglercraft.v1_8.Base64; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMDataURLManager { + + private static void checkDataURLSupport0(boolean fetchBased, final AsyncCallback callback) { + final byte[] testData = new byte[1024]; + for(int i = 0; i < 1024; ++i) { + testData[i] = (byte)i; + } + String testURL = "data:application/octet-stream;base64," + Base64.encodeBase64String(testData); + TeaVMFetchJS.FetchHandler cb = (data) -> { + if(data != null && TeaVMUtils.isTruthy(data) && data.getByteLength() == 1024) { + byte[] bb = TeaVMUtils.wrapByteArrayBuffer(data); + callback.complete(Arrays.equals(bb, testData)); + }else { + callback.complete(false); + } + }; + try { + if(fetchBased) { + TeaVMFetchJS.doFetchDownload(testURL, "force-cache", cb); + }else { + TeaVMFetchJS.doXHRDownload(testURL, cb); + } + }catch(Throwable t) { + callback.complete(false); + } + } + + @Async + private static native Boolean checkDataURLSupport0(boolean fetchBased); + + public static boolean checkDataURLSupport(boolean fetchBased) { + Boolean b = null; + try { + b = checkDataURLSupport0(fetchBased); + }catch(Throwable t) { + } + return b != null && b.booleanValue(); + } + + public static byte[] decodeDataURLFallback(String dataURL) { + if(dataURL.length() < 6 || !dataURL.substring(0, 5).equalsIgnoreCase("data:")) { + return null; + } + int i = dataURL.indexOf(','); + if(i == -1 || i >= dataURL.length() - 1) { + return null; + } + String mime = dataURL.substring(0, i).toLowerCase(); + String str = dataURL.substring(i + 1); + try { + if(mime.endsWith(";base64")) { + return Base64.decodeBase64(str); + }else { + return Window.decodeURIComponent(str).getBytes(StandardCharsets.UTF_8); + } + }catch(Throwable t) { + return null; + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMEnterBootMenuException.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMEnterBootMenuException.java new file mode 100755 index 0000000..7a16a0a --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMEnterBootMenuException.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMEnterBootMenuException extends RuntimeException { + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMFetchJS.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMFetchJS.java new file mode 100755 index 0000000..fcfadb9 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMFetchJS.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.typedarrays.ArrayBuffer; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMFetchJS { + + @JSFunctor + public static interface FetchHandler extends JSObject { + void onFetch(ArrayBuffer data); + } + + @JSBody(params = { }, script = "return (typeof fetch === \"function\");") + public static native boolean checkFetchSupport(); + + @JSBody(params = { "uri", "forceCache", "callback" }, script = "fetch(uri, { cache: forceCache, mode: \"no-cors\" })" + + ".then(function(res) { return res.arrayBuffer(); }).then(function(arr) { callback(arr); })" + + ".catch(function(err) { console.error(err); callback(null); });") + public static native void doFetchDownload(String uri, String forceCache, FetchHandler callback); + + @JSBody(params = { "uri", "callback" }, script = "var eag = function(xhrObj){xhrObj.responseType = \"arraybuffer\";" + + "xhrObj.addEventListener(\"load\", function(evt) { var stat = xhrObj.status; if(stat === 0 || (stat >= 200 && stat < 400)) { callback(xhrObj.response); } else { callback(null); } });" + + "xhrObj.addEventListener(\"error\", function(evt) { callback(null); });" + + "xhrObj.open(\"GET\", uri, true); xhrObj.send();}; eag(new XMLHttpRequest());") + public static native void doXHRDownload(String uri, FetchHandler callback); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMRuntimeDeobfuscator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMRuntimeDeobfuscator.java new file mode 100755 index 0000000..04bdf82 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMRuntimeDeobfuscator.java @@ -0,0 +1,242 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.teavm.backend.javascript.spi.GeneratedBy; +import org.teavm.jso.JSObject; +import org.teavm.jso.core.JSArrayReader; +import org.teavm.jso.core.JSString; + +import com.google.common.collect.Lists; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.generators.TeaVMRuntimeDeobfuscatorGenerator; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMRuntimeDeobfuscator { + + private static final Logger logger = LogManager.getLogger("TeaVMRuntimeDeobfuscator"); + + private static class DeobfNameEntry { + + private final String className; + private final String functionName; + + private DeobfNameEntry(String className, String functionName) { + this.className = className; + this.functionName = functionName; + } + + } + + private static final Object initLock = new Object(); + + private static final Map deobfClassNames = new HashMap<>(); + private static final Map deobfFuncNames = new HashMap<>(); + + private static boolean isInitialized = false; + private static boolean isFailed = false; + + @GeneratedBy(TeaVMRuntimeDeobfuscatorGenerator.class) + private static native JSArrayReader getAllClasses(); + + private static void initialize0() { + try { + logger.info("Loading deobfuscation data, please wait..."); + }catch(Throwable t2) { + } + long time = PlatformRuntime.steadyTimeMillis(); + JSArrayReader classes = getAllClasses(); + if(classes.getLength() < 2) { + return; + } + deobfClassNames.clear(); + deobfFuncNames.clear(); + JSArrayReader stringReaderA = (JSArrayReader)classes.get(0); + JSArrayReader stringReaderB = (JSArrayReader)classes.get(1); + String[] javaStringPoolA = new String[stringReaderA.getLength()]; + for(int i = 0; i < javaStringPoolA.length; ++i) { + javaStringPoolA[i] = stringReaderA.get(i).stringValue(); + } + String[] javaStringPoolB = new String[stringReaderB.getLength()]; + for(int i = 0; i < javaStringPoolB.length; ++i) { + javaStringPoolB[i] = stringReaderB.get(i).stringValue(); + } + for(int i = 2, l = classes.getLength() - 2; i < l; i += 3) { + int[] lookupTblClsName = Base64VarIntArray.decodeVarIntArray((JSString)classes.get(i)); + StringBuilder classNameBuilder = new StringBuilder(); + boolean b = false; + for(int j = 0; j < lookupTblClsName.length; ++j) { + if(b) { + classNameBuilder.append('.'); + } + classNameBuilder.append(javaStringPoolA[lookupTblClsName[j]]); + b = true; + } + String className = classNameBuilder.toString(); + String classObfName = ((JSString)classes.get(i + 1)).stringValue(); + deobfClassNames.put(classObfName, className); + int[] lookupTbl = Base64VarIntArray.decodeVarIntArray((JSString)classes.get(i + 2)); + for(int j = 0, m = lookupTbl.length - 1; j < m; j += 2) { + String obfName = javaStringPoolB[lookupTbl[j]]; + String deobfName = javaStringPoolB[lookupTbl[j + 1]]; + deobfFuncNames.put(obfName, new DeobfNameEntry(className, deobfName)); + } + } + try { + time = PlatformRuntime.steadyTimeMillis() - time; + logger.info("Indexed {} class names and {} function names after {}ms", deobfClassNames.size(), deobfFuncNames.size(), time); + }catch(Throwable t2) { + } + } + + public static void initialize() { + if(!isFailed) { + synchronized(initLock) { + if(!isInitialized) { + try { + initialize0(); + isInitialized = true; + }catch(Throwable t) { + isFailed = true; + try { + logger.error("Failed to initialize the tables!"); + logger.error(t); + }catch(Throwable t2) { + } + } + } + } + } + } + + public static String deobfClassName(String clsName) { + if(!isInitialized) return null; + return deobfClassNames.get(clsName); + } + + public static String deobfFunctionName(String funcName) { + if(!isInitialized) return null; + DeobfNameEntry ret = deobfFuncNames.get(funcName); + return ret != null ? ret.functionName : null; + } + + public static String deobfFunctionClass(String funcName) { + if(!isInitialized) return null; + DeobfNameEntry ret = deobfFuncNames.get(funcName); + return ret != null ? ret.className : null; + } + + public static String deobfFunctionFullName(String funcName) { + if(!isInitialized) return null; + DeobfNameEntry ret = deobfFuncNames.get(funcName); + return ret != null ? (ret.className != null ? ret.className : "") + "." + ret.functionName + "()" : null; + } + + public static String deobfFullName(String funcName) { + if(!isInitialized) return null; + DeobfNameEntry ret = deobfFuncNames.get(funcName); + return ret != null ? (ret.className != null ? ret.className : "") + "." + ret.functionName + "()" : deobfClassNames.get(funcName); + } + + private static int countLeadingWhitespace(String line) { + for(int i = 0, l = line.length(); i < l; ++i) { + char c = line.charAt(i); + if(c != ' ' && c != '\t') { + return i; + } + } + return 0; + } + + public static String deobfExceptionStack(String stackLines) { + if(!isInitialized) return stackLines; + try { + List lines = Lists.newArrayList(EagUtils.splitPattern.split(stackLines)); + deobfExceptionStack(lines); + return String.join("\n", lines); + }catch(Throwable t) { + try { + logger.error("Failed to deobfuscate stack trace!"); + }catch(Throwable t2) { + } + return stackLines; + } + } + + public static void deobfExceptionStack(List stackLines) { + if(!isInitialized) return; + try { + for(int i = 0, l = stackLines.size(); i < l; ++i) { + String line = stackLines.get(i); + int len = line.length(); + if(len == 0) continue; + int leadingWs = countLeadingWhitespace(line); + if(len > leadingWs + 3 && line.charAt(leadingWs) == 'a' && line.charAt(leadingWs + 1) == 't' && line.charAt(leadingWs + 2) == ' ') { + leadingWs += 3; + } + int nextSpace = line.indexOf(' ', leadingWs); + int nextDot = line.indexOf('.', leadingWs); + String funcName2 = null; + if(nextDot > 0 && nextDot < nextSpace) { + funcName2 = line.substring(nextDot + 1, nextSpace); + nextSpace = nextDot; + } + if(nextSpace == -1) { + nextSpace = line.indexOf('@', leadingWs); + if(nextSpace == -1 && nextSpace < leadingWs) { + if(nextSpace == leadingWs + 1 && line.charAt(leadingWs) == '@') { + continue; + } + nextSpace = len; + } + } + if(nextSpace - leadingWs < 1) { + continue; + } + String funcName = line.substring(leadingWs, nextSpace); + String deobfName = deobfFunctionFullName(funcName); + if(deobfName != null) { + stackLines.set(i, line.substring(0, leadingWs) + deobfName + line.substring(nextSpace)); + }else { + deobfName = deobfClassName(funcName); + if(deobfName != null) { + DeobfNameEntry deobfName2 = null; + if(funcName2 != null && funcName2.indexOf('.') == -1) { + deobfName2 = deobfFuncNames.get(funcName2); + } + if(deobfName2 != null && deobfName.equals(deobfName2.className)) { + deobfName += "." + deobfName2.functionName + "()"; + } + stackLines.set(i, line.substring(0, leadingWs) + deobfName + line.substring(nextSpace)); + } + } + } + }catch(Throwable t) { + try { + logger.error("Failed to deobfuscate stack trace!"); + }catch(Throwable t2) { + } + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUpdateThread.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUpdateThread.java index 6f51e9b..772eb07 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUpdateThread.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUpdateThread.java @@ -20,7 +20,6 @@ import org.teavm.jso.typedarrays.ArrayBuffer; import com.google.common.collect.ListMultimap; import net.lax1dude.eaglercraft.v1_8.Base64; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets; @@ -28,27 +27,22 @@ import net.lax1dude.eaglercraft.v1_8.internal.PlatformUpdateSvc; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate; +import net.lax1dude.eaglercraft.v1_8.update.UpdateDataObj; import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct; +import net.lax1dude.eaglercraft.v1_8.update.UpdateResultObj; import net.lax1dude.eaglercraft.v1_8.update.UpdateService; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -68,6 +62,7 @@ public class TeaVMUpdateThread implements Runnable { @Override public void run() { boolean success = false; + boolean hasCompleted = false; try { logger.info("Starting update thread..."); updateProg.clear(); @@ -75,73 +70,73 @@ public class TeaVMUpdateThread implements Runnable { updateProg.statusString1 = updateCert.bundleDisplayName + " - " + updateCert.bundleDisplayVersion; updateProg.statusString2 = "Please Wait"; - List urlListA = new ArrayList(); - ListMultimap downloadSources = updateCert.getSourceMultimap(); + List urlListA = new ArrayList<>(); + ListMultimap downloadSources = updateCert.getSourceMultimap(); List ls = downloadSources.get("list"); - for (int k = 0, l = ls.size(); k < l; ++k) { + for(int k = 0, l = ls.size(); k < l; ++k) { String str1 = ls.get(k); updateProg.statusString2 = "Fetch List (" + (k + 1) + "/" + l + ")"; byte[] b = downloadWithProgress(str1); - if (b == null) { + if(b == null) { logger.error("Failed to load additional url list: {}", str1); continue; } try { String[] str2 = EagUtils.linesArray(new String(b, StandardCharsets.UTF_8)); - for (int i = 0; i < str2.length; ++i) { - if (!StringUtils.isAllBlank(str2[i]) && (str2[i] = str2[i].trim()).charAt(0) != '#') { + for(int i = 0; i < str2.length; ++i) { + if(!StringUtils.isAllBlank(str2[i]) && (str2[i] = str2[i].trim()).charAt(0) != '#') { String[] strrr = str2[i].split(":", 2); downloadSources.put(strrr[0].trim(), strrr[1].trim()); } } - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Failed to load/parse url list: {}", str1); logger.error(t); } } - + updateProg.statusString2 = "Please Wait"; - + urlListA.addAll(downloadSources.get("url")); - + List ls2 = downloadSources.get("use-gateway"); ls = downloadSources.get("ipfs"); - for (int k = 0, l = ls.size(); k < l; ++k) { + for(int k = 0, l = ls.size(); k < l; ++k) { String str1 = ls.get(k); String cid = str1; String path = ""; int pathSep = str1.indexOf('/'); - if (pathSep != -1) { + if(pathSep != -1) { path = cid.substring(pathSep + 1); cid = cid.substring(0, pathSep); } - for (int p = 0, q = ls2.size(); p < q; ++p) { + for(int p = 0, q = ls2.size(); p < q; ++p) { String str2 = ls2.get(p); urlListA.add(formatIPFSURL(cid, path, str2)); } } - - List urlListB = new ArrayList(); + + List urlListB = new ArrayList<>(); ls = downloadSources.get("use-proxy"); - for (int k = 0, l = ls.size(); k < l; ++k) { + for(int k = 0, l = ls.size(); k < l; ++k) { String str1 = ls.get(k); - for (int p = 0, q = urlListA.size(); p < q; ++p) { + for(int p = 0, q = urlListA.size(); p < q; ++p) { String str2 = urlListA.get(p); urlListB.add(formatProxyURL(str2, str1)); } } - + Collections.shuffle(urlListA); Collections.shuffle(urlListB); - + urlListA.addAll(urlListB); - - for (int i = 0, l = urlListA.size(); i < l; ++i) { + + for(int i = 0, l = urlListA.size(); i < l; ++i) { String url = urlListA.get(i); updateProg.statusString2 = "Attempt (" + (i + 1) + "/" + l + ")"; byte[] b = downloadWithProgress(url); - if (b == null) { + if(b == null) { updateProg.progressBar = 1.0f; updateProg.statusString3 = "FAILED!"; EagUtils.sleep(300l); @@ -152,9 +147,9 @@ public class TeaVMUpdateThread implements Runnable { updateProg.progressBar = 1.0f; updateProg.statusString2 = "Verifying"; logger.info("Verifying downloaded file..."); - if (updateCert.isBundleDataValid(b)) { + if(updateCert.isBundleDataValid(b)) { logger.info("Success! Signature is valid!"); - downloadSignedOffline(updateCert, b); + PlatformUpdateSvc.setUpdateResultTeaVM(UpdateResultObj.createSuccess(new UpdateDataObj(updateCert, b))); success = true; return; } @@ -162,22 +157,26 @@ public class TeaVMUpdateThread implements Runnable { logger.error("File signature is invalid: {}", url); EagUtils.sleep(1000l); } - + updateProg.progressBar = -1.0f; updateProg.statusString3 = null; - - } catch (Throwable t) { + + }catch(Throwable t) { logger.error("Uncaught exception downloading updates!"); logger.error(t); - } finally { + hasCompleted = true; + PlatformUpdateSvc.setUpdateResultTeaVM(UpdateResultObj.createFailure(t.toString())); + }finally { PlatformUpdateSvc.updateThread = null; updateProg.isBusy = false; - if (!success) { - logger.error("Failed to download updates! No valid URL was found for {}", - updateCert.bundleDisplayVersion); - Window.alert( - "ERROR: Failed to download updates!\n\nIf you are on a device with restricted internet access, try a different device or connect to a different WiFi network\n\nCheck the debug console for more info"); - } else { + if(!success) { + String str = "Failed to download updates! No valid URL was found for " + updateCert.bundleDisplayVersion; + logger.error(str); + if(!hasCompleted) { + PlatformUpdateSvc.setUpdateResultTeaVM(UpdateResultObj.createFailure(str)); + } + Window.alert("ERROR: Failed to download updates!\n\nIf you are on a device with restricted internet access, try a different device or connect to a different WiFi network\n\nCheck the debug console for more info"); + }else { UpdateService.dismiss(updateCert); } } @@ -189,11 +188,11 @@ public class TeaVMUpdateThread implements Runnable { updateProg.statusString3 = url; logger.info("Trying to download: {}", url); byte[] b = downloadWithProgress0(this, url); - if (b == null) { + if(b == null) { logger.error("Failed to download: {}", url); } return b; - } finally { + }finally { updateProg.statusString3 = null; } } @@ -204,7 +203,7 @@ public class TeaVMUpdateThread implements Runnable { private static void downloadWithProgress0(TeaVMUpdateThread self, String url, AsyncCallback cb) { try { self.downloadWithProgressImpl(url, cb); - } catch (Throwable t) { + }catch(Throwable t) { logger.error("Exception caught downloading file: {}", url); logger.error(t); cb.complete(null); @@ -218,25 +217,23 @@ public class TeaVMUpdateThread implements Runnable { TeaVMUtils.addEventListener(xhr, "progress", new EventListener() { @Override public void handleEvent(ProgressEvent evt) { - updateProg.progressBar = Math.min((float) evt.getLoaded() / (float) updateCert.bundleDataLength, 1.0f); + updateProg.progressBar = Math.min((float)evt.getLoaded() / (float)updateCert.bundleDataLength, 1.0f); } }); TeaVMUtils.addEventListener(xhr, "readystatechange", new EventListener() { @Override public void handleEvent(Event evt) { - if (xhr.getReadyState() == 4) { - if (xhr.getStatus() == 200) { - ArrayBuffer data = (ArrayBuffer) xhr.getResponse(); - if (data.getByteLength() == updateCert.bundleDataLength) { + if(xhr.getReadyState() == 4) { + if(xhr.getStatus() == 200) { + ArrayBuffer data = (ArrayBuffer)xhr.getResponse(); + if(data.getByteLength() == updateCert.bundleDataLength) { cb.complete(TeaVMUtils.wrapByteArrayBuffer(data)); - } else { - logger.error("Unexpected response length {} (expect: {}) from URL: {}", xhr.getStatus(), - xhr.getStatusText(), url); + }else { + logger.error("Unexpected response length {} (expect: {}) from URL: {}", xhr.getStatus(), xhr.getStatusText(), url); cb.complete(null); } - } else { - logger.error("Got response code {} \"{}\" for url: {}", xhr.getStatus(), xhr.getStatusText(), - url); + }else { + logger.error("Got response code {} \"{}\" for url: {}", xhr.getStatus(), xhr.getStatusText(), url); cb.complete(null); } } @@ -246,7 +243,7 @@ public class TeaVMUpdateThread implements Runnable { @Override public void handleEvent(ProgressEvent evt) { logger.error("Exception caught downloading file: {}", url); - + } }); xhr.send(); @@ -261,20 +258,16 @@ public class TeaVMUpdateThread implements Runnable { } public static void downloadSignedOffline(UpdateCertificate cert, byte[] data) { - PlatformApplication.downloadFileWithName( - cert.bundleDisplayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + "_" - + cert.bundleDisplayVersion.replaceAll("[^a-zA-Z0-9\\-_]", "_") + "_Offline_Signed.html", - generateSignedOffline(cert, data)); + PlatformApplication.downloadFileWithName(cert.bundleDisplayName.replaceAll("[^a-zA-Z0-9\\-_\\.]", "_") + "_" + cert.bundleDisplayVersion.replaceAll("[^a-zA-Z0-9\\-_]", "_") + "_Offline_Signed.html", generateSignedOffline(cert, data)); } public static byte[] generateSignedOffline(UpdateCertificate cert, byte[] data) { - return generateSignedOffline(cert.rawCertData, data, - EagRuntime.fixDateFormat(new SimpleDateFormat("MM/dd/yyyy")).format(new Date(cert.sigTimestamp))); + return generateSignedOffline(cert.rawCertData, data, (new SimpleDateFormat("MM/dd/yyyy")).format(new Date(cert.sigTimestamp))); } public static byte[] generateSignedOffline(byte[] cert, byte[] data, String date) { byte[] b = PlatformAssets.getResourceBytes("SignedClientTemplate.txt"); - if (b == null) { + if(b == null) { throw new RuntimeException("Could not load SignedClientTemplate.txt from assets.epk!"); } String templateHtml = new String(b, StandardCharsets.UTF_8); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java index 5922e2e..ad2c8d8 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java @@ -1,11 +1,18 @@ package net.lax1dude.eaglercraft.v1_8.internal.teavm; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.teavm.backend.javascript.spi.GeneratedBy; +import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.interop.Async; import org.teavm.interop.AsyncCallback; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; -import org.teavm.jso.JSProperty; import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.html.HTMLScriptElement; import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.ArrayBufferView; import org.teavm.jso.typedarrays.Float32Array; @@ -14,26 +21,19 @@ import org.teavm.jso.typedarrays.Int32Array; import org.teavm.jso.typedarrays.Int8Array; import org.teavm.jso.typedarrays.Uint8Array; -import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.generators.TeaVMUtilsUnwrapGenerator; /** * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -42,214 +42,96 @@ public class TeaVMUtils { @JSBody(params = { "url" }, script = "URL.revokeObjectURL(url);") public static native void freeDataURL(String url); - + @JSBody(params = { "buf", "mime" }, script = "return URL.createObjectURL(new Blob([buf], {type: mime}));") public static native String getDataURL(ArrayBuffer buf, String mime); - + + @JSBody(params = { "blob" }, script = "return URL.createObjectURL(blob);") + public static native String getDataURL(JSObject blob); + @JSBody(params = { "obj", "name", "handler" }, script = "obj.addEventListener(name, handler);") public static native void addEventListener(JSObject obj, String name, JSObject handler); - + @JSBody(params = {}, script = "return (new Error()).stack;") public static native String dumpJSStackTrace(); - private static abstract class TeaVMArrayObject implements JSObject { - @JSProperty - public abstract ArrayBufferView getData(); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native Int8Array unwrapByteArray(byte[] buf); - public static Int8Array unwrapByteArray(byte[] buf) { - if (buf == null) { - return null; - } - return Int8Array.create(((TeaVMArrayObject) (Object) buf).getData().getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class) + public static native ArrayBuffer unwrapArrayBuffer(byte[] buf); - public static ArrayBuffer unwrapArrayBuffer(byte[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData().getBuffer(); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native ArrayBufferView unwrapArrayBufferView(byte[] buf); - public static ArrayBufferView unwrapArrayBufferView(byte[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData(); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class) + public static native byte[] wrapByteArray(Int8Array buf); - @JSBody(params = { "buf" }, script = "return $rt_createByteArray(buf)") - private static native JSObject wrapByteArray0(JSObject buf); + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class) + public static native byte[] wrapByteArrayBuffer(ArrayBuffer buf); - public static byte[] wrapByteArray(Int8Array buf) { - if (buf == null) { - return null; - } - return (byte[]) (Object) wrapByteArray0(buf.getBuffer()); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class) + public static native byte[] wrapByteArrayBufferView(ArrayBufferView buf); - public static byte[] wrapByteArrayBuffer(ArrayBuffer buf) { - if (buf == null) { - return null; - } - return (byte[]) (Object) wrapByteArray0(buf); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapUnsignedTypedArray.class) + public static native Uint8Array unwrapUnsignedByteArray(byte[] buf); - public static byte[] wrapByteArrayBufferView(ArrayBufferView buf) { - if (buf == null) { - return null; - } - return (byte[]) (Object) wrapByteArray0(buf.getBuffer()); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class) + public static native byte[] wrapUnsignedByteArray(Uint8Array buf); - public static Uint8Array unwrapUnsignedByteArray(byte[] buf) { - if (buf == null) { - return null; - } - return Uint8Array.create(((TeaVMArrayObject) (Object) buf).getData().getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native Int32Array unwrapIntArray(int[] buf); - public static byte[] wrapUnsignedByteArray(Uint8Array buf) { - if (buf == null) { - return null; - } - return (byte[]) (Object) wrapByteArray0(buf.getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class) + public static native ArrayBuffer unwrapArrayBuffer(int[] buf); - public static Int32Array unwrapIntArray(int[] buf) { - if (buf == null) { - return null; - } - return Int32Array.create(((TeaVMArrayObject) (Object) buf).getData().getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native ArrayBufferView unwrapArrayBufferView(int[] buf); - public static ArrayBuffer unwrapArrayBuffer(int[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData().getBuffer(); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class) + public static native int[] wrapIntArray(Int32Array buf); - public static ArrayBufferView unwrapArrayBufferView(int[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData(); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class) + public static native int[] wrapIntArrayBuffer(ArrayBuffer buf); - @JSBody(params = { "buf" }, script = "return $rt_createIntArray(buf)") - private static native JSObject wrapIntArray0(JSObject buf); + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class) + public static native int[] wrapIntArrayBufferView(ArrayBufferView buf); - public static int[] wrapIntArray(Int32Array buf) { - if (buf == null) { - return null; - } - return (int[]) (Object) wrapIntArray0(buf.getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native Float32Array unwrapFloatArray(float[] buf); - public static int[] wrapIntArrayBuffer(ArrayBuffer buf) { - if (buf == null) { - return null; - } - return (int[]) (Object) wrapIntArray0(buf); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class) + public static native ArrayBuffer unwrapArrayBuffer(float[] buf); - public static int[] wrapIntArrayBufferView(ArrayBufferView buf) { - if (buf == null) { - return null; - } - return (int[]) (Object) wrapIntArray0(buf.getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native ArrayBufferView unwrapArrayBufferView(float[] buf); - public static Float32Array unwrapFloatArray(float[] buf) { - if (buf == null) { - return null; - } - return Float32Array.create(((TeaVMArrayObject) (Object) buf).getData().getBuffer()); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class) + public static native float[] wrapFloatArray(Float32Array buf); - public static ArrayBuffer unwrapArrayBuffer(float[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData().getBuffer(); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class) + public static native float[] wrapFloatArrayBuffer(ArrayBuffer buf); - public static ArrayBufferView unwrapArrayBufferView(float[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData(); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class) + public static native float[] wrapFloatArrayBufferView(ArrayBufferView buf); - @JSBody(params = { "buf" }, script = "return $rt_createFloatArray(buf)") - private static native JSObject wrapFloatArray0(JSObject buf); + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native Int16Array unwrapShortArray(short[] buf); - public static float[] wrapFloatArray(Float32Array buf) { - if (buf == null) { - return null; - } - return (float[]) (Object) wrapFloatArray0(buf.getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class) + public static native ArrayBuffer unwrapArrayBuffer(short[] buf); - public static float[] wrapFloatArrayBuffer(ArrayBuffer buf) { - if (buf == null) { - return null; - } - return (float[]) (Object) wrapFloatArray0(buf); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class) + public static native ArrayBufferView unwrapArrayBufferView(short[] buf); - public static float[] wrapFloatArrayBufferView(ArrayBufferView buf) { - if (buf == null) { - return null; - } - return (float[]) (Object) wrapFloatArray0(buf.getBuffer()); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class) + public static native short[] wrapShortArray(Int16Array buf); - public static Int16Array unwrapShortArray(short[] buf) { - if (buf == null) { - return null; - } - return Int16Array.create(((TeaVMArrayObject) (Object) buf).getData().getBuffer()); - } + @GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class) + public static native short[] wrapShortArrayBuffer(ArrayBuffer buf); - public static ArrayBuffer unwrapArrayBuffer(short[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData().getBuffer(); - } - - public static ArrayBufferView unwrapArrayBufferView(short[] buf) { - if (buf == null) { - return null; - } - return ((TeaVMArrayObject) (Object) buf).getData(); - } - - @JSBody(params = { "buf" }, script = "return $rt_createShortArray(buf)") - private static native JSObject wrapShortArray0(JSObject buf); - - public static short[] wrapShortArray(Int16Array buf) { - if (buf == null) { - return null; - } - return (short[]) (Object) wrapShortArray0(buf.getBuffer()); - } - - public static short[] wrapShortArrayBuffer(ArrayBuffer buf) { - if (buf == null) { - return null; - } - return (short[]) (Object) wrapShortArray0(buf); - } - - public static short[] wrapShortArrayBuffer(ArrayBufferView buf) { - if (buf == null) { - return null; - } - return (short[]) (Object) wrapShortArray0(buf.getBuffer()); - } + @InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class) + public static native short[] wrapShortArrayBuffer(ArrayBufferView buf); @Async public static native void sleepSetTimeout(int millis); @@ -258,41 +140,56 @@ public class TeaVMUtils { Window.setTimeout(() -> cb.complete(null), millis); } - public static String tryResolveClassesSource() { - String str = dumpJSStackTrace(); - String[] frames = EagUtils.splitPattern.split(str); - if ("Error".equals(frames[0])) { - // V8 stack trace - if (frames.length > 1) { - String framesTrim = frames[1].trim(); - if (framesTrim.startsWith("at")) { - // definitely V8 - int i = framesTrim.indexOf('('); - int j = framesTrim.indexOf(')'); - if (i != -1 && j != -1 && i < j) { - return tryResolveClassesSourceFromFrame(framesTrim.substring(i + 1, j)); - } - } - } - } else { - // Mozilla/WebKit stack trace - String framesTrim = frames[0].trim(); - int i = framesTrim.indexOf('@'); - if (i != -1) { - return tryResolveClassesSourceFromFrame(framesTrim.substring(i + 1)); - } + public static final Comparator touchSortingComparator = (t1, t2) -> { + return t1.getIdentifier() - t2.getIdentifier(); + }; + + public static final Comparator touchSortingComparator2 = (t1, t2) -> { + return t1.touch.getIdentifier() - t2.touch.getIdentifier(); + }; + + public static List toSortedTouchList(TouchList touchList, SortedTouchEvent.ITouchUIDMapper mapper, + int originX, int originY) { + int l = touchList.getLength(); + List ret = new ArrayList<>(l); + for(int i = 0; i < l; ++i) { + ret.add(OffsetTouch.create(touchList.item(i), mapper, originX, originY)); } - return null; + Collections.sort(ret, touchSortingComparator2); + return ret; } - private static String tryResolveClassesSourceFromFrame(String fileLineCol) { - int i = fileLineCol.lastIndexOf(':'); - if (i > 0) { - i = fileLineCol.lastIndexOf(':', i - 1); - } - if (i != -1) { - return fileLineCol.substring(0, i); - } - return null; + public static String tryResolveClassesSource() { + return ClassesJSLocator.resolveClassesJSFromThrowable(); } + + public static HTMLScriptElement tryResolveClassesSourceInline() { + return ClassesJSLocator.resolveClassesJSFromInline(); + } + + @JSBody(params = { "obj" }, script = "console.log(obj);") + public static native void objDump(JSObject obj); + + @JSBody(params = { "obj" }, script = "return \"\" + obj;") + public static native String safeToString(JSObject obj); + + @JSBody(params = { "obj" }, script = "return (!!obj && (typeof obj.message === \"string\")) ? obj.message : (\"\" + obj);") + public static native String safeErrorMsgToString(JSObject obj); + + @JSBody(params = { "obj" }, script = "return !!obj;") + public static native boolean isTruthy(JSObject object); + + @JSBody(params = { "obj" }, script = "return !obj;") + public static native boolean isNotTruthy(JSObject object); + + @JSBody(params = { "obj" }, script = "return obj === undefined;") + public static native boolean isUndefined(JSObject object); + + public static T ensureDefined(T valIn) { + return isUndefined((JSObject)valIn) ? null : valIn; + } + + @JSBody(params = { "obj" }, script = "return obj.stack||null;") + public static native String getStackSafe(JSObject object); + } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMWebSocketClient.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMWebSocketClient.java new file mode 100755 index 0000000..ad458f0 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMWebSocketClient.java @@ -0,0 +1,125 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSBody; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.events.MessageEvent; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.websocket.WebSocket; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.AbstractWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMWebSocketClient extends AbstractWebSocketClient { + + private final WebSocket sock; + private boolean sockIsConnecting = true; + private boolean sockIsConnected = false; + private boolean sockIsFailed = false; + + public TeaVMWebSocketClient(String socketURI) { + super(socketURI); + sock = WebSocket.create(socketURI); + sock.setBinaryType("arraybuffer"); + TeaVMUtils.addEventListener(sock, "open", new EventListener() { + @Override + public void handleEvent(Event evt) { + sockIsConnecting = false; + sockIsConnected = true; + } + }); + TeaVMUtils.addEventListener(sock, "close", new EventListener() { + @Override + public void handleEvent(Event evt) { + sockIsConnecting = false; + sockIsConnected = false; + } + }); + TeaVMUtils.addEventListener(sock, "message", new EventListener() { + @Override + public void handleEvent(MessageEvent evt) { + addRecievedFrame(new TeaVMWebSocketFrame(evt.getData())); + } + }); + TeaVMUtils.addEventListener(sock, "error", new EventListener() { + @Override + public void handleEvent(Event evt) { + if(sockIsConnecting) { + sockIsFailed = true; + sockIsConnecting = false; + } + } + }); + } + + @Override + public boolean connectBlocking(int timeoutMS) { + long startTime = PlatformRuntime.steadyTimeMillis(); + while(!sockIsConnected && !sockIsFailed) { + EagUtils.sleep(50l); + if(PlatformRuntime.steadyTimeMillis() - startTime > timeoutMS * 1000) { + break; + } + } + return sockIsConnected; + } + + @Override + public EnumEaglerConnectionState getState() { + return sockIsConnected ? EnumEaglerConnectionState.CONNECTED + : (sockIsFailed ? EnumEaglerConnectionState.FAILED + : (sockIsConnecting ? EnumEaglerConnectionState.CONNECTING : EnumEaglerConnectionState.CLOSED)); + } + + @Override + public boolean isOpen() { + return sockIsConnected; + } + + @Override + public boolean isClosed() { + return !sockIsConnecting && !sockIsConnected; + } + + @Override + public void close() { + sockIsConnecting = false; + sockIsConnected = false; + sock.close(); + } + + @Override + public void send(String str) { + if(sockIsConnected) { + sock.send(str); + } + } + + @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") + protected static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); + + @Override + public void send(byte[] bytes) { + if(sockIsConnected) { + nativeBinarySend(sock, TeaVMUtils.unwrapArrayBuffer(bytes)); + } + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMWebSocketFrame.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMWebSocketFrame.java new file mode 100755 index 0000000..63f3394 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMWebSocketFrame.java @@ -0,0 +1,114 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.io.InputStream; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMWebSocketFrame implements IWebSocketFrame { + + private JSObject data; + private boolean str; + + private String cachedStrContent = null; + private byte[] cachedByteContent = null; + + private int cachedLen = -1; + + private final long timestamp; + + @JSBody(params = { "obj" }, script = "return (typeof obj === \"string\");") + private static native boolean isStr(JSObject obj); + + public TeaVMWebSocketFrame(JSObject data) { + this.data = data; + this.str = isStr(data); + this.timestamp = PlatformRuntime.steadyTimeMillis(); + } + + @Override + public boolean isString() { + return str; + } + + @JSBody(params = { "obj" }, script = "return obj;") + private static native String toStr(JSObject obj); + + @Override + public String getString() { + if(str) { + if(cachedStrContent == null) { + return (cachedStrContent = toStr(data)); + }else { + return cachedStrContent; + } + }else { + return null; + } + } + + @Override + public byte[] getByteArray() { + if(!str) { + if(cachedByteContent == null) { + return (cachedByteContent = TeaVMUtils.wrapByteArrayBuffer((ArrayBuffer)data)); + }else { + return cachedByteContent; + } + }else { + return null; + } + } + + @Override + public InputStream getInputStream() { + if(!str) { + return new ArrayBufferInputStream((ArrayBuffer)data); + }else { + return null; + } + } + + @JSBody(params = { "obj" }, script = "return obj.length;") + private static native int strLen(JSObject obj); + + @JSBody(params = { "obj" }, script = "return obj.byteLength;") + private static native int arrLen(JSObject obj); + + @Override + public int getLength() { + if(cachedLen == -1) { + if(str) { + cachedLen = strLen(data); + }else { + cachedLen = arrLen(data); + } + } + return cachedLen; + } + + @Override + public long getTimestamp() { + return timestamp; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/Touch.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/Touch.java new file mode 100755 index 0000000..ad5154c --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/Touch.java @@ -0,0 +1,67 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; +import org.teavm.jso.dom.xml.Element; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class Touch implements JSObject { + + @JSProperty + public abstract int getIdentifier(); + + @JSProperty + public abstract double getScreenX(); + + @JSProperty + public abstract double getScreenY(); + + @JSProperty + public abstract double getClientX(); + + @JSProperty + public abstract double getClientY(); + + @JSProperty + public abstract double getPageX(); + + @JSProperty + public abstract double getPageY(); + + @JSProperty + public abstract double getRadiusX(); + + @JSBody(params = { "defVal" }, script = "return (typeof this.radiusX === \"number\") ? this.radiusX : defVal;") + public abstract double getRadiusXSafe(double defaultVal); + + @JSProperty + public abstract double getRadiusY(); + + @JSBody(params = { "defVal" }, script = "return (typeof this.radiusY === \"number\") ? this.radiusY : defVal;") + public abstract double getRadiusYSafe(double defaultVal); + + @JSProperty + public abstract double getForce(); + + @JSBody(params = { "defVal" }, script = "return (typeof this.force === \"number\") ? this.force : defVal;") + public abstract double getForceSafe(double defaultVal); + + @JSProperty + public abstract Element getTarget(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TouchEvent.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TouchEvent.java new file mode 100755 index 0000000..349faf4 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TouchEvent.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSProperty; +import org.teavm.jso.dom.events.Event; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface TouchEvent extends Event { + + @JSProperty + boolean getAltKey(); + + @JSProperty + boolean getCtrlKey(); + + @JSProperty + boolean getMetaKey(); + + @JSProperty + boolean getShiftKey(); + + @JSProperty + TouchList getChangedTouches(); + + @JSProperty + TouchList getTargetTouches(); + + @JSProperty + TouchList getTouches(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TouchList.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TouchList.java new file mode 100755 index 0000000..9708bf8 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TouchList.java @@ -0,0 +1,28 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface TouchList extends JSObject { + + @JSProperty + int getLength(); + + Touch item(int idx); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/VisualViewport.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/VisualViewport.java new file mode 100755 index 0000000..c94212b --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/VisualViewport.java @@ -0,0 +1,45 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; +import org.teavm.jso.dom.events.EventTarget; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface VisualViewport extends JSObject, EventTarget { + + @JSProperty + int getOffsetLeft(); + + @JSProperty + int getOffsetTop(); + + @JSProperty + int getPageLeft(); + + @JSProperty + int getPageTop(); + + @JSProperty + int getWidth(); + + @JSProperty + int getHeight(); + + @JSProperty + double getScale(); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLANGLEInstancedArrays.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLANGLEInstancedArrays.java new file mode 100755 index 0000000..f0cd374 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLANGLEInstancedArrays.java @@ -0,0 +1,28 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSObject; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface WebGLANGLEInstancedArrays extends JSObject { + + void drawArraysInstancedANGLE(int mode, int first, int count, int instanced); + + void drawElementsInstancedANGLE(int mode, int count, int type, int offset, int primcount); + + void vertexAttribDivisorANGLE(int index, int divisor); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLBackBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLBackBuffer.java new file mode 100755 index 0000000..8dcdd4e --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLBackBuffer.java @@ -0,0 +1,300 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import org.teavm.jso.webgl.WebGLFramebuffer; + +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; +import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class WebGLBackBuffer { + + private static int glesVers = -1; + + private static WebGL2RenderingContext ctx; + private static WebGLFramebuffer framebuffer; + private static IFramebufferGL eagFramebuffer; + private static int width; + private static int height; + + // GLES 3.0+ + private static IRenderbufferGL gles3ColorRenderbuffer; + private static IRenderbufferGL gles3DepthRenderbuffer; + + // GLES 2.0 + private static ITextureGL gles2ColorTexture; + private static IRenderbufferGL gles2DepthRenderbuffer; + private static IProgramGL gles2BlitProgram; + private static IBufferArrayGL gles2BlitVAO; + private static IBufferGL gles2BlitVBO; + + private static boolean isVAOCapable = false; + private static boolean isEmulatedVAOPhase = false; + + private static final int _GL_FRAMEBUFFER = 0x8D40; + private static final int _GL_RENDERBUFFER = 0x8D41; + private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; + private static final int _GL_DEPTH_ATTACHMENT = 0x8D00; + private static final int _GL_DEPTH_COMPONENT16 = 0x81A5; + private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC; + private static final int _GL_READ_FRAMEBUFFER = 0x8CA8; + private static final int _GL_DRAW_FRAMEBUFFER = 0x8CA9; + + public static void initBackBuffer(WebGL2RenderingContext ctxIn, WebGLFramebuffer fbo, IFramebufferGL eagFbo, int sw, int sh) { + ctx = ctxIn; + glesVers = checkOpenGLESVersion(); + framebuffer = fbo; + eagFramebuffer = eagFbo; + isVAOCapable = checkVAOCapable(); + isEmulatedVAOPhase = false; + width = sw; + height = sh; + if(glesVers >= 300) { + gles3ColorRenderbuffer = _wglCreateRenderbuffer(); + gles3DepthRenderbuffer = _wglCreateRenderbuffer(); + _wglBindFramebuffer(_GL_FRAMEBUFFER, eagFbo); + _wglBindRenderbuffer(_GL_RENDERBUFFER, gles3ColorRenderbuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, GL_RGBA8, sw, sh); + _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, _GL_RENDERBUFFER, gles3ColorRenderbuffer); + _wglBindRenderbuffer(_GL_RENDERBUFFER, gles3DepthRenderbuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT32F, sw, sh); + _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, gles3DepthRenderbuffer); + _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); + }else { + gles2ColorTexture = _wglGenTextures(); + gles2DepthRenderbuffer = _wglCreateRenderbuffer(); + _wglBindFramebuffer(_GL_FRAMEBUFFER, eagFbo); + _wglBindTexture(GL_TEXTURE_2D, gles2ColorTexture); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sw, sh, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gles2ColorTexture, 0); + _wglBindRenderbuffer(_GL_RENDERBUFFER, gles2DepthRenderbuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, sw, sh); + _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, gles2DepthRenderbuffer); + + ByteBuffer upload = PlatformRuntime.allocateByteBuffer(48); + upload.putFloat(0.0f); upload.putFloat(0.0f); + upload.putFloat(1.0f); upload.putFloat(0.0f); + upload.putFloat(0.0f); upload.putFloat(1.0f); + upload.putFloat(1.0f); upload.putFloat(0.0f); + upload.putFloat(1.0f); upload.putFloat(1.0f); + upload.putFloat(0.0f); upload.putFloat(1.0f); + upload.flip(); + + gles2BlitVBO = _wglGenBuffers(); + EaglercraftGPU.bindVAOGLArrayBufferNow(gles2BlitVBO); + _wglBufferData(GL_ARRAY_BUFFER, upload, GL_STATIC_DRAW); + + PlatformRuntime.freeByteBuffer(upload); + + if(isVAOCapable) { + gles2BlitVAO = _wglGenVertexArrays(); + _wglBindVertexArray(gles2BlitVAO); + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + } + + IShaderGL vertShader = _wglCreateShader(GL_VERTEX_SHADER); + _wglShaderSource(vertShader, "#version 100\nprecision mediump float; attribute vec2 a_pos2f; varying vec2 v_tex2f; void main() { v_tex2f = a_pos2f; gl_Position = vec4(a_pos2f * 2.0 - 1.0, 0.0, 1.0); }"); + _wglCompileShader(vertShader); + + IShaderGL fragShader = _wglCreateShader(GL_FRAGMENT_SHADER); + _wglShaderSource(fragShader, checkTextureLODCapable() + ? "#version 100\n#extension GL_EXT_shader_texture_lod : enable\nprecision mediump float; precision mediump sampler2D; varying vec2 v_tex2f; uniform sampler2D u_samplerTex; void main() { gl_FragColor = vec4(texture2DLodEXT(u_samplerTex, v_tex2f, 0.0).rgb, 1.0); }" + : "#version 100\nprecision mediump float; precision mediump sampler2D; varying vec2 v_tex2f; uniform sampler2D u_samplerTex; void main() { gl_FragColor = vec4(texture2D(u_samplerTex, v_tex2f).rgb, 1.0); }"); + _wglCompileShader(fragShader); + + gles2BlitProgram = _wglCreateProgram(); + + _wglAttachShader(gles2BlitProgram, vertShader); + _wglAttachShader(gles2BlitProgram, fragShader); + + _wglBindAttribLocation(gles2BlitProgram, 0, "a_pos2f"); + + _wglLinkProgram(gles2BlitProgram); + + _wglDetachShader(gles2BlitProgram, vertShader); + _wglDetachShader(gles2BlitProgram, fragShader); + + _wglDeleteShader(vertShader); + _wglDeleteShader(fragShader); + + _wglUseProgram(gles2BlitProgram); + + _wglUniform1i(_wglGetUniformLocation(gles2BlitProgram, "u_samplerTex"), 0); + } + } + + public static void enterVAOEmulationPhase() { + if(glesVers < 300) { + if(!isEmulatedVAOPhase) { + if(isVAOCapable) { + _wglDeleteVertexArrays(gles2BlitVAO); + } + gles2BlitVAO = EaglercraftGPU.createGLBufferArray(); + EaglercraftGPU.bindGLBufferArray(gles2BlitVAO); + EaglercraftGPU.bindVAOGLArrayBuffer(gles2BlitVBO); + EaglercraftGPU.enableVertexAttribArray(0); + EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + isEmulatedVAOPhase = true; + } + } + } + + private static void drawBlitQuad() { + if(isEmulatedVAOPhase) { + EaglercraftGPU.bindGLBufferArray(gles2BlitVAO); + EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6); + }else { + if(isVAOCapable) { + _wglBindVertexArray(gles2BlitVAO); + _wglDrawArrays(GL_TRIANGLES, 0, 6); + }else { + EaglercraftGPU.bindGLArrayBuffer(gles2BlitVBO); + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0); + _wglDrawArrays(GL_TRIANGLES, 0, 6); + } + } + } + + public static void flipBuffer(int windowWidth, int windowHeight) { + if(glesVers >= 300) { + ctx.bindFramebuffer(_GL_READ_FRAMEBUFFER, framebuffer); + ctx.bindFramebuffer(_GL_DRAW_FRAMEBUFFER, null); + ctx.blitFramebuffer(0, 0, width, height, 0, 0, windowWidth, windowHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + ctx.bindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + + if(windowWidth != width || windowHeight != height) { + width = windowWidth; + height = windowHeight; + + _wglBindRenderbuffer(_GL_RENDERBUFFER, gles3ColorRenderbuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, GL_RGBA8, windowWidth, windowHeight); + + _wglBindRenderbuffer(_GL_RENDERBUFFER, gles3DepthRenderbuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT32F, windowWidth, windowHeight); + } + }else { + ctx.bindFramebuffer(_GL_FRAMEBUFFER, null); + _wglActiveTexture(GL_TEXTURE0); + _wglBindTexture(GL_TEXTURE_2D, gles2ColorTexture); + + int[] viewportStash = null; + if(isEmulatedVAOPhase) { + viewportStash = new int[4]; + EaglercraftGPU.glGetInteger(GL_VIEWPORT, viewportStash); + GlStateManager.viewport(0, 0, windowWidth, windowHeight); + GlStateManager.eagPushStateForGLES2BlitHack(); + GlStateManager.disableDepth(); + GlStateManager.disableBlend(); + }else { + _wglViewport(0, 0, windowWidth, windowHeight); + _wglDisable(GL_DEPTH_TEST); + _wglDisable(GL_BLEND); + } + + EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM | EaglercraftGPU.CLEAR_BINDING_ARRAY_BUFFER); + + EaglercraftGPU.bindGLShaderProgram(gles2BlitProgram); + + drawBlitQuad(); + + if(windowWidth != width || windowHeight != height) { + width = windowWidth; + height = windowHeight; + + _wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); + + _wglBindRenderbuffer(_GL_RENDERBUFFER, gles2DepthRenderbuffer); + _wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, windowWidth, windowHeight); + } + + if(isEmulatedVAOPhase) { + EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_TEXTURE0 | EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM); + if(viewportStash[2] > 0) { + GlStateManager.viewport(viewportStash[0], viewportStash[1], viewportStash[2], viewportStash[3]); + } + GlStateManager.eagPopStateForGLES2BlitHack(); + }else { + EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_TEXTURE0 | EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM | EaglercraftGPU.CLEAR_BINDING_BUFFER_ARRAY); + } + + ctx.bindFramebuffer(_GL_FRAMEBUFFER, framebuffer); + } + } + + public static void destroy() { + if(eagFramebuffer != null) { + _wglDeleteFramebuffer(eagFramebuffer); + eagFramebuffer = null; + } + if(gles3ColorRenderbuffer != null) { + _wglDeleteRenderbuffer(gles3ColorRenderbuffer); + gles3ColorRenderbuffer = null; + } + if(gles3DepthRenderbuffer != null) { + _wglDeleteRenderbuffer(gles3DepthRenderbuffer); + gles3DepthRenderbuffer = null; + } + if(gles2ColorTexture != null) { + _wglDeleteTextures(gles2ColorTexture); + gles2ColorTexture = null; + } + if(gles2DepthRenderbuffer != null) { + _wglDeleteRenderbuffer(gles2DepthRenderbuffer); + gles2DepthRenderbuffer = null; + } + if(gles2BlitProgram != null) { + _wglDeleteProgram(gles2BlitProgram); + gles2BlitProgram = null; + } + if(gles2BlitVAO != null) { + if(isEmulatedVAOPhase) { + EaglercraftGPU.destroyGLBufferArray(gles2BlitVAO); + }else if(isVAOCapable) { + _wglDeleteVertexArrays(gles2BlitVAO); + } + gles2BlitVAO = null; + } + if(gles2BlitVBO != null) { + _wglDeleteBuffers(gles2BlitVBO); + gles2BlitVBO = null; + } + framebuffer = null; + width = 0; + height = 0; + isVAOCapable = false; + isEmulatedVAOPhase = false; + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLOESVertexArrayObject.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLOESVertexArrayObject.java new file mode 100755 index 0000000..504f9e3 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/WebGLOESVertexArrayObject.java @@ -0,0 +1,28 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import org.teavm.jso.JSObject; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface WebGLOESVertexArrayObject extends JSObject { + + WebGLVertexArray createVertexArrayOES(); + + void deleteVertexArrayOES(WebGLVertexArray obj); + + void bindVertexArrayOES(WebGLVertexArray obj); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/generators/TeaVMRuntimeDeobfuscatorGenerator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/generators/TeaVMRuntimeDeobfuscatorGenerator.java new file mode 100755 index 0000000..161f8c2 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/generators/TeaVMRuntimeDeobfuscatorGenerator.java @@ -0,0 +1,108 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm.generators; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.teavm.backend.javascript.codegen.ScopedName; +import org.teavm.backend.javascript.codegen.SourceWriter; +import org.teavm.backend.javascript.spi.Generator; +import org.teavm.backend.javascript.spi.GeneratorContext; +import org.teavm.model.MethodReference; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.Base64VarIntArray; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMRuntimeDeobfuscatorGenerator implements Generator { + + private int indexIntoSet(String name, Map namesSet, List namesList) { + Integer ret = namesSet.get(name); + if(ret != null) { + return ret.intValue(); + } + int i = namesList.size(); + namesList.add(name); + namesSet.put(name, i); + return i; + } + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + Map> map = new HashMap<>(); + List classNamesPartsList = new ArrayList<>(); + Map classNamesPartsSet = new HashMap<>(); + List namesList = new ArrayList<>(); + Map namesSet = new HashMap<>(); + Map> namesEncSet = new HashMap<>(); + for(MethodReference method : context.getDependency().getReachableMethods()) { + ScopedName name = writer.getNaming().getFullNameFor(method); + if(name.scoped) { + continue; + } + String clsName = method.getClassName(); + List lst = map.get(clsName); + if(lst == null) { + map.put(clsName, lst = new ArrayList<>()); + } + lst.add(indexIntoSet(name.value, namesSet, namesList)); + lst.add(indexIntoSet(method.getName(), namesSet, namesList)); + } + for(String str : map.keySet()) { + List builder = new ArrayList<>(); + boolean b = false; + for(String strr : str.split("\\.")) { + builder.add(indexIntoSet(strr, classNamesPartsSet, classNamesPartsList)); + b = true; + } + namesEncSet.put(str, builder); + } + writer.append("return [").ws().append('[').ws(); + boolean b = false; + for(String str : classNamesPartsList) { + if(b) { + writer.append(',').ws(); + } + writer.append('\"').append(str).append('\"'); + b = true; + } + writer.append("],").ws().append('[').ws(); + b = false; + for(String str : namesList) { + if(b) { + writer.append(',').ws(); + } + writer.append('\"').append(str).append('\"'); + b = true; + } + writer.ws().append("],").ws(); + b = false; + for (Entry> name : map.entrySet()) { + if(b) { + writer.append(',').ws(); + } + writer.append('\"').append(Base64VarIntArray.encodeVarIntArray(namesEncSet.get(name.getKey()))).append("\",").ws(); + writer.append('\"').appendClass(name.getKey()).append("\",").ws().append('\"'); + writer.append(Base64VarIntArray.encodeVarIntArray(name.getValue())).append('\"').ws(); + b = true; + } + writer.ws().append("];").softNewLine(); + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/generators/TeaVMUtilsUnwrapGenerator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/generators/TeaVMUtilsUnwrapGenerator.java new file mode 100755 index 0000000..457e6d7 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/generators/TeaVMUtilsUnwrapGenerator.java @@ -0,0 +1,166 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm.generators; + +import java.io.IOException; + +import org.teavm.backend.javascript.codegen.SourceWriter; +import org.teavm.backend.javascript.spi.Generator; +import org.teavm.backend.javascript.spi.GeneratorContext; +import org.teavm.backend.javascript.spi.Injector; +import org.teavm.backend.javascript.spi.InjectorContext; +import org.teavm.model.MethodReference; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class TeaVMUtilsUnwrapGenerator { + + // WARNING: This code uses internal TeaVM APIs that may not have + // been intended for end users of the compiler to program with + + public static class UnwrapArrayBuffer implements Injector { + + @Override + public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + context.writeExpr(context.getArgument(0)); + context.getWriter().append(".data.buffer"); + } + + } + + public static class UnwrapTypedArray implements Injector { + + @Override + public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + context.writeExpr(context.getArgument(0)); + context.getWriter().append(".data"); + } + + } + + public static class WrapArrayBuffer implements Generator { + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + throws IOException { + String parName = context.getParameterName(1); + switch (methodRef.getName()) { + case "wrapByteArrayBuffer": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_bytecls(),").ws().append("new Int8Array(").append(parName).append("))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapIntArrayBuffer": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_intcls(),").ws().append("new Int32Array(").append(parName).append("))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapFloatArrayBuffer": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_floatcls(),").ws().append("new Float32Array(").append(parName).append("))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapShortArrayBuffer": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_shortcls(),").ws().append("new Int16Array(").append(parName).append("))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + default: + break; + } + } + + } + + public static class WrapArrayBufferView implements Generator { + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + throws IOException { + String parName = context.getParameterName(1); + switch (methodRef.getName()) { + case "wrapByteArrayBufferView": + case "wrapUnsignedByteArray": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_bytecls(),").ws().append("new Int8Array(").append(parName).append(".buffer))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapIntArrayBufferView": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_intcls(),").ws().append("new Int32Array(").append(parName).append(".buffer))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapFloatArrayBufferView": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_floatcls(),").ws().append("new Float32Array(").append(parName).append(".buffer))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapShortArrayBufferView": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_shortcls(),").ws().append("new Int16Array(").append(parName).append(".buffer))").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + default: + break; + } + } + + } + + public static class WrapTypedArray implements Generator { + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + throws IOException { + String parName = context.getParameterName(1); + switch (methodRef.getName()) { + case "wrapByteArray": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_bytecls(),").ws().append(parName).append(")").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapIntArray": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_intcls(),").ws().append(parName).append(")").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapFloatArray": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_floatcls(),").ws().append(parName).append(")").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + case "wrapShortArray": + writer.append("return ").append(parName).ws().append('?').ws(); + writer.append("$rt_createNumericArray($rt_shortcls(),").ws().append(parName).append(")").ws(); + writer.append(':').ws().append("null;").softNewLine(); + break; + default: + break; + } + } + + } + + public static class UnwrapUnsignedTypedArray implements Injector { + + @Override + public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + context.getWriter().append("new Uint8Array("); + context.writeExpr(context.getArgument(0)); + context.getWriter().append(".data.buffer)"); + } + + } + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java index 7b2aa21..bd0b406 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java @@ -6,21 +6,14 @@ import org.teavm.jso.JSObject; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -36,4 +29,7 @@ public abstract class JSEaglercraftXOptsHooks implements JSObject { @JSBody(script = "return (typeof this.crashReportShow === \"function\") ? this.crashReportShow : null;") public native JSObject getCrashReportHook(); + @JSBody(script = "return (typeof this.screenChanged === \"function\") ? this.screenChanged : null;") + public native JSObject getScreenChangedHook(); + } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java index 0612ef8..b6291aa 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java @@ -1,123 +1,174 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts; - -import org.teavm.jso.JSBody; -import org.teavm.jso.JSObject; -import org.teavm.jso.core.JSArrayReader; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public abstract class JSEaglercraftXOptsRoot implements JSObject { - - @JSBody(script = "return (typeof this.container === \"string\") ? this.container : null;") - public native String getContainer(); - - @JSBody(script = "return (typeof this.assetsURI === \"string\") ? this.assetsURI : null;") - public native String getAssetsURI(); - - @JSBody(script = "return (typeof this.assetsURI === \"object\") ? this.assetsURI : null;") - public native JSArrayReader getAssetsURIArray(); - - @JSBody(params = { "def" }, script = "return (typeof this.lang === \"string\") ? this.lang : def;") - public native String getLang(String defaultValue); - - @JSBody(params = { "def" }, script = "return (typeof this.joinServer === \"string\") ? this.joinServer : def;") - public native String getJoinServer(String defaultValue); - - @JSBody(params = { "def" }, script = "return (typeof this.localesURI === \"string\") ? this.localesURI : def;") - public native String getLocalesURI(String defaultValue); - - @JSBody(params = { "def" }, script = "return (typeof this.worldsDB === \"string\") ? this.worldsDB : def;") - public native String getWorldsDB(String defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.resourcePacksDB === \"string\") ? this.resourcePacksDB : def;") - public native String getResourcePacksDB(String defaultValue); - - @JSBody(params = { "def" }, script = "return (typeof this.demoMode === \"boolean\") ? this.demoMode : def;") - public native boolean getDemoMode(boolean defaultValue); - - @JSBody(script = "return (typeof this.servers === \"object\") ? this.servers : null;") - public native JSArrayReader getServers(); - - @JSBody(script = "return (typeof this.relays === \"object\") ? this.relays : null;") - public native JSArrayReader getRelays(); - - @JSBody(params = { - "def" }, script = "return (typeof this.checkShaderGLErrors === \"boolean\") ? this.checkShaderGLErrors : def;") - public native boolean getCheckShaderGLErrors(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.enableDownloadOfflineButton === \"boolean\") ? this.enableDownloadOfflineButton : def;") - public native boolean getEnableDownloadOfflineButton(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.downloadOfflineButtonLink === \"string\") ? this.downloadOfflineButtonLink : def;") - public native String getDownloadOfflineButtonLink(String defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.html5CursorSupport === \"boolean\") ? this.html5CursorSupport : def;") - public native boolean getHtml5CursorSupport(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.allowUpdateSvc === \"boolean\") ? this.allowUpdateSvc : def;") - public native boolean getAllowUpdateSvc(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.allowUpdateDL === \"boolean\") ? this.allowUpdateDL : def;") - public native boolean getAllowUpdateDL(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.logInvalidCerts === \"boolean\") ? this.logInvalidCerts : def;") - public native boolean getLogInvalidCerts(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.enableSignatureBadge === \"boolean\") ? this.enableSignatureBadge : def;") - public native boolean getEnableSignatureBadge(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.checkRelaysForUpdates === \"boolean\") ? this.checkRelaysForUpdates : def;") - public native boolean getCheckRelaysForUpdates(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.allowVoiceClient === \"boolean\") ? this.allowVoiceClient : def;") - public native boolean getAllowVoiceClient(boolean defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.allowFNAWSkins === \"boolean\") ? this.allowFNAWSkins : def;") - public native boolean getAllowFNAWSkins(boolean defaultValue); - - @JSBody(script = "return (typeof this.hooks === \"object\") ? this.hooks : null;") - public native JSEaglercraftXOptsHooks getHooks(); - - @JSBody(params = { - "def" }, script = "return (typeof this.localStorageNamespace === \"string\") ? this.localStorageNamespace : def;") - public native String getLocalStorageNamespace(String defaultValue); - - @JSBody(params = { - "def" }, script = "return (typeof this.enableMinceraft === \"boolean\") ? this.enableMinceraft : def;") - public native boolean getEnableMinceraft(boolean defaultValue); - - @JSBody(params = { "def" }, script = "return (typeof this.crashOnUncaughtExceptions === \"boolean\") ? this.crashOnUncaughtExceptions : def;") - public native boolean getCrashOnUncaughtExceptions(boolean defaultValue); - -} +package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.core.JSArrayReader; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public abstract class JSEaglercraftXOptsRoot implements JSObject { + + @JSBody(script = "return (typeof this.container === \"string\") ? this.container : null;") + public native String getContainer(); + + @JSBody(script = "return (typeof this.assetsURI === \"string\") ? this.assetsURI : null;") + public native String getAssetsURI(); + + @JSBody(script = "return (typeof this.assetsURI === \"object\") ? this.assetsURI : null;") + public native JSArrayReader getAssetsURIArray(); + + @JSBody(params = { "def" }, script = "return (typeof this.lang === \"string\") ? this.lang : def;") + public native String getLang(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.joinServer === \"string\") ? this.joinServer : def;") + public native String getJoinServer(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.localesURI === \"string\") ? this.localesURI : def;") + public native String getLocalesURI(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.worldsDB === \"string\") ? this.worldsDB : def;") + public native String getWorldsDB(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.resourcePacksDB === \"string\") ? this.resourcePacksDB : def;") + public native String getResourcePacksDB(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.demoMode === \"boolean\") ? this.demoMode : def;") + public native boolean getDemoMode(boolean defaultValue); + + @JSBody(script = "return (typeof this.servers === \"object\") ? this.servers : null;") + public native JSArrayReader getServers(); + + @JSBody(script = "return (typeof this.relays === \"object\") ? this.relays : null;") + public native JSArrayReader getRelays(); + + @JSBody(params = { "def" }, script = "return (typeof this.checkShaderGLErrors === \"boolean\") ? this.checkShaderGLErrors : def;") + public native boolean getCheckShaderGLErrors(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.enableDownloadOfflineButton === \"boolean\") ? this.enableDownloadOfflineButton : def;") + public native boolean getEnableDownloadOfflineButton(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.downloadOfflineButtonLink === \"string\") ? this.downloadOfflineButtonLink : def;") + public native String getDownloadOfflineButtonLink(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.html5CursorSupport === \"boolean\") ? this.html5CursorSupport : def;") + public native boolean getHtml5CursorSupport(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowUpdateSvc === \"boolean\") ? this.allowUpdateSvc : def;") + public native boolean getAllowUpdateSvc(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowUpdateDL === \"boolean\") ? this.allowUpdateDL : def;") + public native boolean getAllowUpdateDL(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.logInvalidCerts === \"boolean\") ? this.logInvalidCerts : def;") + public native boolean getLogInvalidCerts(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.enableSignatureBadge === \"boolean\") ? this.enableSignatureBadge : def;") + public native boolean getEnableSignatureBadge(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.checkRelaysForUpdates === \"boolean\") ? this.checkRelaysForUpdates : def;") + public native boolean getCheckRelaysForUpdates(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowVoiceClient === \"boolean\") ? this.allowVoiceClient : def;") + public native boolean getAllowVoiceClient(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowFNAWSkins === \"boolean\") ? this.allowFNAWSkins : def;") + public native boolean getAllowFNAWSkins(boolean defaultValue); + + @JSBody(script = "return (typeof this.hooks === \"object\") ? this.hooks : null;") + public native JSEaglercraftXOptsHooks getHooks(); + + @JSBody(params = { "def" }, script = "return (typeof this.localStorageNamespace === \"string\") ? this.localStorageNamespace : def;") + public native String getLocalStorageNamespace(String defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.enableMinceraft === \"boolean\") ? this.enableMinceraft : def;") + public native boolean getEnableMinceraft(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.enableServerCookies === \"boolean\") ? this.enableServerCookies : def;") + public native boolean getEnableServerCookies(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowServerRedirects === \"boolean\") ? this.allowServerRedirects : def;") + public native boolean getAllowServerRedirects(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.crashOnUncaughtExceptions === \"boolean\") ? this.crashOnUncaughtExceptions : def;") + public native boolean getCrashOnUncaughtExceptions(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.openDebugConsoleOnLaunch === \"boolean\") ? this.openDebugConsoleOnLaunch : def;") + public native boolean getOpenDebugConsoleOnLaunch(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.fixDebugConsoleUnloadListener === \"boolean\") ? this.fixDebugConsoleUnloadListener : def;") + public native boolean getFixDebugConsoleUnloadListener(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.forceWebViewSupport === \"boolean\") ? this.forceWebViewSupport : def;") + public native boolean getForceWebViewSupport(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.enableWebViewCSP === \"boolean\") ? this.enableWebViewCSP : def;") + public native boolean getEnableWebViewCSP(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.autoFixLegacyStyleAttr === \"boolean\") ? this.autoFixLegacyStyleAttr : def;") + public native boolean getAutoFixLegacyStyleAttr(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.showBootMenuOnLaunch === \"boolean\") ? this.showBootMenuOnLaunch : def;") + public native boolean getShowBootMenuOnLaunch(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.bootMenuBlocksUnsignedClients === \"boolean\") ? this.bootMenuBlocksUnsignedClients : def;") + public native boolean getBootMenuBlocksUnsignedClients(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowBootMenu === \"boolean\") ? this.allowBootMenu : def;") + public native boolean getAllowBootMenu(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.forceProfanityFilter === \"boolean\") ? this.forceProfanityFilter : def;") + public native boolean getForceProfanityFilter(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.forceWebGL1 === \"boolean\") ? this.forceWebGL1 : def;") + public native boolean getForceWebGL1(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.forceWebGL2 === \"boolean\") ? this.forceWebGL2 : def;") + public native boolean getForceWebGL2(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowExperimentalWebGL1 === \"boolean\") ? this.allowExperimentalWebGL1 : def;") + public native boolean getAllowExperimentalWebGL1(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.useWebGLExt === \"boolean\") ? this.useWebGLExt : def;") + public native boolean getUseWebGLExt(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.useDelayOnSwap === \"boolean\") ? this.useDelayOnSwap : def;") + public native boolean getUseDelayOnSwap(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.useJOrbisAudioDecoder === \"boolean\") ? this.useJOrbisAudioDecoder : def;") + public native boolean getUseJOrbisAudioDecoder(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.useXHRFetch === \"boolean\") ? this.useXHRFetch : def;") + public native boolean getUseXHRFetch(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.useVisualViewport === \"boolean\") ? this.useVisualViewport : def;") + public native boolean getUseVisualViewport(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.deobfStackTraces === \"boolean\") ? this.deobfStackTraces : def;") + public native boolean getDeobfStackTraces(boolean deobfStackTraces); + + @JSBody(params = { "def" }, script = "return (typeof this.disableBlobURLs === \"boolean\") ? this.disableBlobURLs : def;") + public native boolean getDisableBlobURLs(boolean deobfStackTraces); + + @JSBody(params = { "def" }, script = "return (typeof this.eaglerNoDelay === \"boolean\") ? this.eaglerNoDelay : def;") + public native boolean getEaglerNoDelay(boolean deobfStackTraces); + + @JSBody(params = { "def" }, script = "return (typeof this.ramdiskMode === \"boolean\") ? this.ramdiskMode : def;") + public native boolean getRamdiskMode(boolean deobfStackTraces); + + @JSBody(params = { "def" }, script = "return (typeof this.singleThreadMode === \"boolean\") ? this.singleThreadMode : def;") + public native boolean getSingleThreadMode(boolean deobfStackTraces); + +} diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServer.java index 8ff7e6c..e5c0624 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServer.java @@ -23,6 +23,9 @@ public abstract class JSEaglercraftXOptsServer implements JSObject { @JSBody(script = "return (typeof this.addr === \"string\") ? this.addr : null;") public native String getAddr(); + @JSBody(params = { "def" }, script = "return (typeof this.hideAddr === \"boolean\") ? this.hideAddr : def;") + public native boolean getHideAddr(boolean defaultValue); + @JSBody(params = { "def" }, script = "return (typeof this.name === \"string\") ? this.name : def;") public native String getName(String defaultValue); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java index 7f7abc1..bae2309 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/internal/ClientPlatformSingleplayer.java @@ -1,253 +1,339 @@ -package net.lax1dude.eaglercraft.v1_8.sp.internal; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import org.teavm.jso.JSBody; -import org.teavm.jso.JSFunctor; -import org.teavm.jso.JSObject; -import org.teavm.jso.dom.events.ErrorEvent; -import org.teavm.jso.dom.events.EventListener; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.workers.Worker; - -import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; - -/** - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public class ClientPlatformSingleplayer { - - private static final Logger logger = LogManager.getLogger("ClientPlatformSingleplayer"); - - private static final LinkedList messageQueue = new LinkedList(); - - @JSBody(params = {}, script = "return (typeof window.eaglercraftXClientScriptElement !== \"undefined\") ? window.eaglercraftXClientScriptElement : null;") - private static native JSObject loadIntegratedServerSourceOverride(); - - @JSBody(params = {}, script = "return (typeof window.eaglercraftXClientScriptURL === \"string\") ? window.eaglercraftXClientScriptURL : null;") - private static native String loadIntegratedServerSourceOverrideURL(); - - @JSBody(params = {}, script = "try{throw new Error();}catch(ex){return ex.stack;}return null;") - private static native String loadIntegratedServerSourceStack(); - - @JSBody(params = { - "csc" }, script = "if(typeof csc.src === \"string\" && csc.src.length > 0) return csc.src; else return null;") - private static native String loadIntegratedServerSourceURL(JSObject scriptTag); - - @JSBody(params = { "csc", "tail" }, script = "const cscText = csc.text;" - + "if(typeof cscText === \"string\" && cscText.length > 0) return new Blob([cscText, tail], { type: \"text/javascript;charset=utf8\" });" - + "else return null;") - private static native JSObject loadIntegratedServerSourceInline(JSObject scriptTag, String tail); - - private static String integratedServerSource = null; - private static String integratedServerSourceOriginalURL = null; - private static boolean serverSourceLoaded = false; - - private static Worker workerObj = null; - - @JSFunctor - private static interface WorkerBinaryPacketHandler extends JSObject { - public void onMessage(String channel, ArrayBuffer buf); - } - - @JSBody(params = { "w", "wb" }, script = "w.onmessage = function(o) { wb(o.data.ch, o.data.dat); };") - private static native void registerPacketHandler(Worker w, WorkerBinaryPacketHandler wb); - - @JSBody(params = { "w", "ch", "dat" }, script = "w.postMessage({ ch: ch, dat : dat });") - private static native void sendWorkerPacket(Worker w, String channel, ArrayBuffer arr); - - @JSBody(params = { "w", "workerArgs" }, script = "w.postMessage({ msg : workerArgs });") - private static native void sendWorkerStartPacket(Worker w, String workerArgs); - - private static class WorkerBinaryPacketHandlerImpl implements WorkerBinaryPacketHandler { - - public void onMessage(String channel, ArrayBuffer buf) { - if (channel == null) { - logger.error("Recieved IPC packet with null channel"); - return; - } - - if (buf == null) { - logger.error("Recieved IPC packet with null buffer"); - return; - } - - synchronized (messageQueue) { - messageQueue.add(new IPCPacketData(channel, TeaVMUtils.wrapByteArrayBuffer(buf))); - } - } - - } - - @JSBody(params = { "blobObj" }, script = "return URL.createObjectURL(blobObj);") - private static native String createWorkerScriptURL(JSObject blobObj); - - @JSBody(params = { "cscText", - "tail" }, script = "return new Blob([cscText, tail], { type: \"text/javascript;charset=utf8\" });") - private static native JSObject createBlobObj(ArrayBuffer buf, String tail); - - private static final String workerBootstrapCode = "\n\nmain([\"_worker_process_\"]);"; - - private static JSObject loadIntegratedServerSource() { - String str = loadIntegratedServerSourceOverrideURL(); - if (str != null) { - ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str); - if (buf != null) { - integratedServerSourceOriginalURL = str; - logger.info("Using integrated server at: {}", str); - return createBlobObj(buf, workerBootstrapCode); - } else { - logger.error("Failed to load integrated server: {}", str); - } - } - JSObject el = loadIntegratedServerSourceOverride(); - if (el != null) { - String url = loadIntegratedServerSourceURL(el); - if (url == null) { - el = loadIntegratedServerSourceInline(el, workerBootstrapCode); - if (el != null) { - integratedServerSourceOriginalURL = "inline script tag"; - logger.info("Loading integrated server from inline script tag"); - return el; - } - } else { - ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url); - if (buf != null) { - integratedServerSourceOriginalURL = url; - logger.info("Using integrated server from script tag src: {}", url); - return createBlobObj(buf, workerBootstrapCode); - } else { - logger.error("Failed to load integrated server from script tag src: {}", url); - } - } - } - str = TeaVMUtils.tryResolveClassesSource(); - if (str != null) { - ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str); - if (buf != null) { - integratedServerSourceOriginalURL = str; - logger.info("Using integrated server from script src: {}", str); - return createBlobObj(buf, workerBootstrapCode); - } else { - logger.error("Failed to load integrated server from script src: {}", str); - } - } - logger.info("Could not resolve the location of client's classes.js!"); - logger.info("Make sure client's classes.js is linked/embedded in a dedicated